简体   繁体   English

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

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

How to implement a method of adding related data in EF core ? 如何在EF core实现添加相关数据的方法? What objects need to be submitted to the add method and what should it return? 哪些对象需要提交给add方法,它应该返回什么? I need of related data add in DB, example: 我需要在DB中添加相关数据,例如:

 {
        "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"
            }
        ]
    }

These are my model classes: 这些是我的模型类:

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

}

These are my DTO classes: 这些是我的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; }

}

My implementation of method of add related data in DB: 我在数据库中添加相关数据的方法的实现:

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

I am getting the following exception a compiler error: 我收到以下异常的编译器错误:

Severity Code Description Project File Line Suppression State Error CS0266 Cannot implicitly convert type 'GdmStore.DTO.ProductDTO' to 'System.Collections.Generic.IEnumerable'. 严重性代码说明项目文件行抑制状态错误CS0266无法将类型“ GdmStore.DTO.ProductDTO”隐式转换为“ System.Collections.Generic.IEnumerable”。 An explicit conversion exists (are you missing a cast?) 存在显式转换(您是否缺少演员表?)

You method is expecting to return an IEnumerable but you are returning just the single Product DTO that was passed in. 您的方法期望返回IEnumerable,但是您仅返回传入的单个Product DTO。

The signature should be: 签名应为:

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

Given ProductDTO has a collection of ParameterDTO, is the second argument still needed? 给定ProductDTO有ParameterDTO的集合,是否还需要第二个参数? (Looks like it would send the parameters twice) (看起来它会发送两次参数)

With your entity definitions I see a few problems: 通过您的实体定义,我看到了一些问题:

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

should be 应该

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

All navigation properties such as the collections, and producttype should be declared as virtual otherwise you'll get inconsistent behaviour. 所有导航属性(例如集合和产品类型)都应声明为虚拟,否则您将获得不一致的行为。 Ones declared virtual will have access to lazy-loading if needed, the others will be left as #null. 声明为virtual的虚拟机可以在需要时访问延迟加载,其他虚拟机将保留为#null。

Both Product and Parameter should not have references to ProductType, from what I can see it probably should just be on Product to avoid denormalization issues. Product和Parameter都不应引用ProductType,据我所知,它可能应该只在Product上,以避免非规范化问题。 (Product with Parameters with different ProductTypes set.) (具有带有不同ProductTypes设置的参数的产品。)

When dealing with navigation properties I recommend removing the FK property from the entity and using mapping (EF6) / shadow properties. 处理导航属性时,建议从实体中删除FK属性,并使用映射(EF6)/阴影属性。 (EF Core) (EF核心)

For example: 例如:

 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.

The issue with mapping FKs is that then there are 2 sources of truth for the reference. 映射FK的问题在于,有两个真相可供参考。 ProductParameter.ProductId vs. ProductParameter.Product.Id. ProductParameter.ProductId与ProductParameter.Product.Id。 Normally these will point to the same value, but code may be dependent on one path vs. the other and lead to consistency bugs if one is changed without the other. 通常,它们将指向相同的值,但是代码可能依赖于一条路径而不是另一条路径,并且如果更改一条路径而未更改另一条路径,则会导致一致性错误。

Use async operations sparingly. 谨慎使用异步操作。 If you're pulling back a single record by ID, or any other relatively fast operation, don't use async as there is a performance cost with registering the continuation. 如果您要通过ID或任何其他相对快速的操作来回退单个记录,请不要使用异步,因为注册连续性会降低性能。 (Faster to do just a synchronous call) Async is geared towards operations that are expected to take a while. (这样做只是为了进行同步调用,所以速度更快)Async适用于需要花费一些时间的操作。 (Ie more than a second) (即超过一秒钟)

Lastly, the code may work, but it's not leveraging EF very well, setting all of these entities individually, and you generally do not want to call SaveChanges multiple times to ensure that the data is committed all together or none at all if there is an issue. 最后,代码可能会起作用,但是不能很好地利用EF,分别设置所有这些实体,并且您通常不希望多次调用SaveChanges以确保将所有数据一起提交,或者如果有的话根本不提交。问题。

   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;

I don't recommend using a module level variable for the DbContext (_context) as the context should be short-lived to help avoid potential issues where one workflow intends to save, while other code may not. 我不建议为DbContext(_context)使用模块级别的变量,因为该上下文应该是短暂的,以帮助避免潜在的问题,其中一个工作流打算保存,而其他代码则可能不保存。 If it's injected by an IoC container and scoped to a lifetime matching the request then that shouldn't cause any issues. 如果它是由IoC容器注入的,并且作用域范围与请求相匹配,则不会造成任何问题。 Just be cautious of contexts being left open longer than needed. 只是要小心打开上下文的时间比需要的时间长。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM