在本文中,我们将通过演练一个示例来探讨如何在 EF Core 中使用多属性值对象。本文不仅将说明值对象的力量,还将展示它们如何导致更具表现力的领域模型。
考虑电子商务域中的值对象。在此上下文中,地址不仅仅是一个字符串,它还包含各种元素,例如街道、城市、州和邮政编码。将其作为定义订单需要运送到哪里的原子信息进行处理至关重要。以下是我们对此的建模方式:Address
public class Order
{
public int Id { get; set; }
public Address ShippingAddress { get; set; }
// ... remaining properties for an Order
}
public class Address
{
public string Street { get; private set; }
public string City { get; private set; }
public string State { get; private set; }
public string PostalCode { get; private set; }
public Address(string street, string city, string state, string postalCode)
{
Street = street;
City = city;
State = state;
PostalCode = postalCode;
}
// Additional methods to handle address-related operations could go here
}
在上面的代码片段中,实体包含一个 value 对象。该类包含多个属性,每个属性表示地址的一部分,以及一个构造函数,用于确保创建对象时包含其所有必需的部分。OrderAddressAddressAddress
我们将了解如何配置 EF Core 以将此值对象映射到数据库架构:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Order>(order =>
{
order.ToTable("Orders").HasKey(o => o.Id);
order.OwnsOne(o => o.ShippingAddress, a =>
{
a.Property(p => p.Street).HasColumnName("ShippingStreet");
a.Property(p => p.City).HasColumnName("ShippingCity");
a.Property(p => p.State).HasColumnName("ShippingState");
a.Property(p => p.PostalCode).HasColumnName("ShippingPostalCode");
});
});
base.OnModelCreating(modelBuilder);
}
在该方法中,我们指示 EF Core 使用该方法指示实体具有值对象。然后,我们将对象的每个属性映射到表中的一列。OnModelCreatingOrderAddressOwnsOneAddressOrders
通过将地址视为值对象,我们将与地址相关的所有逻辑集中在一个地方。这使得代码不易出错,并确保地址的概念在整个系统中一致地表示。此外,值对象的使用可以简化更改或重构域概念的过程,因为这些更改已本地化到值对象本身。
在 Entity Framework Core 中,OwnsOne 方法用于定义实体与其拥有类型之间的一对一关系,其中拥有的实体在概念上是所有者的一部分,而不是单独的独立实体。配置此类关系时,默认情况下,EF Core 会将拥有类型的属性映射到数据库中所有者的表,这种技术称为表拆分。
在我们的示例中,实体拥有一个值对象。这意味着 的属性将包含在“Orders”表中,默认列名以导航属性名称为前缀,从而产生类似“ShippingAddress_Street”和“ShippingAddress_City”的列名。OrderAddressAddress
但是,我们使用该方法覆盖了这些默认列名,以直接在“Orders”表中具有更简洁的名称,例如“ShippingStreet”和“ShippingCity”,从而根据我们的域模型简化数据库架构。HasColumnName