Nicht eigenständige EntitätstypenOwned Entity Types

EF Core ermöglicht es Ihnen, Entitäts Typen zu modellieren, die nur in den Navigations Eigenschaften anderer Entitäts Typen angezeigt werden können.EF Core allows you to model entity types that can only ever appear on navigation properties of other entity types. Diese werden als eigene Entitäts Typen bezeichnet.These are called owned entity types. Die Entität, die einen eigenen Entitätstyp enthält, ist Ihr Besitzer.The entity containing an owned entity type is its owner.

Besitzende Entitäten sind im wesentlichen Teil des Besitzers und können nicht ohne Sie vorhanden sein, Sie sind konzeptionell ähnlich wie Aggregate.Owned entities are essentially a part of the owner and cannot exist without it, they are conceptually similar to aggregates. Dies bedeutet, dass die eigene Entität definitionsgemäß auf der abhängigen Seite der Beziehung mit dem Besitzer ist.This means that the owned entity is by definition on the dependent side of the relationship with the owner.

Explizite KonfigurationExplicit configuration

Eigene Entitäts Typen werden nie von EF Core im Modell nach Konvention eingeschlossen.Owned entity types are never included by EF Core in the model by convention. Sie können die- OwnsOne Methode in verwenden OnModelCreating oder den Typ mit kommentieren OwnedAttribute , um den Typ als eigenen Typ zu konfigurieren.You can use the OwnsOne method in OnModelCreating or annotate the type with OwnedAttribute to configure the type as an owned type.

In diesem Beispiel StreetAddress ist ein Typ ohne Identity-Eigenschaft.In this example, StreetAddress is a type with no identity property. Dieser Typ wird als Eigenschaft des Typs „Order“ verwendet, um die Lieferadresse für eine bestimmte Bestellung anzugeben.It is used as a property of the Order type to specify the shipping address for a particular order.

Wir können das verwenden OwnedAttribute , um es als eigene Entität zu behandeln, wenn von einem anderen Entitätstyp darauf verwiesen wird: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; }
}

Es ist auch möglich, mit der- OwnsOne Methode in OnModelCreating anzugeben, dass es sich bei der ShippingAddress Eigenschaft um eine Entität des Order Entitäts Typs handelt, und um ggf. weitere Facetten zu konfigurieren.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);

Wenn die ShippingAddress Eigenschaft im-Typ privat ist Order , können Sie die Zeichen folgen Version der- OwnsOne Methode verwenden: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");

Das obige Modell ist dem folgenden Datenbankschema zugeordnet:The model above is mapped to the following database schema:

Sceenshot des Datenbankmodells für eine Entität, die einen eigenen Verweis enthält

Weitere Informationen finden Sie im vollständigen Beispiel Projekt .See the full sample project for more context.

Tipp

Der zugehörige Entitätstyp kann als erforderlich markiert werden. Weitere Informationen finden Sie unter erforderliche 1-zu-eins-abhängige Abhängigkeiten .The owned entity type can be marked as required, see Required one-to-one dependents for more information.

Implizite SchlüsselImplicit keys

OwnsOneBesitzer Typen, die mit einer Verweis Navigation konfiguriert oder ermittelt wurden, haben immer eine eins-zu-eins-Beziehung mit dem Besitzer und benötigen daher keine eigenen Schlüsselwerte, da die Fremdschlüssel Werte eindeutig sind.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. Im vorherigen Beispiel muss der- StreetAddress Typ keine Schlüsseleigenschaft definieren.In the previous example, the StreetAddress type does not need to define a key property.

Um zu verstehen, wie EF Core diese Objekte verfolgt, ist es hilfreich zu wissen, dass ein Primärschlüssel als Schatten Eigenschaft für den eigenen Typ erstellt wird.In order to understand how EF Core tracks these objects, it is useful to know that a primary key is created as a shadow property for the owned type. Der Wert des Schlüssels einer Instanz des eigenen Typs ist mit dem Wert des Schlüssels der Besitzer Instanz identisch.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.

Auflistungen von eigenen TypenCollections of owned types

Verwenden Sie zum Konfigurieren einer Sammlung von eigenen Typen OwnsMany in OnModelCreating .To configure a collection of owned types use OwnsMany in OnModelCreating.

