Asp.net mvc 查找相同类型的两个实体之间的差异
我正在开发一个mvc3网络应用程序。当用户更新某些内容时,我希望将旧数据与用户输入的新数据进行比较,并将不同的字段添加到日志中,以创建活动日志 现在,我的保存操作是这样的:Asp.net mvc 查找相同类型的两个实体之间的差异,asp.net-mvc,entity-framework,linq-to-sql,Asp.net Mvc,Entity Framework,Linq To Sql,我正在开发一个mvc3网络应用程序。当用户更新某些内容时,我希望将旧数据与用户输入的新数据进行比较,并将不同的字段添加到日志中,以创建活动日志 现在,我的保存操作是这样的: [HttpPost] public RedirectToRouteResult SaveSingleEdit(CompLang newcomplang) { var oldCompLang = _db.CompLangs.First(x => x.Id == newcomplang.Id); _db.
[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);
}
我发现我可以用它来迭代我的OldPlumng属性:
var oldpropertyInfos = oldCompLang.GetType().GetProperties();
但这并没有真正的帮助,因为它只显示属性(Id、名称、状态…),而不是这些属性的值(1、Hello、Ready…)
我可以走艰难的路:
if (oldCompLang.Status != newcomplang.Status)
{
// Add to my activity log table something for this scenario
}
但我真的不想对对象的所有属性都这样做
我不确定迭代这两个对象以查找不匹配(例如,用户更改了名称或状态…)并根据这些差异构建列表的最佳方法是什么,我可以将它们存储在另一个表中。使用此方法比较两个对象。如果他们失败了,记录下来。从设计角度来看,在实用程序类中创建此对象
将其用作
object1.IsEqualTo(object2)
在实体上实现IEquatable
。另一种方法是实现自定义的IComparer
,例如,您可以公开属性不同时将触发的事件。还不错,您可以使用反射“手动”比较属性,并编写扩展方法以供重用-您可以以此为起点:
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;
}
}
公共静态类MyExtensions
{
公共静态IEnumerable EnumerativePropertyDifferences(此T obj1,T obj2)
{
PropertyInfo[]properties=typeof(T).GetProperties();
列表更改=新列表();
foreach(PropertyInfo pi in properties)
{
object value1=typeof(T).GetProperty(pi.Name).GetValue(obj1,null);
objectvalue2=typeof(T).GetProperty(pi.Name).GetValue(obj2,null);
如果(value1!=value2&&(value1==null | | |!value1.Equals(value2)))
{
Add(string.Format(“属性{0}从{1}更改为{2}”)、pi.Name、value1、value2);
}
}
回报变化;
}
}
如果使用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();
}
for(int i=0;i
这比@BrokenGlass的答案要好,因为这将深入对象图中任何更改的状态,并将为您提供关联集合的更改属性。这也更好,因为它反映了ObjectContext最终将保存到数据库的所有内容。使用接受的解决方案,您可能会得到在通过对象上下文断开连接的情况下实际上不会持久化的属性更改
使用EF 4.0,您还可以覆盖SaveChanges()方法,并将任何审核或活动包装到与最终实体保存相同的事务中,这意味着如果实体没有更改,审核跟踪将不存在,反之亦然。这保证了日志的准确性。如果你不能保证审计是准确的,那么它几乎是无用的。扩展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;
}
}
}
//必须添加以下内容:
db.ChangeTracker.DetectChanges();
//必须使用System.Data.Entity.Infrastructure添加;为此:
var modifiedEntries=((IOObjectContextAdapter)db.ObjectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Modified);
foreach(modifiedEntries中的var stateChangeEntry)
{
对于(int i=0;i
这比我想象的要复杂得多。我会认为这是一个非常常见的问题,有一个简单的解决方案^^我甚至不知道如何在我的实体上实现这个IEquatable:/我喜欢这个答案的外观,但我的代码不会将ObjectStateManager识别为这种方法。让我感到困惑的是,你认为呢?我知道它很老,但却是一个优秀的解决方案,只有那些看不到它的人才知道两个细节:1-在2块代码之间,你需要一个foreach(modifiedEntries中的var stateChangeEntry)和2-删除for循环中的“-1”。如果我错了,请纠正我,但是,为了让它起作用,你需要保持上下文打开,这意味着您必须为整个viewmodel使用一个上下文(即,如果您希望能够在多个位置检测到更改)。仅仅附加实体也不起作用。在我的例子中,@BrokenGlass解决方案工作得更好,因为我在使用中包装了所有上下文调用。另外,如果属性是datetime,则需要转换属性:if(selfValue是datetime&&toValue是datetime){datetime from=(datetime)selfValue;datetime to=(datetime)toValue;if(!datetime.Equals(from,to)){}