获取对Entity Framework中的对象所做的所有更改

时间:2010-07-16 13:25:18

标签: c# entity-framework

是否有办法在保存所有更改之前获取对Entity Framework中对象所做的所有更改。原因是我想在客户端数据库中创建一个日志表:

所以...

有没有办法在保存更改之前获取当前数据库值(旧)和新值(当前)?

如果没有,我怎样才能以通用的方式实现这一点,所以我的所有View Models都可以继承?(我使用的是MVVM + M结构)

5 个答案:

答案 0 :(得分:55)

您可以使用ObjectContext的ObjectStateManager,GetObjectStateEntry来获取对象ObjectStateEntry,该对象在OriginalValuesCurrentValues属性中保存其原始值和当前值。您可以使用GetModifiedProperties方法获取已更改的属性的名称。

你可以这样写:

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

答案 1 :(得分:33)

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

答案 2 :(得分:8)

我使用此扩展函数提供有关正在更改的实体,旧值和新值,数据类型和实体键的详细信息。

使用ObjectContext对EF6.1进行测试,并使用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)));
        }
    }

答案 3 :(得分:1)

使用IsModified可访问的每个属性的Context.Entry(Entity).Properties字段。

在此示例中,修改后的条目作为原始当前值的Tuple列出,并按名称索引。使用构建审核日志所需的任何转换。

    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));
    //...

使用Entity Framework Core 3.1。尝试使用EF 6.4,但可能无法正常工作。

答案 4 :(得分:0)

您可以通过多种方式将其实现为C#。以下代码演示了其中一个。

要求

安装

  • PM&GT; Install-Package DevExpressMvvm -Version 17.1.6
  • PM&GT; Install-Package System.ValueTuple -Version 4.4.0

实施例

using DevExpress.Mvvm;
using DevExpress.Mvvm.Native;
using System;
using System.Linq;
using System.ComponentModel;
using System.Collections.Generic;

namespace TrackingPropertyManagerApp
{
    public class Member : BindableBase
    {
        public Member()
        {
            PropertyManager = new TrackingPropertyManager();
        }

        private string _MemberProperty;
        [DisplayName("Your property description")]
        public string MemberProperty
        {
            get { return _MemberProperty; }
            set { SetProperty(ref _MemberProperty, value, () => MemberProperty); }
        }

        private TrackingPropertyManager PropertyManager { get; }
        protected override PropertyManager CreatePropertyManager() => PropertyManager;

        public IEnumerable<(string property, object original, object changed)> GetChangedProperties()
            => from changes in PropertyManager.ChangeTracker
               where changes.Value.wasChanged
               select (property: changes.Key, original: changes.Value.original, changed: changes.Value.changed);

        private class TrackingPropertyManager : PropertyManager
        {
            internal IDictionary<string, (object original, bool wasChanged, object changed)> ChangeTracker = new Dictionary<string, (object, bool, object)>();
            protected override bool SetPropertyCore<T>(ref T storage, T value, string propertyName)
            {
                if (ChangeTracker.TryGetValue(propertyName, out (object original, bool wasChanged, object changed) changeInfo))
                {
                    if (!EqualityComparer<T>.Default.Equals(storage, value))
                    {
                        ChangeTracker[propertyName] = (changeInfo.original, true, value);
                    }
                }
                else
                {
                    ChangeTracker[propertyName] = (value, false, null);
                }
                return base.SetPropertyCore(ref storage, value, propertyName);
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var member = new Member();
            member.MemberProperty = "first value";
            member.MemberProperty = "second value";
            member.MemberProperty = "last value";

            var changes = member.GetChangedProperties().ToArray();
            var type = typeof(Member);
            foreach (var (property, original, changed) in changes)
            {
                var propertyInfo = type.GetProperty(property);
                if (propertyInfo.GetCustomAttributes(typeof(DisplayNameAttribute), false).FirstOrDefault() is DisplayNameAttribute displayAttribute)
                {
                    Console.WriteLine($"{ displayAttribute.DisplayName }: old='{ original }' new='{ changed }'");
                }
            }
            Console.ReadLine();
        }
    }
}

输出

您的房产描述:old =&#39; first value&#39; new =&#39;最后一个值&#39;