拥有的类型集合在 EF Core 中永远不会更新

Owned types collection is never updated in EF Core

我有一个这样定义的聚合:

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 方法的原因方法体。问题是我用一条异常消息说 catch 语句:

Database operation expected to affect 1 row(s) but actually affected 0 row(s). Data may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=527962 for information on understanding and handling optimistic concurrency exceptions."}

问题是我不会在其他任何地方更新相同的产品,这是唯一接触它的地方。我也使用 AsNoTracking 作为默认值。如果我用 _context.Update(prop); 注释掉该行,则不会引发异常,但不会更新价格。此外,如果我不更新价格集合而是更新 Something 属性,一切都会顺利进行。什么。这。见鬼。

我正在为未来迷路的旅行者发布这个答案,尽管我很乐意听到比我更了解 EF Core 的人的解释。我也认为我理解这种行为的原因,但我不确定。当我在 Price 对象上添加一个 Id 字段时,我的代码开始正常运行。我怀疑如果没有明确可见的 id 属性,任何附加或更新都无法使 EF 看到该对象。不过,我会欢迎关于它的文档部分...

Collections of owned types 的 EF Core 文档明确指出您必须定义拥有的实体 PK(与 OwnsOne 相反,影子 FK 通常用作 PK)。

因此您需要定义其自己的 PK(就像您所做的 Id)或复合 PK - 例如,如果 Price.Type 在所有者内部是唯一的,那么您可以使用类似

pp.HasKey("LocalId", "Type");

并避免额外的 Id 列。