擁有的實體類型Owned Entity Types

注意

這項功能是在 EF Core 2.0 的新功能。This feature is new in EF Core 2.0.

EF Core 可讓您可以永遠只會出現在導覽屬性的其他實體類型的模型實體類型。EF Core allows you to model entity types that can only ever appear on navigation properties of other entity types. 這些稱為_擁有的實體類型_。These are called owned entity types. 包含擁有的實體類型的實體是其_擁有者_。The entity containing an owned entity type is its owner.

明確的設定Explicit configuration

擁有的實體類型永遠不會包含 EF Core 的模型中的慣例。Owned entity types are never included by EF Core in the model by convention. 您可以使用OwnsOne方法中的OnModelCreating或加上註解具有型別OwnedAttribute(EF Core 2.1 的新) 來設定為擁有的型別類型。You can use the OwnsOne method in OnModelCreating or annotate the type with OwnedAttribute (new in EF Core 2.1) to configure the type as an owned type.

在此範例中,StreetAddress 是任何身分識別屬性的類型。In this example, StreetAddress is a type with no identity property. 它用做訂單類型的屬性,指定特定訂單的送貨地址。It is used as a property of the Order type to specify the shipping address for a particular order. OnModelCreating,我們使用OwnsOne方法,以指定 ShippingAddress 屬性是擁有類型的實體順序。In OnModelCreating, we use the OwnsOne method to specify that the ShippingAddress property is an Owned Entity of the Order type.

public class StreetAddress
{
    public string Street { get; set; }
    public string City { get; set; }
}

public class Order
{
    public int Id { get; set; }
    public StreetAddress ShippingAddress { get; set; }
}

// OnModelCreating
modelBuilder.Entity<Order>().OwnsOne(p => p.ShippingAddress);

如果 [ShippingAddress] 屬性是私用在訂單類型,您可以使用的字串版本OwnsOne方法:If the ShippingAddress property is private in the Order type, you can use the string version of the OwnsOne method:

modelBuilder.Entity<Order>().OwnsOne(typeof(StreetAddress), "ShippingAddress");

在此範例中,我們會使用OwnedAttribute能夠達成此相同的目標:In this example, we use the OwnedAttribute to achieve the same goal:

[Owned]
public class StreetAddress
{
    public string Street { get; set; }
    public string City { get; set; }
}

public class Order
{
    public int Id { get; set; }
    public StreetAddress ShippingAddress { get; set; }
}

隱含索引鍵Implicit keys

在 EF Core 2.0 和 2.1 中,參考導覽屬性可以指向擁有的型別。In EF Core 2.0 and 2.1, only reference navigation properties can point to owned types. 不支援擁有類型的集合。Collections of owned types are not supported. 這些參考擁有的類型與擁有者一定有一對一的關係,因此他們不需要自己的索引鍵值。These reference owned types always have a one-to-one relationship with the owner, therefore they don't need their own key values. 在上述範例中,StreetAddress 類型不必定義索引鍵的屬性。In the previous example, the StreetAddress type does not need to define a key property.

若要了解如何 EF Core 會追蹤這些物件,最好考慮主索引鍵會建立為陰影屬性擁有的類型。In order to understand how EF Core tracks these objects, it is useful to think that a primary key is created as a shadow property for the owned type. 擁有類型的執行個體的索引鍵的值將會擁有者執行個體的索引鍵的值相同。The value of the key of an instance of the owned type will be the same as the value of the key of the owner instance.

擁有的類型與資料表分割的對應Mapping owned types with table splitting

使用關聯式資料庫時,依照慣例,擁有的類型會對應到相同資料表的擁有者。When using relational databases, by convention owned types are mapped to the same table as the owner. 這需要在兩個資料表分割: 某些資料行都會用來儲存資料的擁有者,以及將擁有的實體的資料用於某些資料行。This requires splitting the table in two: some columns will be used to store the data of the owner, and some columns will be used to store data of the owned entity. 這是一項稱為資料表分割的一般功能。This is a common feature known as table splitting.

