2012-09-24 13 views
6

監査ログを追加するには、EFのSaveChangesをそのまま上書きしているようです。以下の監査プロパティ(created、createdby、updated、updatedby)を設定するには、ApplyAuditLoggingメソッドを参照してください。Entity Framework 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 = "????????????????????????"; 
     } 
    } 

問題は、どのようにオブジェクトのUpdatedByとCREATEDBYプロパティを設定するには、Windowsのユーザーのログオン/ユーザ名を取得するということですか?私はこれを使用することはできませんでした!

また、別のケースでは、私の連絡先に新しいCallHistoryレコードを自動的に追加したいと思っていました。連絡先が変更されるたびに、新しいレコードを子テーブルのCallHistoryに追加する必要があります。リポジトリのInsertOrUpdateで行ったのですが、データベースから現在のユーザーを設定する必要があるため、リポジトリのInsertOrUpdateでリポジトリのInsertOrUpdateで行いましたが、汚いと感じました。ここでも問題は、CallHistoryレコード(SalesRep = User)を作成するためにデータベースからユーザーをフェッチする必要があることです。

ContactRepository.SetCurrentUser(User).InsertOrUpdate(contact) 

私のリポジトリ内のコードは1、今2つのことを行い、それが2、作成または更新されたオブジェクトの監査エントリを作成し、それはまた、連絡先が更新されるたびにCallHistoryエントリを作成しました以下のためのリポジトリのコンテキストでユーザーを持つために

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

は何とかDbContextでのSaveChangesオーバーライドにWindowsユーザーを注入する方法があるともからユーザーを取得する方法がありますデータベースは、WindowsログオンIDに基づいて私はサルを設定することができます私のCallHistoryでesRep(上記のコードを参照)?ここで

は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に追加し、コンストラクタを渡すことです。次に、コンテキストを作成するときに、その時点でユーザーを渡すだけです。残念ながら、実際にはコンストラクタからデータベース内のユーザを参照することはできません。

ただし、連絡先IDを保存して連絡先全体ではなく連絡先IDを追加するだけで済みます。連絡先はすでに存在しているはずです。

+3

したがって、SamAccountNameは変更可能であり、したがって良好な識別子ではありません。代わりにすべてのオブジェクトが取得するアクティブディレクトリguidを使用します – meffect

0

私はあなたが懸念のいくつかの分離を越えているかもしれないと思います。リポジトリ・パターンは、ビジネス・ロジック、データベース・マッピング、およびデータベースのクラッシュ・オペレーションを分離するために使用されます。アプリケーションは、どのユーザーがログインしているかに関係している必要があります。リポジトリーは、データの保存にのみ関係する必要があります。リポジトリ内のHttpContextを参照しないようにすることをお勧めします。リポジトリ内のHttpContextは、リポジトリをWebアプリケーションのみで使用できるためです。この種のメタデータの母集団を抽象化しようとしている場合は、ベース・コントローラーなどのアプリケーションで行います。

1

私はこれが遅い答えであることを知っていますが、私はちょうどこの質問を突き詰めました。私は非常に似たユースケースを持っていました。次のように我々はそれをやった:

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

     } 
    } 

これはproccessのWindowsユーザー、またはASP.NETのapplicatoinにログインしているユーザーを返します。もっと読む:http://www.hanselman.com/blog/SystemThreadingThreadCurrentPrincipalVsSystemWebHttpContextCurrentUserOrWhyFormsAuthenticationCanBeSubtle.aspx

関連する問題