Für eigene Typen ist ein Primärschlüssel erforderlich.Owned types need a primary key. Wenn für den .NET-Typ keine guten Kandidaten Eigenschaften vorhanden sind, können EF Core versuchen, eine zu erstellen.If there are no good candidates properties on the .NET type, EF Core can try to create one. Wenn jedoch eigene Typen durch eine Auflistung definiert werden, reicht es nicht aus, eine Schatten Eigenschaft zu erstellen, die sowohl als Fremdschlüssel für den Besitzer als auch als Primärschlüssel der eigenen Instanz fungiert. Dies geschieht wie folgt OwnsOne : Es können mehrere eigene Typinstanzen für jeden Besitzer vorhanden sein, und daher reicht der Schlüssel des Besitzers nicht aus, um eine eindeutige Identität für jede eigene Instanz bereitzustellen.However, when owned types are defined through a collection, it isn't enough to just create a shadow property to act as both the foreign key into the owner and the primary key of the owned instance, as we do for OwnsOne: there can be multiple owned type instances for each owner, and hence the key of the owner isn't enough to provide a unique identity for each owned instance.

Die beiden einfachsten Lösungen hierfür sind:The two most straightforward solutions to this are:

  • Definieren eines Ersatz Primärschlüssels für eine neue Eigenschaft, unabhängig vom Fremdschlüssel, der auf den Besitzer zeigt.Defining a surrogate primary key on a new property independent of the foreign key that points to the owner. Die enthaltenen Werte müssen für alle Besitzer eindeutig sein (z. b. wenn das übergeordnete Element über ein untergeordnetes Element {1} verfügt, das übergeordnete Element kein unter {1} geordnetes Element {2} haben kann {1} ), sodass der Wert keine inhärente Bedeutung hatThe contained values would need to be unique across all owners (e.g. if Parent {1} has Child {1}, then Parent {2} cannot have Child {1}), so the value doesn't have any inherent meaning. Da der Fremdschlüssel nicht Teil des Primärschlüssels ist, können seine Werte geändert werden, sodass Sie ein untergeordnetes Element von einem übergeordneten Element zu einem anderen verschieben können. Dies ist jedoch in der Regel gegen Aggregat Semantik.Since the foreign key is not part of the primary key its values can be changed, so you could move a child from one parent to another one, however this usually goes against aggregate semantics.
  • Verwenden des fremd Schlüssels und einer zusätzlichen Eigenschaft als zusammengesetzten Schlüssel.Using the foreign key and an additional property as a composite key. Der zusätzliche Eigenschafts Wert muss nun nur für ein bestimmtes übergeordnetes Element eindeutig sein (wenn das übergeordnete Element über ein untergeordnetes Element verfügt, kann das übergeordnete Element {1} {1,1} {2} nach wie vor über {2,1}The additional property value now only needs to be unique for a given parent (so if Parent {1} has Child {1,1} then Parent {2} can still have Child {2,1}). Durch den Fremdschlüssel Teil des Primärschlüssels wird die Beziehung zwischen dem Besitzer und der Entität, die im Besitz des Primärschlüssels ist, unveränderlich und die Aggregat Semantik ist besser.By making the foreign key part of the primary key the relationship between the owner and the owned entity becomes immutable and reflects aggregate semantics better. Dies geschieht EF Core standardmäßig.This is what EF Core does by default.

In diesem Beispiel verwenden wir die- Distributor Klasse.In this example we'll use the Distributor class.

public class Distributor
{
    public int Id { get; set; }
    public ICollection<StreetAddress> ShippingCenters { get; set; }
}

Standardmäßig ist der Primärschlüssel, der für den eigenen Typ verwendet wird, auf den über die Navigations Eigenschaft verwiesen wird, ShippingCenters ("DistributorId", "Id") wobei "DistributorId" der FK und ein eindeutiger "Id" int Wert ist.By default the primary key used for the owned type referenced through the ShippingCenters navigation property will be ("DistributorId", "Id") where "DistributorId" is the FK and "Id" is a unique int value.

Zum Konfigurieren eines anderen Primärschlüssel Aufrufes HasKey .To configure a different primary key call HasKey.

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

Das obige Modell ist dem folgenden Datenbankschema zugeordnet:The model above is mapped to the following database schema:

Sceenshot des Datenbankmodells für eine Entität, die eine eigene Sammlung enthält

Zuordnung eigener Typen mit Tabellen AufteilungMapping owned types with table splitting

Bei der Verwendung von relationalen Datenbanken werden Verweis eigene Typen standardmäßig derselben Tabelle wie der Besitzer zugeordnet.When using relational databases, by default reference owned types are mapped to the same table as the owner. Dies erfordert das Aufteilen der Tabelle in zwei: einige Spalten werden zum Speichern der Daten des Besitzers verwendet, und einige Spalten werden zum Speichern von Daten der Entität verwendet.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. Dies ist ein gängiges Feature, das als Tabellen Aufteilungbezeichnet wird.This is a common feature known as table splitting.

Standardmäßig benennen EF Core die Daten Bank Spalten für die Eigenschaften des eigenen Entitäts Typs nach dem Muster Navigation_OwnedEntityProperty.By default, EF Core will name the database columns for the properties of the owned entity type following the pattern Navigation_OwnedEntityProperty. Daher StreetAddress werden die Eigenschaften in der Tabelle "Orders" mit den Namen "ShippingAddress_Street" und "ShippingAddress_City" angezeigt.Therefore the StreetAddress properties will appear in the 'Orders' table with the names 'ShippingAddress_Street' and 'ShippingAddress_City'.

Sie können die- HasColumnName Methode verwenden, um diese Spalten umzubenennen.You can use 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");
    });

