擁有的實體類型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.

我們可以使用OwnedAttribute以將其視為擁有的實體時從另一個實體類型參考:We can use the OwnedAttribute to treat it as an owned entity when referenced from another entity type:

[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; }
}

也可以使用OwnsOne方法中的OnModelCreating來指定ShippingAddress屬性是擁有實體Order實體類型,並視需要設定其他 facet。It is also possible to use the OwnsOne method in OnModelCreating to specify that the ShippingAddress property is an Owned Entity of the Order entity type and to configure additional facets if needed.

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

如果ShippingAddress屬性是在私人Order類型,您可以使用的字串版本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");

請參閱完整的範例專案詳細內容。See the full sample project for more context.

隱含索引鍵Implicit keys

擁有的類型設定OwnsOne或透過參考的瀏覽探索到一定有一對一的關聯性和擁有者,因此它們不需要自己金鑰的值因為外部索引鍵值,都是唯一。Owned types configured with OwnsOne or discovered through a reference navigation always have a one-to-one relationship with the owner, therefore they don't need their own key values as the foreign key values are unique. 在上述範例中,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.

擁有的類型集合Collections of owned types

注意

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

若要設定擁有類型的集合OwnsMany應該用於OnModelCreatingTo configure a collection of owned types OwnsMany should be used in OnModelCreating. 不過的主索引鍵不會設定依照慣例,因此它必須明確加以指定。However the primary key will not be configured by convention, so it need to be specified explicitly. 通常會使用複雜的金鑰,這些類型的合併的擁有者和其他的唯一屬性也可以在陰影狀態中的外部索引鍵的實體:It is common to use a complex key for these type of entities incorporating the foreign key to the owner and an additional unique property that can also be in shadow state:

modelBuilder.Entity<Distributor>().OwnsMany(p => p.ShippingCenters, a =>
{
    a.HasForeignKey("DistributorId");
    a.Property<int>("Id");
    a.HasKey("DistributorId", "Id");
});

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

當使用關聯式資料庫,依慣例參考擁有類型會對應至相同資料表的擁有者。When using relational databases, by convention reference 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 similarly to how complex types are used in EF6.

依照慣例,EF Core 會遵循模式擁有的實體類型的屬性名稱的資料庫資料行_Navigation_OwnedEntityProperty_。By convention, EF Core will name the database columns for the properties of the owned entity type following the pattern Navigation_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:

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.

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

public class OrderDetails
{
    public DetailedOrder Order { get; set; }
    public StreetAddress BillingAddress { get; set; }
    public StreetAddress ShippingAddress { 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擁有BillingAddressShippingAddress,這兩者都StreetAddress型別。In this example OrderDetails owns BillingAddress and ShippingAddress, which are both StreetAddress types. 然後 DetailedOrder 類型擁有 OrderDetailsThen OrderDetails is owned by the DetailedOrder type.

public class DetailedOrder
{
    public int Id { get; set; }
    public OrderDetails OrderDetails { get; set; }
    public OrderStatus Status { get; set; }
}
public enum OrderStatus
{
    Pending,
    Shipped
}

巢狀的擁有類型,除了擁有的類型可以參考的一般實體,您可以擁有者或不同的實體,只要擁有的實體上的相依端。In addition to nested owned types, an owned type can reference a regular entity, it can be either the owner or a different entity as long as the owned entity is on the dependent side. 這項功能在 EF6 中設定的複雜型別除了擁有的實體類型。This capability sets owned entity types apart from complex types in EF6.

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

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

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

您也可達到相同的項目使用OwnedAttribute兩者OrderDetailsStreetAdressIt is also possible to achieve the same thing using OwnedAttribute on both OrderDetails and StreetAdress.

儲存在個別的資料表擁有的類型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及到不同的表格,從其兩個位址DetailedOrder:The following example will map OrderDetails and its two addresses to a separate table from DetailedOrder:

modelBuilder.Entity<DetailedOrder>().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. 根據之前所述的模型,下列查詢將會得到OrderOrderDetails擁有兩個StreetAddresses從資料庫:Based on the model described before, the following query will get Order, OrderDetails and the two owned StreetAddresses from the database:

var order = context.DetailedOrders.First(o => o.Status == OrderStatus.Pending);
Console.WriteLine($"First pending order will ship to: {order.OrderDetails.ShippingAddress.City}");

限制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:

依據設計限制By-design restrictions

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

目前的缺點Current shortcomings

  • 包含繼承階層架構可讓您擁有不支援實體類型Inheritance hierarchies that include owned entity types are not supported
  • 若要參考導覽擁有的實體類型不能是 null,除非它們會明確對應至個別的資料表擁有者Reference 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)

在舊版的缺點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
  • 支援 en-us EF Core 2.0 和 2.1 唯一參考巡覽至擁有的類型。En EF Core 2.0 and 2.1 only reference navigations to owned types were supported. EF Core 2.2 中已移除這項限制This limitation has been removed in EF Core 2.2