提示

擁有儲存資料表分割的類型可以是非常類似使用複雜類型中使用 EF6。Owned types stored with table splitting can be used very similarly to how complex types are used in EF6.

依照慣例,EF Core 會遵循模式擁有的實體類型的屬性名稱的資料庫資料行_EntityProperty_OwnedEntityProperty_。By convention, EF Core will name the database columns for the properties of the owned entity type following the pattern EntityProperty_OwnedEntityProperty. 因此 StreetAddress 屬性會出現在 「 訂單 」 資料表 ShippingAddress_Street 和 ShippingAddress_City 的名稱。Therefore the StreetAddress properties will appear in the Orders table with the names ShippingAddress_Street and ShippingAddress_City.

您可以將附加HasColumnName方法來重新命名這些資料行。You can append the HasColumnName method to rename those columns. StreetAddress 所在的公用屬性的情況下,可能會對應In the case where StreetAddress is a public property, the mappings would be

modelBuilder.Entity<Order>().OwnsOne(
    o => o.ShippingAddress,
    sa =>
        {
            sa.Property(p=>p.Street).HasColumnName("ShipsToStreet");
            sa.Property(p=>p.City).HasColumnName("ShipsToCity");
        });

共用相同的.NET 型別,在多個擁有的類型Sharing the same .NET type among multiple owned types

擁有的實體類型可以是相同的.NET 型別,做為另一個擁有的實體類型,因此.NET 型別可能不足以識別擁有的類型。An owned entity type can be of the same .NET type as another owned entity type, therefore the .NET type may not be enough to identify an owned type.

在這些情況下,擁有者從指向擁有的實體屬性會變成_定義導覽_擁有的實體型別。In those cases, the property pointing from the owner to the owned entity becomes the defining navigation of the owned entity type. EF Core 的觀點而言,定義的導覽是.NET 型別與型別之身分識別的一部分。From the perspective of EF Core, the defining navigation is part of the type's identity alongside the .NET type.

例如,在下列類別中,ShippingAddress 和 BillingAddress 都屬於相同的.NET 型別,StreetAddress:For example, in the following class, ShippingAddress and BillingAddress are both of the same .NET type, StreetAddress:

public class Order
{
    public int Id { get; set; }
    public StreetAddress ShippingAddress { get; set; }
    public StreetAddress BillingAddress { get; set; }
}

若要了解如何 EF Core 會區別這些物件的追蹤執行個體,可能很有用考量定義瀏覽已經成為擁有者的索引鍵與值一起執行個體索引鍵的一部分,並且擁有的類型的.NET 類型。In order to understand how EF Core will distinguish tracked instances of these objects, it may be useful to think that the defining navigation has become part of the key of the instance alongside the value of the key of the owner and the .NET type of the owned type.

巢狀的擁有的類型Nested owned types

在此範例中 OrderDetails 擁有 BillingAddress 和 ShippingAddress,也就是這兩種 StreetAddress 類型。In this example OrderDetails owns BillingAddress and ShippingAddress, which are both StreetAddress types. 然後 OrderDetails 訂單類型所擁有。Then OrderDetails is owned by the Order type.

public class Order
{
    public int Id { get; set; }
    public OrderDetails OrderDetails { get; set; }
    public OrderStatus Status { get; set; }
}

public class OrderDetails
{
    public StreetAddress BillingAddress { get; set; }
    public StreetAddress ShippingAddress { get; set; }
}

public enum OrderStatus
{
    Pending,
    Shipped
}

public class StreetAddress
{
    public string Street { get; set; }
    public string City { get; set; }
}

可鏈結OwnsOnefluent 對應來設定此模型中的方法:It is possible to chain the OwnsOne method in a fluent mapping to configure this model:

modelBuilder.Entity<Order>().OwnsOne(p => p.OrderDetails, od =>
    {
        od.OwnsOne(c => c.BillingAddress);
        od.OwnsOne(c => c.ShippingAddress);
    });

