实体框架4.1 DbContext将SaveChanges替换为审计属性更改 [英] Entity Framework 4.1 DbContext Override SaveChanges to Audit Property Change

查看:108
本文介绍了实体框架4.1 DbContext将SaveChanges替换为审计属性更改的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试对一组类中的属性实现属性更改的约束审核日志。我已经成功地找到了如何设置CreatedOn | ModifiedOn类型属性,但是没有找到如何查找修改的属性。



示例: / p>

  public class TestContext:DbContext 
{
public override int SaveChanges()
{
var utcNowAuditDate = DateTime.UtcNow;
var changeSet = ChangeTracker.Entries< IAuditable>();
if(changeSet!= null)
foreach(DbEntityEntry< IAuditable>在SetSet中的dbEntityEntry)
{

switch(dbEntityEntry.State)
{
case EntityState.Added:
dbEntityEntry.Entity.CreatedOn = utcNowAuditDate;
dbEntityEntry.Entity.ModifiedOn = utcNowAuditDate;
break;
case EntityState.Modified:
dbEntityEntry.Entity.ModifiedOn = utcNowAuditDate;
//某些方式访问在这里更改的属性的名称和值
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(dbEntityEntry.Entity.GetType()中的var property)GetProperties())
{
DbPropertyEntry propertyEntry = dbEntityEntry.Property 。名称);
if(propertyEntry.IsModified)
{
Log.WriteAudit(Entry:{0} Original:{1} New:{2},property.Name,
propertyEntry .OriginalValue,propertyEntry.CurrentValue);
}
}

我不知道这是否真的可以正常工作,但这是我将尝试的第一步。当然可以有一个属性发生变化,因此循环,也可能是多个调用 WriteAudit



SaveChanges里面的反射内容可能会成为一个表演噩梦。



修改



也许最好访问底层的 ObjectContext 。然后可以这样做:

  public class TestContext:DbContext 
{
public override int SaveChanges( )
{
ChangeTracker.DetectChanges(); //重要!

ObjectContext ctx =((IObjectContextAdapter)this).ObjectContext;

列表< ObjectStateEntry> objectStateEntryList =
ctx.ObjectStateManager.GetObjectStateEntries(EntityState.Added
| EntityState.Modified
| EntityState.Deleted)
.ToList();

foreach(objectStateEntryList中的ObjectStateEntry条目)
{
if(!entry.IsRelationship)
{
开关(entry.State)
{
case EntityState.Added:
//写日志...
break;
case EntityState.Deleted:
//写日志...
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)//可能不需要
{
Log.WriteAudit(
条目:{0}原始:{1}新增: {2},
entry.Entity.GetType()。Name,
oldValue,newValue);
}
}
break;
}
}
}
}
return base.SaveChanges();
}
}

我在EF 4.0中自己使用过。我不能在 DbContext API中找到相应的方法来 GetModifiedProperties (这是避免反射代码的关键)。



编辑2



重要:工作时与POCO实体上面的代码需要在开头调用 DbContext.ChangeTracker.DetectChanges()。原因是在这里(在方法的结尾)调用了 base.SaveChanges base.SaveChanges 调用 DetectChanges 内部,但是因为我们以前要分析和记录更改,所以我们必须调用code> DetectChanges 手动使EF可以找到所有修改的属性,并正确设置更改跟踪器中的状态。



有可能的情况其中代码可以在不调用 DetectChanges 的情况下工作,例如,如果DbContext / DbSet方法如添加删除在最后一个属性修改后使用,因为这些方法也在内部调用 DetectChanges 。但是,如果一个实体刚刚从DB加载,那么几个属性被改变,然后调用这个派生的 SaveChanges ,自动更改检测不会发生在 base.SaveChanges ,最终导致修改的属性缺少日志条目。



我已经相应地更新了上面的代码。

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.

Example:

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

So, is there a way to access the property that changed with this level of detail in EF 4.1 DbContext?

解决方案

Very, very rough idea:

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

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.

The reflection stuff inside of SaveChanges could become a performance nightmare though.

Edit

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

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.

Edit 2

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.

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.

这篇关于实体框架4.1 DbContext将SaveChanges替换为审计属性更改的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