EFでSaveChangesをオーバーライドして、監査ロガーを追加するのは簡単なようです。以下の監査プロパティ(created、createdby、updated、updatedby)を設定するには、ApplyAuditLoggingメソッドを参照してください。
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ユーザーのlogon / usernameを取得して、オブジェクトのUpdatedByプロパティとCreatedByプロパティを設定する方法です。したがって、これは使用できませんでした。
また、別のケースでは、新しいCallHistoryレコードを連絡先に自動的に追加したいと思いました。連絡先が変更されるたびに、新しいレコードを子テーブルCallHistoryに追加する必要があります。リポジトリのInsertOrUpdateで実行しましたが、汚れているように感じます。データベースから現在のユーザーを設定する必要があるため、より高いレベルで実行できれば便利です。ここでも問題は、CallHistoryレコードを作成するためにデータベースからユーザーをフェッチする必要があることです(SalesRep = User)。
私のリポジトリのコードは、2つのことを実行します。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に基づいてデータベースからユーザーをフェッチする方法もあります。これにより、CallHistoryでSalesRepを設定できます(上記のコードを参照)。
これが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);
}