如何在EF Core中实现添加相关数据的方法?

How to implement method of add related data in EF core?

如何实现在EF core中添加相关数据的方法?什么对象需要提交给add方法,应该提交什么return?我需要在数据库中添加相关数据,例如:

 {
        "productId": 0,
        "number": "xxx",
        "amount": 5.65,
        "primeCost": 20.33,
        "productTypeId": 0,
        "parameters": [
            {
                "id": 0,
                "name": "Type",
                "value": null
            },
            {
                "id": 3,
                "name": "steel grade",
                "value": "CK45"
            },
            {
                "id": 4,
                "name": "diameter",
                "value": "40"
            }
        ]
    }

这些是我的模型类:

public class Product //: BaseObject
{
    public int Id { get; set; }

    public string Name { get; set; }
    public string Number { get; set; }
    public double Amount { get; set; }
    public double PrimeCost { get; set; }

    [ForeignKey("ProductTypeId")]
    public int  ProductTypeId {  get; set; }
    public virtual ProductType ProductType { get; set; }

    public ICollection<ProductParameter> ProductParameters { get; set; } = new List<ProductParameter>();
}
public class ProductType //: BaseObject
{   
    public int Id { get; set; }

    public string NameType { get; set; }

    public ICollection<Parameter> Parameters { get; set; } = new List<Parameter>();
    public ICollection<Product> Products { get; set; } = new List<Product>();
}
 public class Parameter //: BaseObject
{
    public int Id { get; set; }
    public string Name { get; set; }

    [ForeignKey("ProductTypeId")]
    public int ProductTypeId { get; set; }
    public ProductType ProductType { get; set; }

    public ICollection<ProductParameter> ProductParameters { get; set; } = new List<ProductParameter>();
}
 public class ProductParameter //: BaseObject
{
    public int Id { get; set; }

    public int ProductId { get; set; }
    public virtual Product Product { get; set; }

    public int ParameterId { get; set; }
    public virtual Parameter Parameter { get; set; }


    public string Value { get; set; }

}

这些是我的 DTO 类:

public class ProductDTO
{   

    public int ProductId { get; set; }
    public string Number { get; set; }
    public double Amount { get; set; }
    public double PrimeCostEUR { get; set; }

    public int ProductTypeId { get; set; }
    public string NameType { get; set; }

    public ICollection<ParameterDTO> Parameters { get; set; } = new List<ParameterDTO>();
}
public class ParameterDTO
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Value { get; set; }

}

我在数据库中添加相关数据的方法实现:

public async Task<IEnumerable<ProductDTO>> AddProducts(ProductDTO ProductDTO, 
        List<ParameterDTO> ParameterDTO)
    {
        var EntryProduct = await _context.Products.FindAsync(ProductDTO.ProductId);

        if (EntryProduct == null)
        {

            _context.Products.Add(new Product
            {
                Id = ProductDTO.ProductId,
                Number = ProductDTO.Number,
                Amount = ProductDTO.Amount,
                PrimeCostEUR = ProductDTO.PrimeCostEUR,
            });
            _context.SaveChanges();

            foreach (var i in ParameterDTO)
            {
                var EntryParameter = await _context.Parameters.FindAsync(i.Id);
                if (EntryParameter != null)
                {
                    _context.ProductParameters.Add(
                      new ProductParameter
                      {
                          ProductId = ProductDTO.ProductId,
                          ParameterId = i.Id,
                          Value = i.Value
                      });
                    _context.SaveChanges();
                }
            }

        }

        return ProductDTO;
    }

我收到以下异常编译器错误:

Severity Code Description Project File Line Suppression State Error CS0266 Cannot implicitly convert type 'GdmStore.DTO.ProductDTO' to 'System.Collections.Generic.IEnumerable'. An explicit conversion exists (are you missing a cast?)

您的方法期望 return 一个 IEnumerable 但您 return 只是传入的单个产品 DTO。

签名应该是:

public async Task<ProductDTO> AddProducts(ProductDTO ProductDTO, List<ParameterDTO> ParameterDTO)

鉴于 ProductDTO 具有 ParameterDTO 的集合,是否还需要第二个参数? (貌似会传两次参数)

根据你的实体定义,我发现了一些问题:

[ForeignKey("ProductTypeId")]
public int  ProductTypeId {  get; set; }
public virtual ProductType ProductType { get; set; }

应该是

[ForeignKey("ProductType")] // FK to the reference property.
public int  ProductTypeId {  get; set; }
public virtual ProductType ProductType { get; set; }

所有导航属性(例如集合和产品类型)都应声明为虚拟的,否则您将获得不一致的行为。如果需要,声明为虚拟的将可以访问延迟加载,其他将保留为#null。

Product 和 Parameter 都不应该引用 ProductType,据我所知,它可能应该只引用 Product 以避免非规范化问题。 (设置了不同 ProductType 的参数的产品。)

在处理导航属性时,我建议从实体中删除 FK 属性 并使用映射 (EF6)/阴影属性。 (EF 核心)

例如:

 public class ProductParameter 
{
    public int Id { get; set; }

    public virtual Product Product { get; set; } // No ProductId/ParameterId
    public virtual Parameter Parameter { get; set; }

    public string Value { get; set; }
}

modelBuilder.Entity<Product>()
    .HasMany(x => x.ProductParameters)
    .WithOne(x => x.Product)
    .HasForeignKey("ProductId"); // Sets up a shadow property.

映射 FK 的问题在于有 2 个真实来源供参考。 ProductParameter.ProductId 对比 ProductParameter.Product.Id。通常这些将指向相同的值,但代码可能依赖于一条路径而不是另一条路径,如果只更改一条路径而不更改另一条路径,则会导致一致性错误。

谨慎使用异步操作。如果您通过 ID 或任何其他相对快速的操作拉回单个记录,请不要使用异步,因为注册延续会产生性能成本。 (更快地进行同步调用)异步适用于预计需要一段时间的操作。 (即超过一秒)

最后,代码可能有效,但它没有很好地利用 EF,单独设置所有这些实体,并且您通常不希望多次调用 SaveChanges 以确保数据一起提交或 none 如果有问题的话。

   var EntryProduct = _context.Products.Find(ProductDTO.ProductId);

    if (EntryProduct != null)
        return ProductDTO;

    var product = new Product
    {
        Id = ProductDTO.ProductId,
        Number = ProductDTO.Number,
        Amount = ProductDTO.Amount,
        PrimeCostEUR = ProductDTO.PrimeCostEUR,
    };

    var parameterIds = ParameterDTO.Select(x => x.Id).ToList();
    var parametersToAdd = context.Parameters
        .Where(x => parameterIds.Contains(x.ParameterId))
        .Select(x => new ProductParameter
        {
            Product = product,
            Parameter = x
        }).ToList();

    product.ProductParameters.AddRange(parametersToAdd);
    await _context.SaveChangesAsync();

    return ProductDTO;

我不建议为 DbContext (_context) 使用模块级变量,因为上下文应该是短暂的,以帮助避免一个工作流打算保存而其他代码可能不会保存的潜在问题。如果它由 IoC 容器注入并限定为与请求匹配的生命周期,那么这应该不会导致任何问题。请注意上下文打开时间超过所需时间。