Entity Framework 4.1 DbContext 覆盖 SaveChanges 以审计属性更改 [英] Entity Framework 4.1 DbContext Override SaveChanges to Audit Property Change
问题描述
我正在尝试对一组类中的属性实施一个受约束的审计日志"属性更改.我已经成功地找到了如何设置 CreatedOn|ModifiedOn 类型的属性,但我没有找到如何找到"已修改的属性.
I am trying to implement a constrained 'audit log' of property changes to a properties in a set of classes. I have successfully found out how to set CreatedOn|ModifiedOn type properties, but am failing to find out how to 'find' the property that has been modified.
示例:
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 中随着这种详细程度而改变的属性?
So, is there a way to access the property that changed with this level of detail in 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);
}
}
我不知道这是否真的有效,但这是我会尝试的第一步.当然,可能有不止一个属性发生了变化,因此循环和 WriteAudit
可能多次调用.
I have no clue if this would really work in detail, but this is something I would try as a first step. Of course there could be more then one property which has changed, therefore the loop and perhaps multiple calls of WriteAudit
.
SaveChanges 中的反射内容可能会成为性能噩梦.
The reflection stuff inside of SaveChanges could become a performance nightmare though.
编辑
也许访问底层 ObjectContext
更好.那么这样的事情是可能的:
Perhaps it is better to access the underlying ObjectContext
. Then something like this is possible:
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
对应的方法(这是避免反射代码的关键).
I've used this myself in EF 4.0. I cannot find a corresponding method to GetModifiedProperties
(which is the key to avoid the reflection code) in the DbContext
API.
编辑 2
重要:使用 POCO 实体时,上面的代码需要在开头调用 DbContext.ChangeTracker.DetectChanges()
.原因是 base.SaveChanges
在这里调用太晚了(在方法的末尾).base.SaveChanges
在内部调用了 DetectChanges
,但是因为我们之前要分析和记录更改,所以必须手动调用 DetectChanges
以便 EF 可以找到所有修改过的属性并在更改跟踪器中正确设置状态.
Important: When working with POCO entities the code above needs to call DbContext.ChangeTracker.DetectChanges()
at the beginning. The reason is that base.SaveChanges
is called too late here (at the end of the method). base.SaveChanges
calls DetectChanges
internally, but because we want to analyze and log the changes before, we must call DetectChanges
manually so that EF can find all modified properties and set the states in the change tracker correctly.
在某些情况下,代码可以在不调用 DetectChanges
的情况下工作,例如,如果使用了 Add
或 Remove
等 DbContext/DbSet 方法在进行最后一次属性修改之后,因为这些方法也在内部调用 DetectChanges
.但是如果例如一个实体刚从 DB 加载,一些属性被改变,然后这个派生的 SaveChanges
被调用,自动变化检测不会在 base.SaveChanges
之前发生,最终导致丢失修改属性的日志条目.
There are possible situations where the code can work without calling DetectChanges
, for example if DbContext/DbSet methods like Add
or Remove
are used after the last property modifications are made since these methods also call DetectChanges
internally. But if for instance an entity is just loaded from DB, a few properties are changed and then this derived SaveChanges
is called, automatic change detection would not happen before base.SaveChanges
, finally resulting in missing log entries for modified properties.
我已经相应地更新了上面的代码.
I've updated the code above accordingly.
这篇关于Entity Framework 4.1 DbContext 覆盖 SaveChanges 以审计属性更改的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!