2012-09-24 155 views
6

似乎直接覆蓋EF中的SaveChanges以添加審計日誌記錄器。請參閱ApplyAuditLogging方法以設置下面的審計屬性(由創建,創建,更新,更新)。實體框架5使用SaveChanges添加審計日誌

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 = "????????????????????????"; 
     } 
    } 

的問題是,如何讓Windows用戶登錄/用戶名設置對象的UpdatedBy和CreatedBy屬性?因此我不能使用這個!

此外,在另一種情況下,我想自動添加一個新的CallHistory記錄到我的聯繫人;每當聯繫人被修改時,都需要將新記錄添加到子表CallHistory中。所以我在Repository的InsertOrUpdate中做了它,但它感覺很髒,如果我可以在更高層次上完成,現在我必須從數據庫中設置當前用戶,這會很好。這裏的問題是,我需要從數據庫中提取用戶來創建CallHistory記錄(SalesRep = User)。

在我的庫中的代碼做兩件事情,現在,1,它創建時就創建或更新和2的對象進行審覈項,它也創造了CallHistory項每當聯繫人的更新:

ContactRepository.SetCurrentUser(User).InsertOrUpdate(contact) 

爲了讓用戶在存儲庫上下文:

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 
       }); 
    } 
} 

有沒有辦法以某種方式注入的Windows用戶將在的DbContext調用SaveChanges倍率,有也從獲取用戶的方式基於Windows登錄ID的數據庫,所以我可以設置薩爾esRep在我的CallHistory(見上面的代碼)?

這是我在控制器上行動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); 
} 

回答

6

好了,簡單和懶惰的方式來做到這一點是簡單地從你的審計代碼中訪問HttpContext.Current.User.Identity.Name 。但是,這會對System.Web。*產生依賴關係,如果你有一個很好的分層應用程序,這可能不是你想要的(如果你正在使用實際的分層的話,它將不起作用)。

一個選項是,而不是覆蓋SaveChanges,只是創建一個需要您的用戶名的重載。然後你做你的工作,然後打電話給真正的SaveChanges。缺點是有人可能會錯誤地(或故意)調用SaveChanges()(真實的)並繞過審計。

更好的方法是簡單地將_currentUser屬性添加到您的DbContext中,並使用構造函數將其傳入。然後,當您創建上下文時,您只需在此時傳遞用戶。不幸的是,你不能從構造函數中查找數據庫中的用戶。

但是,您可以簡單地保存ContactID並添加它,而不是整個聯繫人。您的聯繫人應該已經存在。

+3

SamAccountName是可更改的,因此不是一個好的標識符。我使用活動目錄guid來取代所有對象 – meffect

0

我想你可能會跨越一些關注邊界的問題。存儲庫模式用於分離您的業務邏輯,數據庫映射和數據庫crud操作。應用程序應該關注用戶登錄的內容,倉庫應該只關心保存數據。我建議不要在存儲庫中引用HttpContext,因爲如果你這樣做,那麼你的存儲庫只能被web應用程序使用。如果您試圖抽象這種元數據的種羣,請在您的應用程序中執行...例如在基本控制器中或其他東西。

1

我知道這是一個遲到的答案,但我只是在這個問題上st st。我有一個非常相似的用例。我們做到了,如下所示:

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

而且目前等級:

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

     } 
    } 

這將返回工藝在Windows用戶,或者是在ASP.NET applicatoin登錄的用戶。閱讀更多:http://www.hanselman.com/blog/SystemThreadingThreadCurrentPrincipalVsSystemWebHttpContextCurrentUserOrWhyFormsAuthenticationCanBeSubtle.aspx