实体框架6 - 用我的GetHashCode() [英] Entity Framework 6 - use my getHashCode()

查看:151
本文介绍了实体框架6 - 用我的GetHashCode()的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有是为这一个打通了一定的背景 - !请多多包涵。

There's a certain amount of background to get through for this one - please bear with me!

我们必须使用EF一个n层WPF应用程序 - 我们加载从通过的DbContext数据库到POCO类的数据。所述的DbContext被破坏,并且用户随后能够编辑数据。
我们用状态画由朱莉·勒曼在她的书中提出编程实体框架:的DbContext所以,当我们添加了根实体到一个新的DbContext救了我们可以设置每个孩子实体是否被添加,修改或保持不变等等。

We have a n-tier WPF application using EF - we load the data from the database via dbContext into POCO classes. The dbContext is destroyed and the user is then able to edit the data. We use "state painting" as suggested by Julie Lerman in her book "Programming Entity Framework: DBContext" so that when we add the root entity to a new dbContext for saving we can set whether each child entity is added, modified or left unchanged etc.

我们已经当我们第一次(2012年11月!回)这样做的问题是,如果我们要添加到的DbContext根实体有多个同一子实体的实例(即,一个任务记录链接到用户,以状态历史记录的链接到同一用户),因为即使子实体是相同的(从相同的数据库行的过程将失败),他们分别给予不同的哈希码使EF承认他们是不同的对象。

The problem we had when we first did this (back in November 2012!) was that if the root entity we are adding to the dbContext has multiple instances of the same child entity (ie, a "Task" record linked to a user, with "Status Histories" also linked to the same user) the process would fail because even though the child entities were the same (from the same database row) they were given different hashcodes so EF recognised them as different objects.

我们解决了这个问题,(早在2012年12月!),通过我们的实体压倒一切的GetHashCode返回要么数据库ID如果实体从数据库或独特的负数来了,如果实体是尚未得救。现在,当我们添加了根实体到的DbContext它是足够聪明来实现相同的子实体被添加多次并正确处理它。这一直罚款,因为2012年12月,直到我们升级上周EF6 ...

We fixed this, (back in December 2012!), by overriding GetHashCode on our entities to return either the database ID if the entity came from the database, or an unique negative number if the entity is as yet unsaved. Now when we add the root entity to the dbContext it was clever enough to realise the same child entity is being added more than once and it dealt with it correctly. This has been working fine since December 2012 until we upgraded to EF6 last week...

一个新的功能与EF6是,它现在使用它自己的Equals和GetHashCode方法执行更改跟踪任务,忽略任何自定义覆盖。请参阅: http://msdn.microsoft.com/en-us/magazine/dn532202的.aspx (搜索你的编码风格干扰少)。如果你期望EF管理更改跟踪,但在断开连接的n层应用程序,我们不希望这一点,其实这打破我们的代码已经工作正常了一年多,这是很大的。

One of the new "features" with EF6 is that it now uses it's own Equals and GetHashCode methods to perform change-tracking tasks, ignoring any custom overrides. See: http://msdn.microsoft.com/en-us/magazine/dn532202.aspx (search for "Less Interference with your coding style"). This is great if you expect EF to manage the change-tracking but in a disconnected n-tier application we don't want this and in fact this breaks our code that has been working fine for over a year.

希望这是有道理的。

现在 - 的问题 - 没有人知道任何方式,我们可以告诉EF6使用的我们的的GetHashCode的和equals方法,如它在EF5一样,或有没有人有更好的方式来处理增加了根实体,在已复制子实体的DbContext使EF6会很高兴呢?

Now - the question - does anyone know of any way we can tell EF6 to use OUR GetHashCode and Equals methods like it did in EF5, or does anyone have a better way to deal with adding a root entity to a dbContext that has duplicated child entities in it so that EF6 would be happy with it?

感谢您的帮助。很抱歉的长期职位。

Thanks for any help. Sorry for the long post.

更新时间:
已经在EF代码它看起来像一个InternalEntityEntry的哈希码戳左右(dbEntityEntry ),用于通过获取该实体的哈希码进行设置,但现在在EF6通过使用RuntimeHelpers.GetHashCode(_entity),这意味着我们对实体重写哈希码被忽略检索。所以我想获得EF6使用我们的哈希码是出了问题,所以也许我需要专注于如何将实体添加到潜在的复制了子实体不打破EF上下文。有什么建议?

UPDATED Having poked around in the EF code it looks like the hashcode of an InternalEntityEntry (dbEntityEntry) used to be set by getting the hashcode of the entity, but now in EF6 is retrieved by using RuntimeHelpers.GetHashCode(_entity), which means our overridden hashcode on the entity is ignored. So I guess getting EF6 to use our hashcode is out of the question, so maybe I need to concentrate on how to add an entity to the context that potentially has duplicated child entities without upsetting EF. Any suggestions?

更新2
最可气的是,这一功能更改被报告为好事,不,在我看来,一个重大更改!当然,如果你已经断开实体,并且您已经与.AsNoTracking()性能加载他们(因为我们知道我们要切断他们何必跟踪它们)则没有理由的DbContext重写我们的GetHashCode的方法!

