EF代码优先:保存时复制数据(多对多)

时间:2012-08-03 13:02:29

标签: .net entity-framework

我正在使用实体框架,并且遇到了关于多对多映射的绊脚石。 假设我有两个实体,订单和产品。

在我的订单实体中,我有:

int OrderID,
double OrderPrice
datetime OrderDate
ICollection<Product> Products
datetime DispactDate

依旧......

然后在我的产品类中

int ProductID
string ProductName
string ProductDescription

等等。

在我的Code First EF DB中,我有一个订单表,它可以很好地映射到我的订单,一个产品表,其中包含可以订购的产品列表,然后使用EF代码优先迁移EF创建了带有外键的OrderProducts其他表的键。一切都好。

但是,当我去保存其产品系列中包含多个产品的订单时,EF会复制Product表中的所有条目。所以,如果我添加产品10,12和13,它的三个新行装有三个新的ID并映射到它们,而不是原件。如果我再用另外20个现有产品处理另一个订单,那么在产品表中会有20个新条目!

在我的DBContext中,我覆盖了onModelCreatingEvent:

modelBuilder.Entity<Order>()
                .HasMany(x=>x.Products)
                .WithMany()
                .Map(be =>
                    {
                        be.ToTable("OrderProducts");
                });

我猜这里配置错了。我是从How to map many-to-many relationships in Entity Framework CTP5?

得到的

以前我有两个独立的存储库 - 一个用于订单,一个用于产品。我现在将这些合并到orderRepository中,它公开订单和产品,以便我可以将产品附加到orderRepository上下文。 为了生成产品,我有一个Controller动作AddProducts:

public RedirectToRouteResult Products(FormCollection selectedProducts)
        {
                currentOrder.Products = new List<Product>();
                foreach (string thisProduct in selectedProducts)
                {
                    int thisProductID = int.Parse(thisProduct);
                    Product selectedProduct = orderRepository.Products.Where(e => e.ProductID == thisProductID).FirstOrDefault();

                    if (selectedProduct != null)
                    {
                        orderRepository.AttachToContext(selectedProduct);
                        currentOrder.Products.Add(selectedProduct);
                    }                    
                }

                orderRepository.saveChanges(currentOrder);

}

3 个答案:

答案 0 :(得分:1)

我怀疑在orderRepository中你有一个不同于ProductsRepo的上下文实例。您应首先attach将产品发送到Products上下文中的orderRepository

顺便说一句,这就是为什么每个实体的存储库可以真正阻碍你的方式的一个例子。如果要使用存储库模式,通常最好在聚合根中进行思考。

答案 1 :(得分:0)

您是否可以点击一个小单元测试来创建上下文,检索产品,创建订单并将检索到的产品添加到订单中?

这应该绝对正常。听起来您正在创建一个新的产品对象而不是使用预先存在的产品对象。或者创建一个而不是设置适当的实体密钥。

这听起来并不像是从不同的上下文中获取它,因为您通常会收到警告它已经附加到另一个上下文。

答案 2 :(得分:0)

感谢。管理解决这个问题。在我的OrderRepository中,我必须手动标记每个Product状态EntitySate.Unchanged。所以,在SaveOrder()我现在做:

foreach (Product product in Orders.Products)
{
  context.Entry(product).State = System.Data.EntityState.Unchanged;
}

这工作正常,但我很好奇为什么在订单中的每个产品都已经分配给它的有效(和现有)产品时,我必须执行此步骤? 奇怪。