2012-09-24 7 views
6

Dường như chuyển tiếp thẳng tiến lên SaveChanges trong EF để thêm trình kiểm tra logger. Xem phương thức ApplyAuditLogging để thiết lập các thuộc tính kiểm toán (được tạo, tạo, cập nhật, cập nhật) bên dưới.Entity Framework 5 Sử dụng SaveChanges để thêm nhật ký kiểm tra

public override int SaveChanges() 
    { 
     var autoDetectChanges = Configuration.AutoDetectChangesEnabled; 

     try 
     { 
      Configuration.AutoDetectChangesEnabled = false; 
      ChangeTracker.DetectChanges(); 
      var errors = GetValidationErrors().ToList(); 
      if(errors.Any()) 
      { 
       throw new DbEntityValidationException("Validation errors were found during save: " + errors); 
      } 

      foreach (var entry in ChangeTracker.Entries().Where(e => e.State == EntityState.Added || e.State == EntityState.Modified)) 
      { 
       ApplyAuditLogging(entry); 
      } 

      ChangeTracker.DetectChanges(); 

      Configuration.ValidateOnSaveEnabled = false; 

      return base.SaveChanges(); 
     } 
     finally 
     { 
      Configuration.AutoDetectChangesEnabled = autoDetectChanges; 
     } 
    } 

    private static void ApplyAuditLogging(DbEntityEntry entityEntry) 
    { 

     var logger = entityEntry.Entity as IAuditLogger; 
     if (logger == null) return; 

     var currentValue = entityEntry.Cast<IAuditLogger>().Property(p => p.Audit).CurrentValue; 
     if (currentValue == null) currentValue = new Audit(); 
     currentValue.Updated = DateTime.Now; 
     currentValue.UpdatedBy = "???????????????????????"; 
     if(entityEntry.State == EntityState.Added) 
     { 
      currentValue.Created = DateTime.Now; 
      currentValue.CreatedBy = "????????????????????????"; 
     } 
    } 

Vấn đề là cách để người dùng đăng nhập/đăng nhập để đặt thuộc tính UpdatingBy và CreatedBy của đối tượng? Do đó tôi không thể sử dụng điều này!

Ngoài ra, trong trường hợp khác, tôi muốn tự động thêm bản ghi CallHistory mới vào Liên hệ của mình; bất cứ khi nào liên lạc được sửa đổi, một bản ghi mới cần phải được thêm vào bảng con CallHistory. Vì vậy, tôi đã làm nó trong InsertOrUpdate của Kho lưu trữ nhưng nó cảm thấy bẩn, sẽ là tốt đẹp nếu tôi có thể làm điều đó ở một cấp độ cao hơn như bây giờ tôi phải thiết lập người dùng hiện tại từ cơ sở dữ liệu. Một lần nữa vấn đề ở đây là tôi cần lấy người dùng từ cơ sở dữ liệu để tạo một bản ghi CallHistory (SalesRep = Người dùng).

Các mã trong Repository của tôi làm 2 điều bây giờ, 1, nó tạo ra một mục kiểm toán trên các đối tượng khi nó được tạo ra hoặc cập nhật và, 2, nó cũng tạo ra một mục CallHistory bất cứ khi nào Liên được cập nhật:

ContactRepository.SetCurrentUser(User).InsertOrUpdate(contact) 

để có người dùng trong bối cảnh Repository cho:

var prop = typeof(T).GetProperty("Id", BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase); 

    if (prop.GetValue(entity, null).ToString() == "0") 
    { 
     // New entity 
     _context.Set<T>().Add(entity); 
     var auditLogger = entity as IAuditLogger; 
     if (auditLogger != null) 
      auditLogger.Audit = new Audit(true, _principal.Identity.Name); 
    } 
    else 
    { 
     // Existing entity 
     _context.Entry(entity).State = EntityState.Modified; 
     var auditLogger = entity as IAuditLogger; 
     if (auditLogger != null && auditLogger.Audit != null) 
     { 
      (entity as IAuditLogger).Audit.Updated = DateTime.Now; 
      (entity as IAuditLogger).Audit.UpdatedBy = _principal.Identity.Name; 
     } 

     var contact = entity as Contact; 
     if (_currentUser != null) 
      contact.CallHistories.Add(new CallHistory 
       { 
        CallTime = DateTime.Now, 
        Contact = contact, 
        Created = DateTime.Now, 
        CreatedBy = _currentUser.Logon, 
        SalesRep = _currentUser 
       }); 
    } 
} 

