如何实现在 EF core
中添加相关数据的方法?哪些对象需要提交给add方法,它应该返回什么?我需要在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"
}
]
}
这些是我的模型类:
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;
}
我在编译器错误中遇到以下异常:
严重性代码描述项目文件行抑制状态 错误CS0266无法将类型'GdmStore.DTO.ProductDTO'隐式转换为'System.Collections.Generic.IEnumerable'。存在显式转换(您是否缺少演员表?)
答案 0 :(得分:1)
您的方法期望返回一个IEnumerable,但是您仅返回传入的单个产品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; }
所有导航属性(例如集合和产品类型)都应声明为虚拟,否则您将获得不一致的行为。声明为virtual的虚拟机可以根据需要访问延迟加载,其他虚拟机将保留为#null。
Product和Parameter都不应引用ProductType,据我所知,它应该只在Product上,以避免非规范化问题。 (具有设置了不同ProductTypes参数的产品。)
在处理导航属性时,建议从实体中删除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的问题在于,有两个真相可供参考。 ProductParameter.ProductId与ProductParameter.Product.Id。通常,它们将指向相同的值,但是代码可能依赖于一条路径而不是另一条路径,并且如果在不更改另一条路径的情况下进行更改会导致一致性错误。
谨慎使用异步操作。如果您要通过ID或任何其他相对快速的操作来回退单个记录,请不要使用异步,因为注册连续性会降低性能。 (这样做只是为了进行同步调用,所以速度更快)Async适用于需要花费一些时间的操作。 (即超过一秒钟)
最后,代码可能会起作用,但是不能很好地利用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;
我不建议为DbContext(_context)使用模块级别的变量,因为该上下文应该是短命的,以帮助避免一个工作流打算保存的潜在问题,而其他代码则可能不保存。如果它是由IoC容器注入的,并且作用域范围与请求相匹配,则不会造成任何问题。只是要小心打开上下文的时间比需要的时间长。