我有一个这样定义的聚合:
public class Product {
public int LocalId { get; private set; }
public Something Something { get; private set; }
public ICollection<Price> Prices { get; private set; }
}
public class Something {
public string Name { get; set; }
}
public class Price {
public int Type { get; set; }
public decimal Value { get; set; }
}
以下是定义为这样的模式:
private void DefineProduct(ModelBuilder builder) =>
builder
.Entity<Product>(builder =>
{
builder.HasKey(p => p.LocalId);
builder
.OwnsOne(p => p.Something, smth =>
{
smth.ToTable("somethings");
})
.OwnsMany(p => p.Prices, pp =>
{
pp.ToTable("prices");
});
});
当价格改变时,我会执行以下操作(此处未包含产品方法):
Prices.First(p => p.Type == type).Value = newValue;
然后我尝试这样保存产品:
public async Task UpdateProperties(Product product, IEnumerable<object> props)
{
_context.Attach(product);
_context.Update(product);
foreach (var prop in props)
{
_context.Update(prop);
}
try
{
await _context.SaveChangesAsync();
}
catch (Exception ex)
{
Console.WriteLine("Who the hell allowed such a bug to go into a production release?");
}
}
现在我应该提到,这个产品是从一个最初的查询结果中导入的(通过
AsNoTracking()
调用),这就是为什么我在方法体的第一行调用 Attach
方法。问题是,我遇到了一个异常,提示如下:
预期影响1行记录,但实际上影响0行记录的数据库操作。数据可能已经被修改或删除,因为实体已经被加载。请参阅http://go.microsoft.com/fwlink/?LinkId=527962,了解乐观并发异常的理解和处理信息。
问题是我没有在其他任何位置更新同一产品,那是唯一接触它的地方。另外,我使用 AsNoTracking
作为默认设置。如果我将带有 _context.Update(prop);
的行注释掉,则不会引发异常,但价格也不会更新。另外,如果我不更新价格集合而是更新 Something
属性,则一切都很顺利。简直是闹鬼。
_context.Attach(product).State = EntityState.Modified;
用于通知 EF 应该重新跟踪它。(对于非核心的 EF:_context.Entry(product).State = EntityState.Modified;
) - nilsK