【问题标题】:The instance of entity type 'x' cannot be tracked because another instance with the same key value for {'a', 'b'} is already being tracked无法跟踪实体类型“x”的实例,因为已经在跟踪具有相同键值 {'a', 'b'} 的另一个实例
【发布时间】:2021-01-24 12:58:28
【问题描述】:

我在编辑多对多关系之间的关系表时出现标题错误。它不会让表上出现重复,所以我尝试删除行然后创建新行,但它不起作用。

public void Update(ThermoformProduct entity, int[] thermoformCategoryIds)
{
    using (var context = new ShopContext())
    {
        var product = context.ThermoformProducts                  
            .Include(i => i.ThermoformProductCategories)                
            .FirstOrDefault(i => i.ProductId == entity.ProductId);            

        if (product != null)
        {           
            product.Code = entity.Code;
            product.Culture = entity.Culture;
            product.Renk = entity.Renk;
            product.UstGenislik = entity.UstGenislik;
            product.UstCap = entity.UstCap;
            product.AltCap = entity.AltCap;
            product.TbCap = entity.TbCap;
            product.Yukseklik = entity.Yukseklik;
            product.Hacim = entity.Hacim;
            product.TamHacim = entity.TamHacim;
            product.Baski = entity.Baski;
            product.SosisIciAdet = entity.SosisIciAdet;
            product.KoliIciAdet = entity.KoliIciAdet;
            product.ImageUrl = entity.ImageUrl;

            product.ThermoformProductCategories.RemoveAll(s=>s.ProductId == product.ProductId);

            product.ThermoformProductCategories = thermoformCategoryIds.Select(catid =>  new ThermoformProductCategory()
            {
                ProductId = product.ProductId,
                ThermoformProduct = product,
                CategoryId = catid,
                ThermoformCategory = context.ThermoformCategories.Where(i => i.CategoryId == catid).FirstOrDefault()

            }).ToList();

            context.SaveChanges();
        }
    }
}

【问题讨论】:

    标签: entity-framework asp.net-core entity-framework-core


    【解决方案1】:

    EF 无法跟踪具有相同主键的实体类型的两个不同实例。您已包含相关的 ThermoformProductCategory 实体,因此它们正在被上下文跟踪。当您删除它们时,它们会从该产品的 ThermoformProductCategories 属性中清除,但它们不会从上下文中删除,并且仍在被跟踪。最后,当您创建 ThermoformProductCategory 的新列表时,一些新的主键与以前的主键匹配(已经存在于上下文中)

    由于您要再次创建整个列表,因此您不需要首先获取相关实体。只需分配一个新列表,EF 就会替换整个相关实体列表 -

    var product = context.ThermoformProducts.FirstOrDefault(i => i.ProductId == entity.ProductId);
    if (product != null)
    {       
        // set all the properties
    
        product.ThermoformProductCategories = thermoformCategoryIds.Select(catid =>  new ThermoformProductCategory()
        {
            ProductId = product.ProductId,
            CategoryId = catid
        }).ToList();
    
        context.SaveChanges();     
    }
    

    【讨论】:

      【解决方案2】:

      两件事:

      1. 过滤器是多余的 - ThermoformProductCategories 导航属性应该已经被过滤了。
      product.ThermoformProductCategories.RemoveAll(s=>s.ProductId == product.ProductId);
      

      改为这样做:

      product.ThermoformProductCategories.RemoveAll(); // Or .Clear()
      
      1. 在这种情况下不要设置导航属性,只设置外键值 - 这应该可以解决您的问题:

      代替:

      product.ThermoformProductCategories = thermoformCategoryIds.Select(catid =>  new ThermoformProductCategory()
      {
          ProductId = product.ProductId,
          ThermoformProduct = product,
          CategoryId = catid,
          ThermoformCategory = context.ThermoformCategories.Where(i => i.CategoryId == catid).FirstOrDefault()
      
      }).ToList();
      

      做:

      product.ThermoformProductCategories = thermoformCategoryIds.Select(catid =>  new ThermoformProductCategory()
      {
          ProductId = product.ProductId, // Even this might be redundant since you're adding to the product navigation property list.
          CategoryId = catid
      }).ToList();
      

      【讨论】:

        【解决方案3】:

        两个答案都是解决方案,如果有人遇到麻烦,这是我的最终形式

         public void Update(ThermoformProduct entity, int[] thermoformCategoryIds)
            {
                using (var context = new ShopContext())
                {
                    var product = context.ThermoformProducts                  
                        .FirstOrDefault(i => i.ProductId == entity.ProductId);            
        
                    if (product != null)
                    {
                        
                        product.Code = entity.Code;
                        product.Culture = entity.Culture;
                        product.Renk = entity.Renk;
                        product.UstGenislik = entity.UstGenislik;
                        product.UstCap = entity.UstCap;
                        product.AltCap = entity.AltCap;
                        product.TbCap = entity.TbCap;
                        product.Yukseklik = entity.Yukseklik;
                        product.Hacim = entity.Hacim;
                        product.TamHacim = entity.TamHacim;
                        product.Baski = entity.Baski;
                        product.SosisIciAdet = entity.SosisIciAdet;
                        product.KoliIciAdet = entity.KoliIciAdet;
                        product.ImageUrl = entity.ImageUrl;
                     
                        var cmd = "delete from ThermoformProductCategory where ProductId=@p0";
                        context.Database.ExecuteSqlRaw(cmd, product.ProductId);    
                        
                        product.ThermoformProductCategories = thermoformCategoryIds.Select(catid =>  new ThermoformProductCategory()
                        {
                            ProductId = product.ProductId,                       
                            CategoryId = catid,
                            ThermoformCategory = context.ThermoformCategories.Where(i => i.CategoryId == catid).FirstOrDefault()
                        }).ToList();
        
                        context.Entry(product).State = EntityState.Modified;                   
                        context.SaveChanges();                   
                    }
                   
                }
            }
        

        【讨论】:

        • 如果您在加载产品时通过includeing ThermoformProductCategories 让 EntityFramework 完成它的工作(跟踪更改),则不需要使用 Raw SQL 命令,然后改为操作加载的列表.像这样运行原始 SQL 会在 EF 后面进行,并对 EF 不知道的数据存储进行更改,这可能会在未来导致问题。
        • @Lacutah 在解决实例错误后我遇到了重复的原始错误和 productcategory.removeall 和 .clear 方法不起作用
        • 如果您包含类别,它将正常工作,然后 EF 将跟踪对列表所做的更改:var product = context.ThermoformProducts.Include(p => p.ThermoformProductCategories) .FirstOrDefault(i => i.ProductId == entity.ProductId); - 这是 EF Core 5.0+ 的一个功能
        • (有时版本很重要,哈哈)
        猜你喜欢
        • 2022-01-02
        • 2021-11-24
        • 2020-10-16
        • 2018-12-01
        • 2021-08-27
        • 1970-01-01
        • 1970-01-01
        • 2023-01-03
        • 1970-01-01
        相关资源
        最近更新 更多