Owned types collection is never updated in EF Core

c# entity-framework-core owned-types

Question

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.

1
1
2/19/2020 1:50:27 PM

Accepted Answer

EF Core documentation for Collections of owned types explicitly states that you have to define the owned entity PK (as opposed to OwnsOne where the shadow FK is normally used as PK).

Hence you need to either define its own PK (like Id you did), or composite PK - for instance, if Price.Type is unique inside the owner, then you can use something like

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

and avoid the additional Id column.

0
2/19/2020 8:48:16 PM

Popular Answer

I'm posting this answer for the future lost travelers out there, although I would gladly hear an explanation from someone who knows more than me about EF Core. I also think I understand the reason for that behavior, but I'm not sure. My code started behaving properly when I added an Id field on the Price object. My suspicion is that without an explicitly visible id property, no amount of attaching or updating would make EF see and that object. I would welcome a documentation section about it though...



Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow