在将实体框架中新创建的实体保存到数据库之前获取其 Id

问题描述 投票:0回答:1

我有一个端点,它在数据库中创建一个新实体。在端点中,我需要更新数据库,然后创建一个新的

Audit
条目,将其添加到
Audits
表中以记录此创建的发生。

流程如下所示:

  • 获取请求模型并将其映射到我的 DTO
  • 从数据库获取父实体
  • 将新的 DTO 添加到父实体
  • 使用 DTO 创建新的
    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();
}
c# .net entity-framework entity-framework-core
1个回答
0
投票

在 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 是如何关联的,并相应地填充它们。

© www.soinside.com 2019 - 2024. All rights reserved.