gpt4 book ai didi

c# - 在数据库中更新时未保存嵌套的拥有类型

转载 作者:太空狗 更新时间:2023-10-30 01:29:01 25 4
gpt4 key购买 nike

我有以下类(class):

顺序:

public class Order {
private Order()
{
//some code to initialise object
}
//more properties
public Prepayment Prepayment { get; private set; }
//more methods and properties
}

预付款:

public class Prepayment:ValueObject<Prepayment>
{
private Prepayment()
{
}

public Money AmountPrepaid { get; private set; }
public bool HasPrepaymentBeenTaken => AmountPrepaid.Amount > 0;
}

金钱:

public class Money {
private Money()
{
}
private Money(decimal amount)
: this()
{
Amount = amount;
}
public decimal Amount { get; private set; }
}

然后我按以下方式将 Order 类映射到数据库:

modelBuilder.Entity<Order>()
.OwnsOne(x => x.Prepayment,
prepayment =>
{
prepayment.OwnsOne(x => x.AmountPrepaid,
amountPrepaid =>
{
amountPrepaid.Property(x => x.Amount)
.HasColumnName("PrepaymentAmount")
.HasColumnType("decimal(7,2)");
});
});

SaveChanges 的存储库代码:

public async Task<int> SaveAsync(Order order)
{
if (order.Id == 0)
{
await _context.AddAsync(order);
}
else
{
_context.Update(order);
}

return await _context.SaveChangesAsync();
}

请理解,我从代码中删除了所有不重要的属性,以使示例更加清晰。

以上代码适用于 INSERT 场景,其中 Prepayment -> Money -> Amount 已正确保存到数据库中。 UPDATE 虽然似乎没有反射(reflect)在数据库中。

请注意,我在那个模型中有很多自有类型,而且它们都运行良好。据我所知,唯一的区别是 Prepayment 属性有另一个嵌套的 Owned 类型 - Money

Order 类对象传递到存储库,首先从数据库中提取,然后更改应用于该实例,最后保存回数据库。示例中未提及的其他属性(如 Customer)也是 OwnedType 并且 UPDATE 按预期工作。

以防万一 - 用于在更新之前检索对象的代码:

public async Task<Order> GetOrderByIdAsync(int orderId)
{
var result = (from order in _context.Orders
where order.Id == orderId
select order).Include(x => x.OrderLines);

return await result.FirstOrDefaultAsync();
}

我使用的 Entity Framework Core 的确切版本是:2.2.0

任何帮助将不胜感激。谢谢。

编辑:

更新数据的代码如下所示:

public async Task<int> Handle(EditOrderCommand request, CancellationToken cancellationToken)
{
var order = await _orderRepository.GetOrderByIdAsync(request.Id);

var customer = new Customer(
request.FirstName,
request.LastName,
request.TelephoneNumber);

var prepayment = new Prepayment(
Money.SomeMoney(
request.PrepaymentAmount
)
);

order.ApplyChanges(
request.UserId,
request.AdditionalInformation,
collectionDate,
customer,
prepayment);

await _orderRepository.SaveAsync(order);

return order.Id;
}

以及设置预付款的 ApplyChanges 方法的一部分:

private void SetPrepayment(Prepayment prepayment)
{
Prepayment = prepayment ?? throw new ArgumentNullException(nameof(prepayment));
}

最佳答案

该问题与嵌套拥有的实体类型有一些共同之处。

但普遍的问题是您使用自有实体类型的方式违反了 EF Core 规则,因此行为未定义 - 有时可能有效,有时无效,有时甚至抛出异常等。

拥有的实体类型不能用于实现值对象,因为即使它们是“拥有的”,根据 EF Core 术语,它们仍然是“实体”(具有隐藏的影子 PK),因此它们通过引用进行跟踪 并遵循与其他实体引用相同的规则 - 最重要的是,每个定义导航 PK 必须只有一个对象实例。

简而言之,它们应该被分配一次,然后通过它们的原始属性进行变异。而您正在做的是用不可变对象(immutable对象)改变引用。

由于前面提到的 EF Core 规则违规,很难给你好的建议。唯一可行的解​​决方法是确保上下文不跟踪所有原始对象引用。

例如,如果 GetOrderByIdAsync 实现使用 AsNoTracking 查询,因此 orderorder.Prepaymentorder.Prepayment.AmountPrepaid 实例由 _context 跟踪,然后 _context.Update(order) 将起作用。

如果您在调用 ApplyChanges 之前手动分离它们,它也会起作用(需要访问数据库上下文):

_context.Entry(order).State = EntityState.Detached;
_context.Entry(order.Prepayment).State = EntityState.Detached;
_context.Entry(order.Prepayment.AmountPrepaid).State = EntityState.Detached;

order.ApplyChanges(...);

await _orderRepository.SaveAsync(order); // _context.Update(order);

看起来 AsNoTracking 是更好的选择。您可以通过在数据库上下文构造函数中设置 ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; 并在需要时使用 AsTracking() 使其成为所有查询的默认值。

关于c# - 在数据库中更新时未保存嵌套的拥有类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54856563/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com