В EF Core 8 узаконили Value Object 🤩
Одно из тех обновлений, которое не следует пропускать😱
Например, есть следующая сущность заказа
Кажется, что уже существует семантика работы с типами значениями, ведь никто не отменял атрибут [Owned].
Однако, при попытке сохранить сущность, которая использует один инстанс на несколько полей возникнет ошибка:
Кажется, что всё будет хорошо, но получим исключение
«
При этом он может быть иммутабельным!
Соответственно, такое обновление сущности:
Сгенерирует следующий SQL:
То есть, это собственный тип на стероидах💉
Одно из тех обновлений, которое не следует пропускать
Например, есть следующая сущность заказа
Order
вместе с сущностью клиента Customer
:public class Customer
{
public int Id { get; set; }
public required string Name { get; set; }
public required Address Address { get; set; }
public List<Order> Orders { get; } = new();
}
public class Order
{
public int Id { get; set; }
public required string Contents { get; set; }
public required Address ShippingAddress { get; set; }
public required Address BillingAddress { get; set; }
public Customer Customer { get; set; } = null!;
}
Кажется, что уже существует семантика работы с типами значениями, ведь никто не отменял атрибут [Owned].
Однако, при попытке сохранить сущность, которая использует один инстанс на несколько полей возникнет ошибка:
var address = GetAddress();
customer.Orders.Add(
new Order { Contents = "Tesco Tasty Treats", BillingAddress = address, ShippingAddress = address, });
await context.SaveChangesAsync();
Кажется, что всё будет хорошо, но получим исключение
InvalidOperationException
с сообщением«
Cannot save instance of 'Order.ShippingAddress#Address' because it is an owned entity without any reference to its owner. Owned entities can only be saved as part of an aggregate also including the owner entity.
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.PrepareToSave()»
Теперь, достаточно разметить тип данных атрибутом [ComplexType]
.При этом он может быть иммутабельным!
[ComplexType]
public record Address(string Line1, string? Line2, string City, string Country, string PostCode);
Соответственно, такое обновление сущности:
customer.Address = customer.Address with { Line1 = "Peacock Lodge" };
await context.SaveChangesAsync();
Сгенерирует следующий SQL:
UPDATE [Customers] SET [Address_Line1] = @p0
OUTPUT 1
WHERE [Id] = @p1;
То есть, это собственный тип на стероидах