có một cách nào đó tiêm cho người sử dụng cửa sổ vào override SaveChanges trong DbContext và có cũng là một cách để lấy một tài khoản từ cơ sở dữ liệu dựa trên windows logon id để tôi có thể thiết lập Sal esRep trên CallHistory của tôi (xem mã trên)?

Đây là hành động của tôi trên bộ điều khiển trên ứng dụng MVC:

[HttpPost] 
public ActionResult Create([Bind(Prefix = "Contact")]Contact contact, FormCollection collection) 
{ 
    SetupVOs(collection, contact, true); 
    SetupBuyingProcesses(collection, contact, true); 

    var result = ContactRepository.Validate(contact); 

    Validate(result); 

    if (ModelState.IsValid) 
    { 
     ContactRepository.SetCurrentUser(User).InsertOrUpdate(contact); 
     ContactRepository.Save(); 
     return RedirectToAction("Edit", "Contact", new {id = contact.Id}); 
    } 

    var viewData = LoadContactControllerCreateViewModel(contact); 

    SetupPrefixDropdown(viewData, contact); 

    return View(viewData); 
} 

Trả lời

6

Vâng, cách đơn giản và lười biếng để làm điều đó là chỉ cần truy cập vào HttpContext.Current.User.Identity.Name từ bên trong mã kiểm toán của bạn . Tuy nhiên, điều này sẽ tạo ra một sự phụ thuộc vào System.Web. *, Có lẽ không phải là những gì bạn muốn nếu bạn có một ứng dụng độc đáo tầng (và nó sẽ không hoạt động nếu bạn đang sử dụng các tầng riêng biệt thực tế).

Một tùy chọn sẽ là, thay vì ghi đè SaveChanges, chỉ cần tạo một tình trạng quá tải lấy tên người dùng của bạn. Sau đó, bạn thực hiện công việc của mình và gọi cho SaveChanges thực sau đó. Điểm bất lợi là ai đó có thể gọi SaveChanges() (một thực tế) do nhầm lẫn (hoặc có mục đích) và bỏ qua việc kiểm tra.

Cách tốt hơn là chỉ cần thêm thuộc tính _currentUser vào DbContext và uase một hàm tạo để truyền nó vào. Sau đó, khi bạn tạo ngữ cảnh, bạn chỉ cần chuyển người dùng vào lúc đó. Thật không may, bạn không thể thực sự tìm kiếm người dùng trong cơ sở dữ liệu từ hàm tạo.

Nhưng bạn chỉ cần lưu ContactID và thêm số đó thay vì toàn bộ số liên lạc. Liên hệ của bạn đã tồn tại.

+3

SamAccountName có thể thay đổi do đó không phải là số nhận dạng tốt. Tôi sử dụng hướng dẫn thư mục hoạt động mà tất cả các đối tượng nhận được thay vào đó – meffect

0

Tôi nghĩ rằng bạn có thể gặp phải một số vấn đề về mối quan tâm bị ràng buộc. Mẫu kho lưu trữ được sử dụng để tách logic nghiệp vụ, lập bản đồ cơ sở dữ liệu và các hoạt động crud cơ sở dữ liệu của bạn. Ứng dụng nên được quan tâm với những gì người dùng đăng nhập, kho lưu trữ chỉ nên quan tâm đến việc lưu dữ liệu. Tôi sẽ tư vấn chống lại tham chiếu HttpContext trong kho lưu trữ của bạn bởi vì nếu bạn làm thì kho lưu trữ của bạn chỉ có thể được sử dụng bởi một ứng dụng web. Nếu bạn đang cố gắng để trừu tượng dân số của loại siêu dữ liệu, hãy làm điều đó trong ứng dụng của bạn ... ví dụ trong một bộ điều khiển cơ sở hoặc một cái gì đó.

1

tôi biết rằng đây là câu trả lời trễ nhưng tôi chỉ bị lúng túng về câu hỏi này. Tôi đã có một trường hợp sử dụng rất giống nhau. Chúng tôi đã làm điều đó như sau:

var auditUsername = Current.User.Identity.Name; 
var auditDate = DateTime.Now; 

Và các lớp hiện tại:

public class Current 
    { 
     public static IPrincipal User 
     { 
      get 
      { 
       return System.Threading.Thread.CurrentPrincipal; 
      } 
      set 
      { 
       System.Threading.Thread.CurrentPrincipal = value; 
      } 

     } 
    } 

này trả về cho người sử dụng cửa sổ của proccess, hoặc người dùng được đăng nhập trong applicatoin ASP.NET. Để đọc thêm: http://www.hanselman.com/blog/SystemThreadingThreadCurrentPrincipalVsSystemWebHttpContextCurrentUserOrWhyFormsAuthenticationCanBeSubtle.aspx