似乎直接覆盖EF中的SaveChanges以添加审计记录器。请参阅下面的ApplyAuditLogging方法来设置审核属性(created、createdby、updated、updatedby)。
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中。所以我在存储库的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从数据库中获取用户,这样我就可以在我的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);
}
好吧,简单而懒惰的方法就是简单地访问HttpContext。现在的使用者身份审计代码中的名称。但是,这将创建对系统的依赖关系。Web.*,如果你有一个分层良好的应用程序,这可能不是你想要的(如果你使用实际的独立层,它也不会工作)。
一种选择是,不覆盖SaveChanges,只创建一个使用您的用户名的重载。然后你完成你的工作,然后调用真正的SaveChanges。缺点是,有人可能会错误地(或故意)调用SaveChanges()(真正的)并绕过审计。
一个更好的方法是简单地将_currentUser属性添加到DbContext中,并使用构造函数将其传入。然后,当您创建上下文时,您只需在那时传入用户。不幸的是,您无法真正从构造函数中查找数据库中的用户。
但您可以简单地保存ContactID并添加它,而不是整个联系人。您的联系人应该已经存在。
我知道这是一个迟来的答案,但我只是研究了这个问题。我有一个非常相似的用例。我们是这样做的:
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应用程序。要阅读更多信息:http://www.hanselman.com/blog/SystemThreadingThreadCurrentPrincipalVsSystemWebHttpContextCurrentUserOrWhyFormsAuthenticationCanBeSubtle.aspx
我认为您可能正在跨越一些关注点边界。存储库模式用于分离业务逻辑、数据库映射和数据库crud操作。应用程序应该关注登录的用户,存储库应该只关注保存数据。我建议不要在存储库中引用HttpContext,因为如果这样做,那么存储库只能由web应用程序使用。如果您试图抽象这种元数据的总体,请在您的应用程序中执行。。。例如在基本控制器或其它什么中。