简体   繁体   English

获取对实体框架中的对象所做的所有更改

[英]Getting all changes made to an object in the Entity Framework

Is there a way to get all the changes made to a object in the Entity Framework before it saves all changes.有没有办法在保存所有更改之前获取对实体框架中的对象所做的所有更改。 The reason for this is that i want to create a log table in our clients database:这样做的原因是我想在我们的客户数据库中创建一个日志表:

so...所以...

Is there a way to get the current database values(old) and the new values(current) before changes are saved?有没有办法在保存更改之前获取当前数据库值(旧)和新值(当前)?

If not, how can i achieve this in a generic way, so all my View Models can inherit from this?(I am using the MVVM + M Structure)如果没有,我怎样才能以通用方式实现这一点,以便我所有的视图模型都可以从中继承?(我正在使用 MVVM + M 结构)

You can use ObjectContext's ObjectStateManager,GetObjectStateEntry to get an object's ObjectStateEntry , which holds its original and current values in the OriginalValues and CurrentValues properties.您可以使用 ObjectContext 的ObjectStateManager,GetObjectStateEntry来获取对象的ObjectStateEntry ,该对象在OriginalValuesCurrentValues属性中保存其原始值和当前值。 You can get the names of the properties that changed using the GetModifiedProperties method.您可以使用GetModifiedProperties方法获取更改的属性的名称。

You can write something like:你可以这样写:

var myObjectState=myContext.ObjectStateManager.GetObjectStateEntry(myObject);
var modifiedProperties=myObjectState.GetModifiedProperties();
foreach(var propName in modifiedProperties)
{
    Console.WriteLine("Property {0} changed from {1} to {2}", 
         propName,
         myObjectState.OriginalValues[propName],
         myObjectState.CurrentValues[propName]);
}

For EF5 upwards you can log your changes in the SaveChanges() method like this:对于 EF5 以上,您可以在 SaveChanges() 方法中记录您的更改,如下所示:

    public override int SaveChanges()
    {

        var changes = from e in this.ChangeTracker.Entries()
                      where e.State != System.Data.EntityState.Unchanged
                      select e;

        foreach (var change in changes)
        {
            if (change.State == System.Data.EntityState.Added)
            {
                // Log Added
            }
            else if (change.State == System.Data.EntityState.Modified)
            {
                // Log Modified
                var item = change.Cast<IEntity>().Entity;
                var originalValues = this.Entry(item).OriginalValues;
                var currentValues = this.Entry(item).CurrentValues;

                foreach (string propertyName in originalValues.PropertyNames)
                {
                    var original = originalValues[propertyName];
                    var current = currentValues[propertyName];

                    if (!Equals(original, current))
                    {
                        // log propertyName: original --> current
                    }
                }

            }
            else if (change.State ==  System.Data.EntityState.Deleted)
            {
                // log deleted
            }
        }
        // don't forget to save
        base.SaveChanges();
    }

I use this extension function that provides details on the entity being changed, the old and new values, the datatype, and the entity key.我使用此扩展函数提供有关正在更改的实体、旧值和新值、数据类型和实体键的详细信息。

This is tested with EF6.1 using ObjectContext and uses log4net for output.这是通过 EF6.1 使用 ObjectContext 进行测试的,并使用 log4net 进行输出。

    /// <summary>
    /// dump changes in the context to the debug log
    /// <para>Debug logging must be turned on using log4net</para>
    /// </summary>
    /// <param name="context">The context to dump the changes for</param>
    public static void DumpChanges(this ObjectContext context)
    {
        context.DetectChanges();
        // Output any added entries
        foreach (var added in context.ObjectStateManager.GetObjectStateEntries(EntityState.Added))
        {
            Log.DebugFormat("{0}:{1} {2} {3}", added.State, added.Entity.GetType().FullName, added.Entity.ToString(), string.Join(",", added.CurrentValues.GetValue(1), added.CurrentValues.GetValue(2)));
        }
        foreach (var modified in context.ObjectStateManager.GetObjectStateEntries(EntityState.Modified))
        {
            // Put original field values into dictionary
            var originalValues = new Dictionary<string,int>();
            for (var i = 0; i < modified.OriginalValues.FieldCount; ++i)
            {
                originalValues.Add(modified.OriginalValues.GetName(i), i);
            }
            // Output each of the changed properties.
            foreach (var entry in modified.GetModifiedProperties())
            {
                var originalIdx = originalValues[entry];
                Log.DebugFormat("{6} = {0}.{4} [{7}][{2}] [{1}] --> [{3}]  Rel:{5}", 
                    modified.Entity.GetType(), 
                    modified.OriginalValues.GetValue(originalIdx), 
                    modified.OriginalValues.GetFieldType(originalIdx), 
                    modified.CurrentValues.GetValue(originalIdx), 
                    modified.OriginalValues.GetName(originalIdx), 
                    modified.IsRelationship, 
                    modified.State, 
                    string.Join(",", modified.EntityKey.EntityKeyValues.Select(v => string.Join(" = ", v.Key, v.Value))));
            }
        }
        // Output any deleted entries
        foreach (var deleted in context.ObjectStateManager.GetObjectStateEntries(EntityState.Deleted))
        {
            Log.DebugFormat("{1} {0} {2}", deleted.Entity.GetType().FullName, deleted.State, string.Join(",", deleted.CurrentValues.GetValue(1), deleted.CurrentValues.GetValue(2)));
        }
    }

Use IsModified field of each property accessible by Context.Entry(Entity).Properties .使用Context.Entry(Entity).Properties可访问的每个属性的IsModified字段。

In this example the modified entries are listed as a Tuple of the original an current value, indexed by name.在此示例中,修改后的条目被列为原始当前值的Tuple ,按名称索引。 Use any conversion that is required to build the audit log.使用构建审计日志所需的任何转换。

    using Microsoft.EntityFrameworkCore;
    using System.Collections.Generic;
    //...
    DbContext Context;  //gets somewhere in the scope
    object Entity; // Some entity that has been modified, but not saved and is being tracked by Context
    //...
    Dictionary<string, System.Tuple<object, object>> modified = 
      Context.Entry(Entity)
        .Properties.Where(p => p.IsModified)
        .ToDictionary(p => p.Metadata.Name, p => new System.Tuple<object,object>(p.OriginalValue, p.CurrentValue));
    //...

Uses Entity Framework Core 3.1.使用实体框架核心 3.1。 Try it for EF 6.4 , but may not work.EF 6.4尝试一下,但可能不起作用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM