在Entity Framework核心中更新多对多 [英] Updating many to many in Entity Framework core

查看:67
本文介绍了在Entity Framework核心中更新多对多的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下是相关的类及其配置:

Here are the relevant classes and their configuration:

public class Product 
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public long ProductId { get; set; }

    [Required]
    public string ProductName { get; set; }

    public ICollection<ProductSpecification> ProductSpecifications { get; set; }
} 

public class ProductAttributeValue 
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public long ProductAttributeValueId { get; set; }

    [Required]
    public string ProductAttributeValueName { get; set; }

    public ICollection<ProductSpecification> ProductSpecifications { get; set; }
}

public class ProductSpecification
{
    public long ProductId { get; set; }
    public long ProductAttributeValueId { get; set; }

    public string Note { get; set; }

    public Product Product { get; set; }
    public ProductAttributeValue ProductAttributeValue { get; set; }
}

// Configuration in the dbConext
modelBuilder.Entity<ProductSpecification>().HasKey(ps => new { ps.ProductId, ps.ProductAttributeValueId });
modelBuilder.Entity<ProductSpecification>().HasOne(p => p.Product).WithMany(ps => ps.ProductSpecifications).HasForeignKey(ps => ps.ProductId);
modelBuilder.Entity<ProductSpecification>().HasOne(pav => pav.ProductAttributeValue).WithMany(ps => ps.ProductSpecifications).HasForeignKey(ps => ps.ProductAttributeValueId);

在控制器中:

public async Task<IActionResult> UpdateProduct([FromRoute] long id, [FromBody] Product product)
{
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }

    if (id != product.ProductId)
    {
        return BadRequest();
    }

    Product productToBeUpdated = await _unitOfWork.Repository<Product>().GetEntityList(p => p.ProductId == id).Include(p => p.ProductSpecifications).SingleOrDefaultAsync();

    if (productToBeUpdated == null)
    {
        return NotFound();
    }

    foreach (ProductSpecification productSpecification in productToBeUpdated.ProductSpecifications.ToList())
    {
        productToBeUpdated.ProductSpecifications.Remove(productSpecification);
    }

    productToBeUpdated.ProductSpecifications = product.ProductSpecifications;

    productToBeUpdated.ModifiedOn = DateTime.UtcNow;
    await _unitOfWork.SaveChangesAsync();

    return Ok(true);
 }

我也尝试过:

foreach (ProductSpecification productSpecification in productToBeUpdated.ProductSpecifications.ToList())
{
    _unitOfWork.Repository<ProductSpecifications>().DeleteEntity(productSpecification);
}

他们两个都抛出以下异常:

Both of them throwing the following exception:

无法跟踪实体类型'ProductSpecification'的实例,因为已经跟踪了具有相同的{'ProductId','ProductAttributeValueId'}关键字值的另一个实例.附加现有实体时,请确保仅附加一个具有给定键值的实体实例.考虑使用'DbContextOptionsBuilder.EnableSensitiveDataLogging'来查看冲突的键值.

The instance of entity type 'ProductSpecification' cannot be tracked because another instance with the same key value for {'ProductId', 'ProductAttributeValueId'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values.

找不到真正的问题所在!任何帮助将不胜感激!

Could not find where the problem actually lies! Any help will be highly appreciated!

推荐答案

终于确定了好问题!在 Entity Framework 6.x 中,在多对多导航属性更新期间,我们可以清除/删除现有的子列表,然后将新的子列表添加到父项中,最后更新父项,并且父项是更新了新的子列表.

Well problem is finally identified! In Entity Framework 6.x, during many to to many navigation properties update, we can clear/delete the existing child list and then add the new child list to the parent and finally update the parent and parent is updated with the new child list.

但是在 Entity Framework Core 中,我们不能做同样的事情.错误消息表明我们不能多次拥有相同键值的同一个孩子.我们必须在Entity Framework交易中维护实体的唯一性.

But in Entity Framework Core, we cannot do the same.The error message suggest that we cannot have same child with the same key value more than once. We have to maintain the uniqueness of entities in the Entity Framework transaction.

这就是为什么我们必须首先确定在更新操作期间哪些现有子级将被删除以及哪些新子级将被添加.这将使实体框架的交易状态唯一.

That's why we have to first identified which existing children are being deleted and which children are being newly added during update operation. This will make Entity framework Transaction state unique.

所以Update方法如下:

So the Update method will be as follows:

public async Task<IActionResult> UpdateProduct([FromRoute] long id, [FromBody] Product product)
{
    if (!ModelState.IsValid)
    {
       return BadRequest(ModelState);
    }

    if (id != product.ProductId)
    {
       return BadRequest();
    }

    Product productToBeUpdated = await _unitOfWork.Repository<Product>().GetEntityList(p => p.ProductId == id).Include(p => p.ProductSpecifications).SingleOrDefaultAsync();
    if (productToBeUpdated == null)
    {
        return NotFound();
    }

    _mapper.Map(product, productToBeUpdated); // If you use AutoMapper than ignore the many-to-many navigation property in the mapping

   List<ProductSpecification> productSpecificationsToBeDeleted = productToBeUpdated.ProductSpecifications.Where(c1 => product.ProductSpecifications.All(c2 => c2.ProductAttributeValueId != c1.ProductAttributeValueId)).ToList();
   foreach (ProductSpecification productSpecification in productSpecificationsToBeDeleted)
   {
                productToBeUpdated.ProductSpecifications.Remove(productSpecification);
   }

   List<ProductSpecification> productSpecificationsToBeAdded = product.ProductSpecifications.Where(c1 => productToBeUpdated.ProductSpecifications.All(c2 => c2.ProductAttributeValueId != c1.ProductAttributeValueId)).ToList();
   foreach (ProductSpecification productSpecification in productSpecificationsToBeAdded)
   {
                productToBeUpdated.ProductSpecifications.Add(productSpecification);
   }

   productToBeUpdated.ModifiedOn = DateTime.UtcNow;
   await _unitOfWork.SaveChangesAsync();
   return Ok(true);
}

这篇关于在Entity Framework核心中更新多对多的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