Hinweis

Die meisten der normalen Entitätstyp-Konfigurations Methoden wie Ignore können auf die gleiche Weise aufgerufen werden.Most of the normal entity type configuration methods like Ignore can be called in the same way.

Gemeinsame Nutzung desselben .net-Typs für mehrere eigene TypenSharing the same .NET type among multiple owned types

Ein eigener Entitätstyp kann vom gleichen .NET-Typ wie ein anderer eigener Entitätstyp sein. Daher reicht der .NET-Typ möglicherweise nicht aus, um einen eigenen Typ zu identifizieren.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 diesen Fällen wird die-Eigenschaft, die vom Besitzer auf die eigene Entität zeigt, zur definierenden Navigation des eigenen Entitäts Typs.In those cases, the property pointing from the owner to the owned entity becomes the defining navigation of the owned entity type. Aus Sicht der EF Core ist die definierende Navigation Teil der Typidentität neben dem .NET-Typ.From the perspective of EF Core, the defining navigation is part of the type's identity alongside the .NET type.

In der folgenden Klasse ShippingAddress und BillingAddress sind z. b. beide identisch mit dem .NET-Typ 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; }
}

Um zu verstehen, wie EF Core überwachte Instanzen dieser Objekte unterscheiden, kann es hilfreich sein zu wissen, dass die definierende Navigation neben dem Wert des Schlüssels des Besitzers und dem .NET-Typ des eigenen Typs zu einem Teil des Schlüssels der Instanz geworden ist.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.

Im Besitz befindliche TypenNested owned types

In diesem Beispiel OrderDetails gehören BillingAddress und ShippingAddress , bei denen es sich um beide StreetAddress Typen handelt.In this example OrderDetails owns BillingAddress and ShippingAddress, which are both StreetAddress types. Dann besitzt der DetailedOrder-Typ OrderDetails.Then 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
}

Jede Navigation zu einem eigenen Typ definiert einen separaten Entitätstyp mit vollständig unabhängiger Konfiguration.Each navigation to an owned type defines a separate entity type with completely independent configuration.

Zusätzlich zu den Typen, die im Besitz von Typen sind, kann ein eigener Typ auf eine reguläre Entität verweisen, die entweder der Besitzer oder eine andere Entität sein kann, solange sich die zugehörige Entität auf der abhängigen Seite befindet.In addition to nested owned types, an owned type can reference a regular entity which can be either the owner or a different entity as long as the owned entity is on the dependent side. Diese Funktion legt eigene Entitäts Typen außer komplexen Typen in EF6 fest.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; }
}

Konfigurieren von eigenen TypenConfiguring Owned Types

Es ist möglich, die- OwnsOne Methode in einem fließend aufzurufenden aufzurufen, um dieses Modell zu konfigurieren: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.WithOwner(d => d.Order);
    od.Navigation(d => d.Order).UsePropertyAccessMode(PropertyAccessMode.Property);
    od.OwnsOne(c => c.BillingAddress);
    od.OwnsOne(c => c.ShippingAddress);
});

Beachten Sie den-Befehl WithOwner , der zum Definieren der Navigations Eigenschaft verwendet wird, die auf den Besitzer zeigt.Notice the WithOwner call used to define the navigation property pointing back at the owner. Zum Definieren einer Navigation zum Owner-Entitätstyp, der nicht Teil der Besitz Beziehung ist, WithOwner() sollte ohne Argumente aufgerufen werden.To define a navigation to the owner entity type that's not part of the ownership relationship WithOwner() should be called without any arguments.

