首页 > 解决方案 > Owned types collection is never updated in EF Core

问题描述

I have an aggregate defined like this:

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; }
}

And a schema defined like this:

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");
                });
        });

When a price change is requested, I do this (inside the product method not included here for brevity):

Prices.First(p => p.Type == type).Value = newValue;

And then I try to save the product like this:

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?");
    }
}

Now I should mention that the product comes in from an initial query whose results were not tracked (via AsNoTracking() call), that's why I'm calling the Attach method in the first line of the method body. The problem is that I'm hitting that catch statement with an exception message saying:

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."}

The thing is that I'm not updating the same product anywhere else and that's the only place touching it. Also I use AsNoTracking as the default. If I comment out the line with _context.Update(prop);, then there's no exception raised, but the price is not being updated. Also, if I don't update that prices collection but the Something property, everything goes well. What. The. Hell.

标签: c#entity-framework-coreowned-types

解决方案


拥有类型集合的EF Core 文档明确指出您必须定义拥有的实体 PK(而不是OwnsOne影子 FK 通常用作 PK)。

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

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

并避免额外的Id列。


推荐阅读