【问题标题】:Updating many to many in Entity Framework core在 Entity Framework 核心中更新多对多
【发布时间】:2023-04-07 02:27:01
【问题描述】:

以下是相关类及其配置:

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);
}

他们都抛出了以下异常:

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

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

【问题讨论】:

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


    【解决方案1】:

    问题终于确定了!在 Entity Framework 6.x 中,在多对多导航属性更新过程中,我们可以清除/删除现有的子列表,然后将新的子列表添加到父级,最后更新父级和父级使用新的子列表更新。

    但在 Entity Framework Core 中,我们不能这样做。错误消息表明我们不能多次拥有具有相同键值的相同子项。我们必须在 Entity Framework 事务中保持实体的唯一性。

    这就是为什么我们必须首先确定哪些现有的孩子正在被删除,哪些孩子是在更新操作期间新添加的。这将使实体框架事务状态独一无二。

    所以Update方法如下:

    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);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-11
      • 1970-01-01
      • 1970-01-01
      • 2018-11-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多