2

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

假设我们有以下模型:

  1. 用户
  2. 订单

一个用户可以负责0个或多个区,一个订单属于一个区,一个用户可以是一个订单的所有者。

当用户登录时,加载相关的分区。稍后用户加载订单,并将自己设置为订单的所有者。用户(和相关地区)和订单(和相关地区)在两个不同的调用中加载,具有两个不同的 dbcontexts。当我在用户将自己分配给订单后保存订单时。我得到一个例外,说 acceptchanges 不能继续,因为对象的键值与另一个对象冲突。

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

我一直在寻找解决这个问题的方法,但我找到的答案似乎是:

  1. 在我的情况下,不要加载对象之一的相关实体,这将是用户的区域。
  2. 不要使用对象将用户分配给订单,只需在订单对象上设置外键 id。
  3. 使用 nHibernate,因为它显然可以处理它。

我尝试了 1 并且可行,但我觉得这是错误的,因为我要么必须在将其与订单相关联之前加载没有其区域的用户,要么进行浅层克隆。这对于这里的这个简单案例来说很好,但问题是在我的案例中,区域可能会在图表中出现多次。而且这似乎毫无意义,因为我有对象,所以为什么不让我连接它们并更新图表。我需要订单的整个图表的原因是我需要向用户显示所有信息。所以既然我得到了所有的对象,为什么我需要重新加载或浅克隆它才能让它工作?

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

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

我已经从 Julia Lerman 的 O'Reilly 购买了两本“Programming Entity Framework”书籍,但似乎也无法在这些书中找到任何解决我问题的方法。

有没有人可以阐明如何处理某些对象可能重复且不一定从同一上下文加载的图形。

4

1 回答 1

0

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

现在,您可以说如果两个对象都没有更改也没关系,您只想将它​​们附加到 state 中的上下文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

于 2012-11-07T22:00:09.103 回答