首页 > 解决方案 > 为多对多关系创建默认关系对象,并在我的数据库上下文中保持更改?

问题描述

我想要做什么:当用户选择一个产品时,用每个产品填充一个数据网格。如果该产品/事件组合具有关联的 EventProduct,则使用该数据填充数据网格的其他部分。如果没有,则创建一个新的 EventProduct 并将所有属性默认为 0。保存事件时,如果 EventProduct 属性已更改或已填充,则将该 EventProduct 作为新的 EventProduct 保存到数据库中。

我目前的方法:我有三个类:Event、Product 和 EventProduct,如此处定义(截断)。

public partial class Event
{
   [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
   public Event()
   {
      EventProducts = new HashSet<EventProduct>();
   }

   [Key]
   public int index { get; set; }

   [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
   public virtual ICollection<EventProduct> EventProducts { get; set; }

}

public partial class Product
{
   [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
   public Product()
   {
      EventProducts = new HashSet<EventProduct>();
   }

   [Key]
   public int index { get; set; }
   [StringLength(200)]
   public string name { get; set; }

   [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
   public virtual ICollection<EventProduct> EventProducts { get; set; }
}

public partial class EventProduct
    {
        public EventProduct()
        {
            Event = new Event();
            Product = new Product();
            quantity_allocated = 0;
            quantity_sold = 0;
            quantity_sampled = 0;
        }
        public int index { get; set; }
        public int EventID { get; set; }
        public int ProductID { get; set; }
        public int? quantity_allocated { get; set; }
        public int? quantity_sold { get; set; }
        public decimal? quantity_sampled { get; set; }

        public virtual Event Event { get; set; }
        public virtual Product Product { get; set; }
    }

我通过查询我的产品并将其连接到我的 EventProducts 来填充表,并创建一个新的关联对象,该对象具有一对一关系的 Product 和 EventProduct。我将我的 itemsource 设置为以下内容:

public static List<ProductEventProduct> GetProductEventProduct(Event e, DatabaseModel dbContext)
{
   var query = from product in dbContext.Products
               join eventProduct in dbContext.EventProducts
                     on new { pIndex = product.index, eIndex = e.index }
                        equals new { pIndex = eventProduct.Product.index, eIndex = eventProduct.Event.index } into temp
               from eventProduct in temp.DefaultIfEmpty()
               select new ProductEventProduct
               {
                     Product = product,
                     EventProduct = eventProduct
               };
   var dataSource = query.ToList();
   foreach (ProductEventProduct entry in dataSource)
   {
         if (entry.EventProduct == null)
         {
            entry.EventProduct = new EventProduct()
            {
               EventID = e.index,
               ProductID = entry.Product.index,
               Product = entry.Product,
               Event = e
            };
         }
   }
   return dataSource;
}

当我有一个手动输入(直接输入我的数据源)EventProduct 时,它会按预期工作,并且用户可以编辑分配的数量(已售和采样在此视图中被锁定): 事件窗口上正确填充的数据网格的图像

我的问题是储蓄。现在我正在遍历数据网格的每一行,如果它已被更改或者值不为空,请从中创建一个 EventProduct 并将该 EventProduct 添加到我的数据库上下文中:

List<Associations.ProductEventProduct> entries = (List<Associations.ProductEventProduct>)EventProductDataGrid.ItemsSource;
IEnumerable<Associations.ProductEventProduct> changedEntries = entries.Where(association =>
   association.EventProduct.quantity_allocated != 0 ||
   association.EventProduct.quantity_sampled != 0 ||
   association.EventProduct.quantity_sold != 0);
foreach (Associations.ProductEventProduct entry in changedEntries)
{
   // if there are no event products in the database that have the same product and event, it's new so save it to DB
   if (!(dbContext.EventProducts.Any(ep =>
      ep.EventID == entry.EventProduct.EventID && ep.ProductID == entry.Product.index)))
   {
      dbContext.EventProducts.Add(entry.EventProduct); // line where I get the error described below
      dbContext.SaveChanges();
   }
   else // if it is an EventProduct which exists in the database already
   {
      EventProduct modifyEvent = dbContext.EventProducts.Single(ep => ep.Event.index == entry.EventProduct.Event.index && ep.Product.index == entry.Product.index);
      modifyEvent.quantity_allocated = entry.EventProduct.quantity_allocated;
      modifyEvent.quantity_sampled = entry.EventProduct.quantity_sampled;
      modifyEvent.quantity_sold = entry.EventProduct.quantity_sold;
   }
}
dbcontext.SaveChanges();

但是当我的 EventProduct 添加到我的 DBContext 时,我收到错误,“'发生了参照完整性约束冲突:当依赖对象未更改时,作为参照完整性约束一部分的主键属性无法更改,除非它被设置为关联的主体对象。主体对象必须被跟踪并且不能被标记为删除。'"。这对我来说没有意义,因为它对 Product 和 Event 的引用在我的调试器中都是填充的、有效的和正确的。

几天来,我一直被困在这个问题的各个方面,我知道我的方法是错误的,任何建议都将不胜感激。

标签: c#entity-frameworkentity-framework-6relational-database

解决方案


我想你的问题是EventProduct你添加到你的DbContext引用一个EventProduct(或两者)已经存在于数据库中,但当前没有被DbContext. 当调用dbContext.EventProducts.Add(entry.EventProduct);它时,它会尝试添加entry.EventProduct.Eventand entry.EventProduct.ProductDbContext就好像它们是新实体一样。

如果您知道entry.EventProduct.Event并且entry.EventProduct.Product已经存在于数据库中,那么您可以将它们添加到更改跟踪器,让 EF 知道它们已经存在并且没有更改:

// Let EF know the entities already exist
dbContext.Set<Event>().Attach(entry.EventProduct.Event);
dbContext.Set<Product>().Attach(entry.EventProduct.Product);

// Now add the EventProduct letting it refer to the existing Event and Product
dbContext.EventProducts.Add(entry.EventProduct);
dbContext.SaveChanges();

注意:根据文档,您附加的实体将获得状态Unchanged,这意味着如果您确实对数据库进行了更改,Event或者Product您想要在数据库中更新,您应该使用DbContext.Entry()并将返回的Entry.State设置为Modified.


推荐阅读