EF Core Value Object Owned by many

1.2k Views Asked by At

I use a value object to represent the Price

public record Price(decimal Amount, string Currency);

Then I have two entities with a price

public class Item 
{
    public Price { get; private set; }
    // rest of properties
}

public class OrderPosition 
{
    public Price { get; private set; }
    // rest
}

In the DB I would like to have these two tables

Items
| Id | Price_Amount | Price_Currency |


OrderPositions
| Id | Price_Amount | Price_Currency |

To achieve this i configured the Price to be an owned type of the item aswell as the order position:

public class ItemConfiguration : IEntityTypeConfiguration<Item>
{
    public void Configure(EntityTypeBuilder<Item> builder)
    {
        builder.OwnsOne(i => i.Price);
    }
}

public class ItemConfiguration : IEntityTypeConfiguration<OrderPosition>
{
    public void Configure(EntityTypeBuilder<OrderPosition> builder)
    {
        builder.OwnsOne(op => op.Price);
    }
}

This works all fine but EF gives me a warning when I have the same price on an item aswell as on the order position:

[09:47:59 WRN] The same entity is being tracked as different entity types 'Item.Price#Price' and 'OrderPosition.Price#Price' with defining navigations. If a property value changes, it will result in two store changes, which might not be the desired outcome.

And I understand the exception fully, it is even documented as a by design restriction: https://learn.microsoft.com/en-us/ef/core/modeling/owned-entities#by-design-restrictions

Instances of owned entity types cannot be shared by multiple owners (this is a well-known scenario for value objects that cannot be implemented using owned entity types).

But how do you solve this issue? Do I need to make a derived class for ItemPrice and OrderPositionPrice with implicit conversions to each other? This would work but I think this is not the best solution.

1

There are 1 best solutions below

0
On

With the mentioned EF Core constraint, it is important not to pass the same value but a copy of it.

public record Price(decimal Amount, string Currency)
{
    public Price Copy() => new(this);
}

// used
var price = new Price(42.0, "USD");
item.Price = price.Copy();
orderPosition.Price = price.Copy();