Sie können dieses Ergebnis auch mithilfe OwnedAttribute von sowohl in als auch in erreichen OrderDetails StreetAddress .It is also possible to achieve this result using OwnedAttribute on both OrderDetails and StreetAddress.

Beachten Sie außerdem den-Befehl Navigation .In addition, notice the Navigation call. In efcore 5,0 können Navigations Eigenschaften zu eigenen Typen für nicht-eigene Navigations Eigenschaftenweiter konfiguriert werden.In EFCore 5.0, navigation properties to owned types can be further configured as for non-owned navigation properties.

Das obige Modell ist dem folgenden Datenbankschema zugeordnet:The model above is mapped to the following database schema:

Sceenshot des Datenbankmodells für eine Entität, die geschpostete Verweise enthält

Speichern von eigenen Typen in separaten TabellenStoring owned types in separate tables

Im Gegensatz zu komplexen EF6-Typen können eigene Typen in einer separaten Tabelle vom Besitzer gespeichert werden.Also unlike EF6 complex types, owned types can be stored in a separate table from the owner. Um die Konvention zu überschreiben, die einen eigenen Typ derselben Tabelle wie der Besitzer zuordnet, können Sie einfach aufzurufen ToTable und einen anderen Tabellennamen angeben.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. Im folgenden Beispiel werden OrderDetails und die beiden Adressen einer separaten Tabelle von zugeordnet 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.ToTable("OrderDetails");
});

Es ist auch möglich TableAttribute , das zu verwenden, aber beachten Sie, dass dies fehlschlägt, wenn mehrere Navigationen zum eigenen Typ vorhanden sind, da in diesem Fall mehrere Entitäts Typen derselben Tabelle zugeordnet werden.It is also possible to use the TableAttribute to accomplish this, but note that this would fail if there are multiple navigations to the owned type since in that case multiple entity types would be mapped to the same table.

Abfragen von eigenen TypenQuerying owned types

Beim Abfragen des Besitzers werden standardmäßig eigene Typen eingeschlossen.When querying the owner the owned types will be included by default. Es ist nicht erforderlich, die- Include Methode zu verwenden, auch wenn die eigenen Typen in einer separaten Tabelle gespeichert werden.It is not necessary to use the Include method, even if the owned types are stored in a separate table. Basierend auf dem zuvor beschriebenen Modell wird die folgende Abfrage Order OrderDetails und die beiden im Besitz der- StreetAddresses Datenbank angezeigt: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}");

EinschränkungenLimitations

Einige dieser Einschränkungen sind grundlegend für die Funktionsweise von Entitäts Typen im Besitz, einige andere sind jedoch Einschränkungen, die in zukünftigen Versionen möglicherweise entfernt werden könnten: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:

Entwurfs EinschränkungenBy-design restrictions

  • Sie können keinen DbSet<T> für einen eigenen Typ erstellen.You cannot create a DbSet<T> for an owned type.
  • Es ist nicht möglich, Entity<T>() mit einem eigenen Typ für aufzurufen ModelBuilder .You cannot call Entity<T>() with an owned type on ModelBuilder.
  • Instanzen von eigenen Entitäts Typen können nicht von mehreren Besitzern gemeinsam genutzt werden (Dies ist ein bekanntes Szenario für Wert Objekte, das nicht mit Entitäts Typen im Besitz von Entitäten implementiert werden kann).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).

Aktuelle MängelCurrent shortcomings

  • Besitzende Entitäts Typen dürfen keine Vererbungs Hierarchien aufweisenOwned entity types cannot have inheritance hierarchies

Mängel in früheren VersionenShortcomings in previous versions

  • In EF Core 2. x-Referenz Navigation zu besitzenden Entitäts Typen dürfen nicht NULL sein, es sei denn, Sie werden explizit einer separaten Tabelle vom Besitzer zugeordnet.In EF Core 2.x reference navigations to owned entity types cannot be null unless they are explicitly mapped to a separate table from the owner.
  • In EF Core 3. x werden die Spalten für eigene Entitäts Typen, die derselben Tabelle wie der Besitzer zugeordnet sind, immer als "Nullable" markiert.In EF Core 3.x the columns for owned entity types mapped to the same table as the owner are always marked as nullable.