如何处理断开连接的对象图中的重复项?

时间:2012-11-07 13:02:48

标签: asp.net entity-framework-4 save poco object-graph

我在ASP.NET应用程序中更新断开连接的POCO模型时遇到问题。

假设我们有以下型号:

  1. 用户
  2. 订单
  3. 用户可以负责0个或更多区域,订单属于区域,用户可以是订单的所有者。

    当用户登录用户并加载相关区域时。稍后,用户加载订单,并将自己设置为订单的所有者。用户(和相关区域)和订单(以及相关区域)使用两个不同的dbcontexts加载两个不同的调用。当我在用户指定自己之后保存订单时。我得到一个异常,说acceptchanges无法继续,因为对象的键值与另一个对象冲突。

    这并不奇怪,因为同一区域既可以出现在用户负责的区域列表中,也可以出现在订单上。

    我已经搜索了这个问题的解决方案,但是我找到的答案似乎是:

    1. 请勿在我的案例中加载其中一个对象的相关实体,这些对象将是用户的区域。
    2. 不要使用对象将用户分配给订单,只需在订单对象上设置外键ID。
    3. 使用nHibernate,因为它显然处理它。
    4. 我试过1并且有效,但我觉得这是错误的,因为我要么在将它与订单关联之前加载没有它的区域的用户,要么做一个浅的克隆。这对于这个简单的情况很好,但问题是在我的情况下,区域可能会在图表中出现几次。此外它似乎没有意义,因为我有对象,所以为什么不让我连接它们并更新图形。我需要订单的整个图表的原因是我需要向用户显示所有信息。所以既然我得到了所有的对象,为什么我需要重新加载或浅重克隆它才能使它工作?

      我尝试使用STE,但我遇到了同样的问题,因为我无法将对象附加到另一个上下文加载的图形中。所以我回到了1号广场。

      我认为除了教程代码之外,这是一个常见的问题。然而,我似乎无法找到任何好的解决方案。这让我觉得我在任何情况下都不理解使用POCO / EF或者我在使用谷歌找到这个问题的答案。

      我已经从朱莉娅·勒曼那里购买了O'Reilly的两本“编程实体框架”书籍,但似乎找不到任何可以在这些书中解决我的问题的书。

      是否有人可以解释如何处理图形,其中某些对象可能会重复,而不一定是从相同的上下文加载。

1 个答案:

答案 0 :(得分:0)

EF不允许将两个具有相同键的实体附加到上下文的原因是EF无法知道哪一个是“有效”。例如:您的对象图中可能有两个District个对象,两个都有一个键Id = 1,但这两个对象具有不同的Name属性值。哪一个代表必须保存到数据库的数据?

现在,您可以说两个对象都没有更改无关紧要,您只想将它​​们附加到状态Unchanged中的上下文,也许是为了与另一个实体建立关系。在这种特殊情况下,重复可能不是问题。但我认为,处理所有情况和不同状态的对象可能必须决定重复的对象是否导致含糊不清,这太复杂了。

无论如何,EF在对象引用标识和键属性值之间实现严格的标识映射,并且不允许有多个具有附加到上下文的给定键的实体。

我认为这种问题没有一般解决方案。除了你问题中的解决方案之外,我只能添加一些想法:

  • User附加到您正在加载订单的上下文中:

    context.Users.Attach(user); // attaches user AND user.Districts
    var order = context.Orders.Include("Districts")
        .Single(o => o.Id == someOrderId);
    // because the user's Districts are attached, no District with the same key
    // will be loaded again, EF will use the already attached Districts to
    // populate the order.Districts collection, thus avoiding duplicate Districts
    order.Owner = user;
    context.SaveChanges();
    // it should work without exception
    
  • 仅将实体附加到您需要的上下文以执行特殊更新:

    using (var context = new MyContext())
    {
        var order = new Order { Id = order.Id };
        context.Orders.Attach(order);
        var user = new User { Id = user.Id };
        context.Users.Attach(user);
    
        order.Owner = user;
    
        context.SaveChanges();
    }
    

    这足以更新Owner关系。您不需要此过程的整个对象图,您只需要为必须为其创建关系的实体的正确主键值。如果您有更多要保存的更改或者不知道究竟可以更改哪些更改,那么它当然不会那么容易。

  • 根本不要将对象图附加到上下文中。而是从数据库中加载表示当前存储在数据库中的对象图的新实体。然后使用分离的对象图更新加载的图形,并保存应用于加载(=附加)图形的更改。显示了此过程的一个示例here。它是安全且非常通用的模式(但不是通用的)但对于复杂的对象图可能非常复杂。

  • 遍历对象图并用唯一的对象替换重复的对象,例如只有第一个具有您找到的类型和密钥的对象。您可以构建一个您查找的唯一对象的字典来替换重复项。一个例子是here