是否有办法在保存所有更改之前获取对Entity Framework中对象所做的所有更改。原因是我想在客户端数据库中创建一个日志表:
所以...
有没有办法在保存更改之前获取当前数据库值(旧)和新值(当前)?
如果没有,我怎样才能以通用的方式实现这一点,所以我的所有View Models都可以继承?(我使用的是MVVM + M结构)
答案 0 :(得分:55)
您可以使用ObjectContext的ObjectStateManager,GetObjectStateEntry来获取对象ObjectStateEntry,该对象在OriginalValues和CurrentValues属性中保存其原始值和当前值。您可以使用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#。以下代码演示了其中一个。
要求
安装
实施例
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;