我正在开发一个mvc3网络应用。当用户更新某些内容时,我想将旧数据与用户输入的新数据进行比较,并将每个不同的字段添加到日志中以创建活动日志。
现在这就是我的保存操作:
[HttpPost]
public RedirectToRouteResult SaveSingleEdit(CompLang newcomplang)
{
var oldCompLang = _db.CompLangs.First(x => x.Id == newcomplang.Id);
_db.CompLangs.Attach(oldCompLang);
newcomplang.LastUpdate = DateTime.Today;
_db.CompLangs.ApplyCurrentValues(newcomplang);
_db.SaveChanges();
var comp = _db.CompLangs.First(x => x.Id == newcomplang.Id);
return RedirectToAction("ViewSingleEdit", comp);
}
我发现我可以使用它来遍历我的oldCompLang属性:
var oldpropertyInfos = oldCompLang.GetType().GetProperties();
但这并没有真正帮助,因为它只显示属性(Id,Name,Status ...)而不是这些属性的值(1,Hello,Ready ...)。
我可以走得很厉害:
if (oldCompLang.Status != newcomplang.Status)
{
// Add to my activity log table something for this scenario
}
但我真的不想为对象的所有属性做这件事。
我不确定迭代两个对象以查找不匹配的最佳方法是什么(例如,用户更改了名称或状态...)并根据我可以存储在另一个表中的差异构建列表
答案 0 :(得分:24)
这不是那么糟糕,您可以使用反射“手动”比较属性并编写扩展方法以供重用 - 您可以将此作为起点:
public static class MyExtensions
{
public static IEnumerable<string> EnumeratePropertyDifferences<T>(this T obj1, T obj2)
{
PropertyInfo[] properties = typeof(T).GetProperties();
List<string> changes = new List<string>();
foreach (PropertyInfo pi in properties)
{
object value1 = typeof(T).GetProperty(pi.Name).GetValue(obj1, null);
object value2 = typeof(T).GetProperty(pi.Name).GetValue(obj2, null);
if (value1 != value2 && (value1 == null || !value1.Equals(value2)))
{
changes.Add(string.Format("Property {0} changed from {1} to {2}", pi.Name, value1, value2));
}
}
return changes;
}
}
答案 1 :(得分:9)
如果您正在使用EntityFramework,您可以直接从ObjectContext
获取更改获取状态更改条目:
var modifiedEntries= this.ObjectStateManager.GetObjectStateEntries(EntityState.Modified);
获取已保存的属性名称以及原始值和更改值:
for (int i = 0; i < stateChangeEntry.CurrentValues.FieldCount - 1; i++)
{
var fieldName = stateChangeEntry.OriginalValues.GetName(i);
if (fieldName != changedPropertyName)
continue;
var originalValue = stateChangeEntry.OriginalValues.GetValue(i).ToString();
var changedValue = stateChangeEntry.CurrentValues.GetValue(i).ToString();
}
这比@ BrokenGlass的答案更好,因为这将深入对象图中任何已更改的状态,并将为您提供相关集合的已更改属性。它也更好,因为它反映了ObjectContext最终将保存到数据库的所有内容。使用已接受的解决方案,您可能会获得在通过对象上下文断开连接的情况下实际上不会保留的属性更改。
使用EF 4.0,您还可以覆盖SaveChanges()方法,并将与最终实体保存在同一事务中的任何审计或活动包装起来,这意味着如果没有更改实体,审计跟踪将不存在,反之亦然。这保证了准确的日志。如果你不能保证审计是准确的,那么它几乎是无用的。
答案 2 :(得分:1)
扩展jfar的答案,提供一些我必须做出的澄清和更改才能让它发挥作用:
// Had to add this:
db.ChangeTracker.DetectChanges();
// Had to add using System.Data.Entity.Infrastructure; for this:
var modifiedEntries = ((IObjectContextAdapter)db).ObjectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Modified);
foreach (var stateChangeEntry in modifiedEntries)
{
for (int i = 0; i < stateChangeEntry.CurrentValues.FieldCount; i++)
{
var fieldName = stateChangeEntry.OriginalValues.GetName(i);
var changedPropertyName = stateChangeEntry.CurrentValues.GetName(i);
if (fieldName != changedPropertyName)
continue;
var originalValue = stateChangeEntry.OriginalValues.GetValue(i).ToString();
var changedValue = stateChangeEntry.CurrentValues.GetValue(i).ToString();
if (originalValue != changedValue)
{
// do stuff
var foo = originalValue;
var bar = changedValue;
}
}
}
答案 3 :(得分:0)
使用此custom code来比较2个对象。如果他们失败则记录下来。设计观点在Utility类中创建它。
将其用作object1.IsEqualTo(object2)
答案 4 :(得分:0)
在您的实体上实施IEquatable<T>
。另一种方法是实现自定义IComparer<T>
,您可以在其中公开在属性不同时将触发的事件。