entity framework6 – 使用我的getHashCode()

有一定的背景可以通过这个 – 请忍受我!

我们有一个使用EF的n层WPF应用程序 – 我们通过dbContext将数据从数据库加载到POCO类中。 销毁dbContext,然后用户就可以编辑数据。 我们使用Julie Lerman在她的书“Programming Entity Framework:DBContext”中建议的“状态绘画”,这样当我们将根实体添加到新的dbContext进行保存时,我们可以设置是否添加,修改或保持每个子实体等等。 。

当我们第一次这样做时(早在2012年11月!)我们遇到的问题是,如果我们添加到dbContext的根实体具有相同子实体的多个实例(即,链接到用户的“任务”记录, “状态历史记录”也链接到同一用户)进程将失败,因为即使子实体是相同的(来自同一数据库行),它们也被赋予不同的哈希码,因此EF将它们识别为不同的对象。

我们修复了这个问题(早在2012年12月!),通过在我们的实体上覆盖GetHashCode,如果实体来自数据库,则返回数据库ID,如果实体尚未保存,则返回唯一的负数。 现在,当我们将根实体添加到dbContext时,它足够聪明,可以实现多次添加相同的子实体并正确处理它。 自2012年12月以来我们一直运作良好,直到上周我们升级到EF6 ……

EF6的一个新“function”是它现在使用它自己的Equals和GetHashCode方法来执行更改跟踪任务,忽略任何自定义覆盖。 请参阅: http : //msdn.microsoft.com/en-us/magazine/dn532202.aspx (搜索“减少对编码风格的干扰”)。 如果您希望EF管理变更跟踪,但是在断开连接的n层应用程序中,我们不希望这样做,这很好,实际上这会破坏我们的代码,这些代码已经运行了一年多。

希望这是有道理的。

现在 – 问题 – 有没有人知道我们可以告诉EF6如何在EF5中使用我们的 GetHashCode和Equals方法,或者是否有人有更好的方法来处理将根实体添加到具有重复子实体的dbContext在它,以便EF6会满意吗?

谢谢你的帮助。 对不起,很长的post。

更新已经在EF代码中查看过,看起来像是通过获取实体的哈希码来设置InternalEntityEntry(dbEntityEntry)的哈希码,但是现在在EF6中使用RuntimeHelpers.GetHashCode(_entity)检索,这意味着我们被覆盖实体上的哈希码被忽略。 所以我想让EF6使用我们的哈希码是不可能的,所以我可能需要专注于如何将实体添加到可能具有重复子实体的上下文而不会扰乱EF。 有什么建议?

更新2最烦人的事情是这种function的变化被报告为好事,而不是,正如我所看到的,这是一个突破性的变化! 当然,如果你有断开连接的实体,并且你已经用.AsNoTracking()加载它们以获得性能(并且因为我们知道我们将断开它们,所以为什么还要跟踪它们),那么dbContext没有理由覆盖我们的getHashcode方法!

更新3感谢您的所有意见和建议 – 非常感谢! 经过一些实验后,它似乎与.AsNoTracking()有关。 如果使用.AsNoTracking()加载数据,则重复的子实体是内存中的单独对象(具有不同的哈希码),因此存在状态绘制问题并在以后保存它们。 我们之前通过覆盖哈希码修复了这个问题,因此当实体被添加回保存上下文时,重复的实体被识别为同一个对象并且只添加一次,但我们不能再使用EF6执行此操作。 所以现在我需要进一步研究为什么我们首先使用.AsNoTracking()。 另外一个想法是,EF6的更改跟踪器应该只对其主动跟踪的条目使用自己的哈希码生成方法 – 如果实体已经加载了.AsNoTracking(),它可能应该使用来自底层实体的哈希码吗?

更新4现在我们已经确定我们不能继续在EF6中使用我们的方法(重写的哈希码和.AsNoTracking),我们应该如何管理对断开连接的实体的更新? 我用blogposts / comments / authors创建了这个简单的例子: 图表和数据

在此示例中,我想打开blogpost 1,更改内容和作者,然后再次保存。 我已经尝试了3种方法与EF6,我无法让它工作:

BlogPost blogpost; using (TestEntities te = new TestEntities()) { te.Configuration.ProxyCreationEnabled = false; te.Configuration.LazyLoadingEnabled = false; //retrieve blog post 1, with all comments and authors //(so we can display the entire record on the UI while we are disconnected) blogpost = te.BlogPosts .Include(i => i.Comments.Select(j => j.Author)) .SingleOrDefault(i => i.ID == 1); } //change the content blogpost.Content = "New content " + DateTime.Now.ToString("HH:mm:ss"); //also want to change the author from Fred (2) to John (1) //attempt 1 - try changing ID? - doesn't work (change is ignored) //blogpost.AuthorID = 1; //attempt 2 - try loading the author from the database? - doesn't work (Multiplicity constraint violated error on Author) //using (TestEntities te = new TestEntities()) //{ // te.Configuration.ProxyCreationEnabled = false; // te.Configuration.LazyLoadingEnabled = false; // blogpost.AuthorID = 1; // blogpost.Author = te.Authors.SingleOrDefault(i => i.ID == 1); //} //attempt 3 - try selecting the author already linked to the blogpost comment? - doesn't work (key values conflict during state painting) //blogpost.Author = blogpost.Comments.First(i => i.AuthorID == 1).Author; //blogpost.AuthorID = 1; //attempt to save using (TestEntities te = new TestEntities()) { te.Configuration.ProxyCreationEnabled = false; te.Configuration.LazyLoadingEnabled = false; te.Set().Add(blogpost); // <-- (2) multiplicity error thrown here //paint the state ("unchanged" for everything except the blogpost which should be "modified") foreach (var entry in te.ChangeTracker.Entries()) { if (entry.Entity is BlogPost) entry.State = EntityState.Modified; else entry.State = EntityState.Unchanged; // <-- (3) key conflict error thrown here } //finished state painting, save changes te.SaveChanges(); } 

如果您在EF5中使用此代码,请使用我们现有的方法将.AsNoTracking()添加到原始查询中。

  blogpost = te.BlogPosts .AsNoTracking() .Include(i => i.Comments.Select(j => j.Author)) .SingleOrDefault(i => i.ID == 1); 

..并覆盖实体上的GetHashCode和Equals :(例如,在BlogPost实体中)..

  public override int GetHashCode() { return this.ID; } public override bool Equals(object obj) { BlogPost tmp = obj as BlogPost; if (tmp == null) return false; return this.GetHashCode() == tmp.GetHashCode(); } 

..代码中的所有三种方法现在都可以正常工作。

请问您能告诉我如何在EF6中实现这一目标吗? 谢谢

让您的应用程序在EF5中以这种方式工作是有趣和令人惊讶的。 EF始终只需要任何实体的单个实例。 如果添加了对象图并且EF错误地假定它在跟踪对象时实际上正在跟踪其他实例,则EF正在跟踪的内部状态将不一致。 例如,图表只使用.NET引用和集合,因此图形仍然有多个实例,但EF只跟踪一个实例。 这意味着可能无法正确检测到实体属性的更改,并且实例之间的修复也可能导致意外行为。 知道你的代码是否以某种方式解决了这些问题,或者你的应用程序没有遇到任何这些问题,因此无效的状态跟踪对你的应用程序无关紧要,这将会很有趣。

我们对EF6所做的更改使应用程序不太可能将EF状态跟踪转换为无效状态,从而导致意外行为。 如果你有一个聪明的模式来确保跟踪状态是有效的,我们打破了EF6,那么如果你可以在http://entityframework.codeplex.com/上提交一个完整的repro错误,那将会很棒。