我几年来一直在使用Entity Framework。我的项目是Database First。我最近从EF 4升级到EF 6.我有代码生成模板,使用以下代码创建部分类,用于标量属性:
Private Nullable<double> _ScalarProp;
public virtual Nullable<double> ScalarProp
{
get
{
return _ScalarProp;
}
set
{
if(_ScalarProp != value)
{
if(IsNotifyEnabled)
{
OnPropertyChanging("ScalarProp");
OnScalarPropChanging();
}
_ScalarProp = value;
if(IsNotifyEnabled)
{
OnPropertyChanged("ScalarProp");
OnScalarPropChanged();
}
}
}
}
partial void OnScalarPropChanging();
partial void OnScalarPropChanged();
我的问题是,当为一个与当前值相同的属性赋值时,上面的代码会绕过赋值语句,而实体的状态是从Unchanged变为Modified。怎么了? Change Tracker是否完全独立于模板化实体运行?如何解决此问题,以便当指定的值与当前值相同时,实体保持不变?
答案 0 :(得分:1)
是否将值设置为计算结果?如果这些值是计算结果,则与双精度的等式比较可能会出现意外行为。您可能希望实现与容差的比较
if (Math.Abs(_ScalarProp.Value - value.Value) > 0.000001)
{ // etc.
(在使用之前,您需要手动检查可空类型的空值。)
答案 1 :(得分:1)
似乎问题是ChangeTracker
并不试图区分新值是否与我的属性设置器的覆盖中的现有值不同。相反,它只是在调用setter时将属性标记为已修改。这对我来说似乎很奇怪,因为我认为它会尝试通过清除非更改来最小化返回数据库的流量。我很想知道这是否是其他人正在看到的行为,或者它是否是我对EF的特定实现的简单怪癖(翻译:我搞砸了一些东西)。作为一种解决方法,我将以下方法添加到我的实体的基类中:
public abstract class EntityBase: INotifyPropertyChanged, INotifyPropertyChanging
{
...
public bool WasUpdated()
{
var entry = MyEntities.Context.Entry(this);
if (entry.State != EntityState.Modified)
return false;
bool changed = false;
foreach (var propName in entry.CurrentValues.PropertyNames)
{
changed = changed || (entry.CurrentValues[propName] != entry.OriginalValues[propName] && !entry.CurrentValues[propName].Equals(entry.OriginalValues[propName]));
if (changed)
break;
}
return changed;
}
}
我会使用这个,直到有人能够帮我弄清楚这是ChangeTracker
的预期行为还是我搞砸了我的实施。
答案 2 :(得分:0)
没有答案。但我有一个工具,可以找到何时以及为什么。 添加到您的上下文类。您可以随时从即时窗口调用它。
myContext.FullDump();
public void FullDump(bool ignoreUnchanged = false)
{
Debug.WriteLine("=====Begin of Context Dump=======");
var dbsetList = this.ChangeTracker.Entries();
foreach (var dbEntityEntry in dbsetList)
{
if (ignoreUnchanged && dbEntityEntry.State == EntityState.Unchanged)
{
continue;
}
Debug.WriteLine(dbEntityEntry.Entity.GetType().Name + " => " + dbEntityEntry.State);
switch (dbEntityEntry.State)
{
case System.Data.Entity.EntityState.Detached:
case System.Data.Entity.EntityState.Unchanged:
case System.Data.Entity.EntityState.Added:
case System.Data.Entity.EntityState.Modified:
WriteCurrentValues(dbEntityEntry);
break;
case System.Data.Entity.EntityState.Deleted:
WriteOriginalValues(dbEntityEntry);
break;
default:
throw new ArgumentOutOfRangeException();
}
Debug.WriteLine("==========End of Entity======");
}
Debug.WriteLine("==========End of Context======");
}
private static void WriteCurrentValues(DbEntityEntry dbEntityEntry)
{
foreach (var cv in dbEntityEntry.CurrentValues.PropertyNames)
{
Debug.WriteLine(cv + "=" + dbEntityEntry.CurrentValues[cv]);
}
}
private static void WriteOriginalValues(DbEntityEntry dbEntityEntry)
{
foreach (var cv in dbEntityEntry.OriginalValues.PropertyNames)
{
Debug.WriteLine(cv + "=" + dbEntityEntry.OriginalValues[cv]);
}
}
}