如何解决实体框架4.1 DbContext覆盖SaveChanges以审核属性更改
|| 我正在尝试对一组类中的属性实施约束的“审核日志”。我已经成功地找到了如何设置CreatedOn | ModifiedOn类型的属性,但是没有找到如何“查找”已修改的属性的方法。 例:public class TestContext : DbContext
{
public override int SaveChanges()
{
var utcNowAuditDate = DateTime.UtcNow;
var changeSet = ChangeTracker.Entries<IAuditable>();
if (changeSet != null)
foreach (DbEntityEntry<IAuditable> dbEntityEntry in changeSet)
{
switch (dbEntityEntry.State)
{
case EntityState.Added:
dbEntityEntry.Entity.CreatedOn = utcNowAuditDate;
dbEntityEntry.Entity.ModifiedOn = utcNowAuditDate;
break;
case EntityState.Modified:
dbEntityEntry.Entity.ModifiedOn = utcNowAuditDate;
//some way to access the name and value of property that changed here
var changedThing = SomeMethodHere(dbEntityEntry);
Log.WriteAudit(\"Entry: {0} Origianl :{1} New: {2}\",changedThing.Name,changedThing.OrigianlValue,changedThing.NewValue)
break;
}
}
return base.SaveChanges();
}
}
因此,是否有一种方法可以访问在EF 4.1 DbContext中使用此详细信息级别更改的属性?
解决方法
非常非常粗糙的想法:
foreach (var property in dbEntityEntry.Entity.GetType().GetProperties())
{
DbPropertyEntry propertyEntry = dbEntityEntry.Property(property.Name);
if (propertyEntry.IsModified)
{
Log.WriteAudit(\"Entry: {0} Original :{1} New: {2}\",property.Name,propertyEntry.OriginalValue,propertyEntry.CurrentValue);
}
}
我不知道这是否真的可以奏效,但这是我第一步要尝试的方法。当然,可能有一个以上的属性发生了变化,因此发生了循环,并可能多次调用了“ 2”。
但是,SaveChanges中的反射内容可能会成为性能噩梦。
编辑
可能最好访问底层的“ 3”。然后可能是这样的:
public class TestContext : DbContext
{
public override int SaveChanges()
{
ChangeTracker.DetectChanges(); // Important!
ObjectContext ctx = ((IObjectContextAdapter)this).ObjectContext;
List<ObjectStateEntry> objectStateEntryList =
ctx.ObjectStateManager.GetObjectStateEntries(EntityState.Added
| EntityState.Modified
| EntityState.Deleted)
.ToList();
foreach (ObjectStateEntry entry in objectStateEntryList)
{
if (!entry.IsRelationship)
{
switch (entry.State)
{
case EntityState.Added:
// write log...
break;
case EntityState.Deleted:
// write log...
break;
case EntityState.Modified:
{
foreach (string propertyName in
entry.GetModifiedProperties())
{
DbDataRecord original = entry.OriginalValues;
string oldValue = original.GetValue(
original.GetOrdinal(propertyName))
.ToString();
CurrentValueRecord current = entry.CurrentValues;
string newValue = current.GetValue(
current.GetOrdinal(propertyName))
.ToString();
if (oldValue != newValue) // probably not necessary
{
Log.WriteAudit(
\"Entry: {0} Original :{1} New: {2}\",entry.Entity.GetType().Name,oldValue,newValue);
}
}
break;
}
}
}
}
return base.SaveChanges();
}
}
我在EF 4.0中已使用过此功能。我在DbContext
API中找不到与GetModifiedProperties
相对应的方法(这是避免反射代码的关键)。
编辑2
重要说明:与POCO实体一起使用时,上述代码必须在开始时调用DbContext.ChangeTracker.DetectChanges()
。原因是在此方法的末尾调用called8ѭ太晚了。 base.SaveChanges
在内部调用DetectChanges
,但是由于我们想在之前分析和记录更改,因此必须手动调用DetectChanges
,以便EF可以找到所有已修改的属性并在更改跟踪器中正确设置状态。
在某些情况下,代码无需调用ѭ10就可以工作,例如,如果在最后一次修改属性后使用了诸如Add
或Remove
之类的DbContext / DbSet方法,因为这些方法也在内部调用DetectChanges
。但是,例如,如果仅从DB加载实体,则更改了一些属性,然后调用了派生的SaveChanges
,则在base.SaveChanges
之前不会进行自动更改检测,最终会导致缺少修改后的属性的日志条目。
我已经相应地更新了上面的代码。
, 您可以使用Slauma建议的方法,但是可以替代SavingChanges
事件,而不是重写SaveChanges()
方法,从而更轻松地实现。
, 似乎Slauma的答案并未审核对复杂类型属性的内部属性的更改。
如果这对您来说是个问题,我在这里的回答可能会有所帮助。如果该属性是复杂属性,并且已更改,则将整个复杂属性序列化为审核日志记录。它不是最有效的解决方案,但还算不错,可以完成工作。
, 我真的很喜欢Slauma的解决方案。我通常更喜欢跟踪已修改的表并记录主键。这是一种非常简单的方法,可以调用ѭ20
public static string getEntityKeys(ObjectStateEntry entry)
{
return string.Join(\",\",entry.EntityKey.EntityKeyValues
.Select(x => x.Key + \"=\" + x.Value));
}
, 请参阅使用实体框架4.1 DbContext更改跟踪进行审核日志记录。
与DbEntityEntry。详细审核添加,删除,修改
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。