我有一个端点,它在数据库中创建一个新实体。在端点中,我需要更新数据库,然后创建一个新的
Audit
条目,将其添加到 Audits
表中以记录此创建的发生。
流程如下所示:
Audit
实体Audit
添加到 Order.AuditEvents
Audits
表但是,我的问题是我需要新的已保存实体的
Id
来创建 Audit
条目。当我尝试如下操作时,我得到一个 Internal Server Error
:
// Turn `requestModel` into a `ProductEntity`
var product = _mapper.Map<Product>(requestModel);
order.AddProduct(product);
// this method creates an `AuditEvent` and saves it onto a collection of
// `Order.AuditEvents`
// the method needs the `product.Id` to log the event in the database
order.CreateAuditEvent(product);
// Save both the `Product` added to the `Order` as well as the `AuditEvents` on the `Order`
await _orderRepository.UpdateOrderAsync(order);
由于这个失败,我不得不做如下的事情:
var product = _mapper.Map<Product>(requestModel);
order.AddProduct(product);
// updated the database to include the newly added product
await _orderRepository.UpdateOrderAsync(order);
// get the newly created `Product` and pass it to `CreateAuditEvent`
var createdProduct= await productRepository.GetByIdAsync(order.Product.Id);
order.CreateAuditEvent(createdProduct);
// update the database AGAIN to save the `AuditEvents`
await _orderRepository.UpdateOrderAsync(order);
有没有办法让我不用打电话两次
UpdateOrderAsync(order)
就能做到这一点?
public Task UpdateOrderAsync(Order order)
{
// nothing currently happens here with `order`
return Context.SaveChangesAsync();
}
在 EF 中,参考文献就是一切。第一个问题是传入的产品实际上代表什么?通常,在创建订单之类的内容时,“产品”不会被视为新产品行,而是与现有产品行的关联。
代码如下:
// Turn `requestModel` into a `ProductEntity`
var product = _mapper.Map<Product>(requestModel);
... 如果产品旨在引用数据库中的现有行,则不应使用。从 DbContext 加载它:
var product = _context.Products.Single(x => x.Id == requestModel.ProductId);
检查 order.AddProduct() 是否没有执行使用对 Product 的不同引用而不是您初始化的引用的操作。如果它确实转到 DbContext 或检查重复项(如果这是编辑订单还是创建订单)等,那么您需要返回实际使用的引用。
通用存储库(OrderRepository、ProductRepository 等)不是您的朋友。他们使用 EF 会让生活变得困难,导致隐藏潜在问题并构建低效代码。 EF 已经以
DbSet<T>
的形式为您提供了通用存储库,老实说,只需使用它即可。
Mapper.Map
仅应在您想要插入新实体而不是引用现有实体时使用。如果您确实打算使用此订单创建新的产品行,则您的 CreateAuditEvent 方法可能存在问题。 EF 在保存时管理引用及其 FK 关联,因此,如果您正在创建尝试仅使用 FK 属性的审核:
var audit = new Audit
{
// .. populate values from this order..
ProductId = product.Id
}
AuditEvents.Add(audit);
...那么这行不通。新的产品 ID 在保存之前不会被分配,并且当它尝试保存产品 ID 0 时,您将遇到索引违规。相反,您需要为审核提供导航属性,以指向添加到的产品的引用订单:
var audit = new Audit
{
// .. populate values from this order..
Product = product // Reference the related, now tracked entity, not the PK
}
AuditEvents.Add(audit);
这样,当
DbContext
将订单、产品和审核事件保存到数据库时,它就知道这些实体及其底层 FK 是如何关联的,并相应地填充它们。