您可達到相同的項目使用OwnedAttributeOrderDetails 和 StreetAdress 上。It is possible to achieve the same thing using OwnedAttribute on both OrderDetails and StreetAdress.

巢狀的擁有類型,除了擁有的類型可以參考的一般實體。In addition to nested owned types, an owned type can reference a regular entity. 在下列範例中,國家/地區會是一般的非擁有實體:In the following example, Country is a regular non-owned entity:

public class StreetAddress
{
    public string Street { get; set; }
    public string City { get; set; }
    public Country Country { get; set; }
}

這項功能在 EF6 中設定的複雜型別除了擁有的實體類型。This capability sets owned entity types apart from complex types in EF6.

儲存在個別的資料表擁有的類型Storing owned types in separate tables

也不同於 EF6 複雜類型,擁有的類型可以儲存在個別的資料表擁有者。Also unlike EF6 complex types, owned types can be stored in a separate table from the owner. 若要覆寫慣例,將擁有的類型對應到相同資料表的擁有者,您可以直接呼叫ToTable並提供不同的資料表名稱。In order to override the convention that maps an owned type to the same table as the owner, you can simply call ToTable and provide a different table name. 下列範例將 OrderDetails 和其兩個位址從對應到個別的資料表順序:The following example will map OrderDetails and its two addresses to a separate table from Order:

modelBuilder.Entity<Order>().OwnsOne(p => p.OrderDetails, od =>
    {
        od.OwnsOne(c => c.BillingAddress);
        od.OwnsOne(c => c.ShippingAddress);
        od.ToTable("OrderDetails");
    });

查詢擁有的類型Querying owned types

查詢擁有者時,預設會包含擁有的類型。When querying the owner the owned types will be included by default. 您不需要使用Include方法,即使擁有的類型會儲存在個別的資料表。It is not necessary to use the Include method, even if the owned types are stored in a separate table. 根據之前所述的模型,下列查詢將會提取順序、 OrderDetails 和兩個擁有的 StreetAddresses,從資料庫的所有暫止訂單:Based on the model described before, the following query will pull Order, OrderDetails and the two owned StreetAddresses for all pending orders from the database:

var orders = context.Orders.Where(o => o.Status == OrderStatus.Pending);

限制Limitations

其中某些限制是基礎如何擁有實體類型的運作,但其他限制,我們可以在未來版本中移除:Some of these limitations are fundamental to how owned entity types work, but some others are restrictions that we may be able to remove in future releases:

在舊版的缺點Shortcomings in previous versions

  • 在 EF Core 2.0 中,巡覽至擁有無法在衍生的實體型別宣告實體類型,除非所擁有的實體會明確對應至個別的資料表擁有者階層架構。In EF Core 2.0, navigations to owned entity types cannot be declared in derived entity types unless the owned entities are explicitly mapped to a separate table from the owner hierarchy. EF Core 2.1 中已移除這項限制This limitation has been removed in EF Core 2.1

目前的缺點Current shortcomings

  • 包含繼承階層架構可讓您擁有不支援實體類型Inheritance hierarchies that include owned entity types are not supported
  • 擁有的實體類型不能指向集合導覽屬性 (目前支援巡覽的唯一參考)Owned entity types cannot be pointed at by a collection navigation property (only reference navigations are currently supported)
  • 巡覽至擁有的實體類型不能是 null,除非它們會明確對應至個別的資料表擁有者Navigations to owned entity types cannot be null unless they are explicitly mapped to a separate table from the owner
  • 擁有的實體類型的執行個體不能共用的多個擁有者 (這是無法使用擁有的實體型別實作的值物件的知名案例)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)

依據設計限制By-design restrictions

  • 您無法建立 DbSet<T>You cannot create a DbSet<T>
  • 您不能呼叫Entity<T>()上擁有的類型 ModelBuilderYou cannot call Entity<T>() with an owned type on ModelBuilder