UPDATE 2 The most annoying thing is that this change in functionality is being reported as a good thing, and not, as I see it, a breaking change! Surely if you have disconnected entities, and you've loaded them with .AsNoTracking() for performance (and because we know we are going to disconnect them so why bother tracking them) then there is no reason for dbContext to override our getHashcode method!

更新3
感谢所有的意见和建议 - 非常感谢!
一些实验后它似乎与到.AsNoTracking()。如果用.AsNoTracking加载数据()重复的子实体在内存单独的对象(具有不同的散列码),所以存在一个问题,国家绘画,后来保存它们。我们解决了这个问题,早些时候通过覆盖哈希码,所以当实体被加回重复的实体被认为是同一个对象,只添加一次保存上下文,但我们不能再这样做有EF6。所以现在我需要进一步调查,为什么我们在第一时间使用.AsNoTracking()。
另外一个想法我是,也许EF6的变化跟踪器只能使用自己的哈希码生成方法条目正在积极跟踪 - 如果实体被装载.AsNoTracking()也许它应该使用的哈希码从底层的实体?

UPDATE 3 Thanks for all the comments and suggestion - really appreciated! After some experiments it does appear to be related to .AsNoTracking(). If you load the data with .AsNoTracking() duplicate child entities are separate objects in memory (with different hashcodes) so there is a problem state painting and saving them later. We fixed this problem earlier by overriding the hashcodes, so when the entities are added back to the saving context the duplicate entities are recognised as the same object and are only added once, but we can no longer do this with EF6. So now I need to investigate further why we used .AsNoTracking() in the first place. One other thought I have is that maybe EF6's change tracker should only use its own hashcode generation method for entries it is actively tracking - if the entities have been loaded with .AsNoTracking() maybe it should instead use the hashcode from the underlying entity?

更新4
现在,我们已经确定,我们不能继续使用我们的方法(重写哈希码和在EF6 .AsNoTracking),如何的的我们设法更新断开实体?我已经创建了相关博客文章/评论/作者这个简单的例子:

UPDATE 4 So now we've ascertained we can't continue to use our approach (overridden hashcodes and .AsNoTracking) in EF6, how should we manage updates to disconnected entities? I've created this simple example with blogposts/comments/authors:

在这个例子中,我想开的博文1,更改内容和作者,并再次保存。我试着EF6 3的方法,我无法得到它的工作:

In this sample, I want to open blogpost 1, change the content and the author, and save again. I've tried 3 approaches with EF6 and I can't get it to work:

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<BlogPost>().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(音)与原来的查询。

If you use this code in EF5, using our existing approach of adding .AsNoTracking() to the original query..

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

..和压倒一切的GetHashCode和等于对实体(例如,在博文实体)。

..and overriding GetHashCode and Equals on the entities: (for example, in the BlogPost entity)..

    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();
    }

..代码中的三种方法现在工作得很好。

..all three approaches in the code now work fine.

请你能告诉我如何在EF6来实现这一目标?谢谢

Please can you tell me how to achieve this in EF6? Thanks

推荐答案

这是有趣和令人惊讶的是你有你的应用程序中工作这种方式EF5。 EF总是仅需要的任何实体的单个实例。如果对象图被添加和EF错误地假定它已经跟踪对象时,它实际上是在跟踪一个不同的实例,则该EF被跟踪的内部状态将不一致。例如,该图只是使用.NET引用和集合,使图形仍会有多个实例,但EF将仅跟踪一个实例。这意味着改变一个实体的属性可能无法正确地检测和实例之间修正也可能会导致意想不到的行为。如果你的代码解决在某种程度上这些问题,或者如果它只是碰巧你的应用程序没有击中任何这些问题,因此无效状态跟踪并没有为你的应用程序不管这将是有趣知道。

It’s interesting and surprising that you got your application working this way in EF5. EF always requires only a single instance of any entity. If a graph of objects are added and EF incorrectly assumes that it is already tracking an object when it is in fact tracking a different instance, then the internal state that EF is tracking will be inconsistent. For example, the graph just uses .NET references and collections, so the graph will still have multiple instances, but EF will only be tracking one instance. This means that changes to properties of an entity may not be detected correctly and fixup between instances may also result in unexpected behavior. It would be interesting to know if your code solved these problems in some way or if it just so happened that your app didn’t hit any of these issues and hence the invalid state tracking didn’t matter for your app.

我们的EF6所做的更改使得它不太可能是一个应用程序可以得到EF状态追成无效状态,那么这将导致意外的行为。如果你有一个聪明的方式,以确保跟踪状态是有效的,我们有EF6打破那么这将是巨大的,如果你能在的 http://entityframework.codeplex.com/

The change we made for EF6 makes it less likely that an app can get EF state tracking into an invalid state which would then cause unexpected behavior. If you have a clever pattern to ensure the tracking state is valid that we broke with EF6 then it would be great if you could file a bug with a full repro at http://entityframework.codeplex.com/.

这篇关于实体框架6 - 用我的GetHashCode()的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