Types d’entité détenusOwned Entity Types

Note

Cette fonctionnalité est une nouveauté dans EF Core 2.0.This feature is new in EF Core 2.0.

EF Core vous permet aux types d’entité de modèle qui peuvent apparaître uniquement sur les propriétés de navigation d’autres types d’entités.EF Core allows you to model entity types that can only ever appear on navigation properties of other entity types. Ils sont appelés types d’entités détenus.These are called owned entity types. L’entité qui contient un type d’entité détenu est son propriétaire.The entity containing an owned entity type is its owner.

Configuration expliciteExplicit configuration

La propriété entité types sont jamais incluses par EF Core dans le modèle par convention.Owned entity types are never included by EF Core in the model by convention. Vous pouvez utiliser la OwnsOne méthode dans OnModelCreating ou annotez le type avec OwnedAttribute (nouveau dans EF Core 2.1) pour configurer le type comme un type détenu.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.

Dans cet exemple, StreetAddress est un type avec aucune propriété d’identité.In this example, StreetAddress is a type with no identity property. Il est utilisé comme propriété du type Order pour spécifier l’adresse d’expédition d’une commande particulière.It is used as a property of the Order type to specify the shipping address for a particular order. Dans OnModelCreating, nous utilisons le OwnsOne méthode pour spécifier que la propriété ShippingAddress est une entité à propriétaire du type de commande.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);

Si la propriété ShippingAddress est privée dans le type de commande, vous pouvez utiliser la version de chaîne de la OwnsOne méthode :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");

Dans cet exemple, nous utilisons le OwnedAttribute pour atteindre le même objectif :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; }
}

Clés implicitesImplicit keys

Dans EF Core 2.0 et 2.1, seules les propriétés de navigation de référence peuvent pointer vers les types détenus.In EF Core 2.0 and 2.1, only reference navigation properties can point to owned types. Collections de types détenus ne sont pas prises en charge.Collections of owned types are not supported. Ces référence détenu types ont toujours une relation un à un avec le propriétaire, par conséquent, ils n’ont pas leurs propres valeurs de clés.These reference owned types always have a one-to-one relationship with the owner, therefore they don't need their own key values. Dans l’exemple précédent, le type StreetAddress est inutile de définir une propriété de clé.In the previous example, the StreetAddress type does not need to define a key property.

Pour comprendre comment EF Core effectue le suivi de ces objets, il est utile de considérer qu’une clé primaire est créée comme un occulter la propriété pour le type détenu.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. La valeur de la clé d’une instance du type détenu sera identique à la valeur de la clé de l’instance de propriétaire.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.

Mappage de types avec fractionnement de table détenuMapping owned types with table splitting

Lorsque vous utilisez des bases de données relationnelles, par convention types détenue sont mappés à la même table en tant que le propriétaire.When using relational databases, by convention owned types are mapped to the same table as the owner. Cela nécessite de fractionnement de la table en deux : certaines colonnes seront utilisées pour stocker les données du propriétaire, et certaines colonnes seront utilisées pour stocker les données de l’entité détenue.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. Il s’agit d’une fonctionnalité commune appelée fractionnement de table.This is a common feature known as table splitting.

Conseil

Détenus types stockés avec le fractionnement de table peuvent être utilisé très semblable aux types complexes comment sont utilisées dans EF6.Owned types stored with table splitting can be used very similarly to how complex types are used in EF6.

Par convention, EF Core nommera les colonnes de la base de données pour les propriétés du type d’entité détenu suivant le modèle EntityProperty_OwnedEntityProperty.By convention, EF Core will name the database columns for the properties of the owned entity type following the pattern EntityProperty_OwnedEntityProperty. Par conséquent, les propriétés StreetAddress apparaîtra dans la table Orders avec les noms ShippingAddress_Street et ShippingAddress_City.Therefore the StreetAddress properties will appear in the Orders table with the names ShippingAddress_Street and ShippingAddress_City.

Vous pouvez ajouter la HasColumnName méthode pour renommer ces colonnes.You can append the HasColumnName method to rename those columns. Dans le cas où StreetAddress est une propriété publique, les mappages seraitIn 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");
        });

Partage le même type .NET parmi plusieurs types détenusSharing the same .NET type among multiple owned types

Type d’entité détenu peut être du même type .NET en tant qu’un autre type d’entité détenu, par conséquent, le type .NET peuvent ne pas suffire pour identifier un type détenu.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.

Dans ce cas, la propriété qui pointe du propriétaire à l’entité détenue devient le définition navigation du type d’entité détenu.In those cases, the property pointing from the owner to the owned entity becomes the defining navigation of the owned entity type. Du point de vue d’EF Core, la navigation de définition fait partie de l’identité du type en même temps que le type .NET.From the perspective of EF Core, the defining navigation is part of the type's identity alongside the .NET type.

Par exemple, dans la classe suivante, ShippingAddress et BillingAddress sont tous deux du même type .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; }
}

Pour comprendre comment EF Core distinguera suivies des instances de ces objets, il peut être utile de considérer que la navigation définie est devenue partie de la clé de l’instance en même temps que la valeur de la clé du propriétaire et le type .NET du type détenu.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.

Types détenus imbriquésNested owned types

Dans cet exemple OrderDetails possède BillingAddress et ShippingAddress, qui sont les deux types de StreetAddress.In this example OrderDetails owns BillingAddress and ShippingAddress, which are both StreetAddress types. OrderDetails est détenu par le type de commande.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; }
}

Il est possible de chaîner les OwnsOne méthode dans un mappage fluent pour configurer ce modèle :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);
    });

Il est possible de réaliser la même chose à l’aide OwnedAttribute sur OrderDetails et StreetAdress.It is possible to achieve the same thing using OwnedAttribute on both OrderDetails and StreetAdress.

En plus des types détenus imbriqués, un type détenu peut référencer une entité ordinaire.In addition to nested owned types, an owned type can reference a regular entity. Dans l’exemple suivant, le pays est une entité non détenus normale :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; }
}

Cette fonctionnalité définit les types d’entité détenus en dehors des types complexes dans EF6.This capability sets owned entity types apart from complex types in EF6.

Stockage des types détenus dans des tables distinctesStoring owned types in separate tables

Également Contrairement aux types complexes d’EF6, types détenus peuvent être stockées dans une table distincte du propriétaire.Also unlike EF6 complex types, owned types can be stored in a separate table from the owner. Pour substituer la convention qui mappe un type détenu à la même table en tant que le propriétaire, vous pouvez simplement appeler ToTable et fournir un autre nom de table.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. L’exemple suivant correspondent OrderDetails et ses deux adresses à une table distincte de commande :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");
    });

Interrogation des types détenusQuerying owned types

Quand le propriétaire fait l’objet d’une interrogation, les types détenus sont inclus par défaut.When querying the owner the owned types will be included by default. Il n’est pas nécessaire d’utiliser le Include (méthode), même si les types détenus sont stockées dans une table distincte.It is not necessary to use the Include method, even if the owned types are stored in a separate table. Selon le modèle décrit précédemment, la requête suivante extrait ordre, OrderDetails et les deux StreetAddresses détenus pour toutes les commandes en attente à partir de la base de données :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);

LimitationsLimitations

Certaines de ces restrictions sont fondamentaux pour comment détenu travail de types d’entité, mais d’autres sont des restrictions que nous pouvons être en mesure de supprimer dans les futures versions :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:

Lacunes dans les versions précédentesShortcomings in previous versions

  • Dans EF Core 2.0, navigations à la propriété types d’entité ne peut pas être déclarés dans les types d’entité dérivés, sauf si les entités sont explicitement mappées à une table distincte de la hiérarchie de propriétaire.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. Cette limitation a été supprimée dans EF Core 2.1This limitation has been removed in EF Core 2.1

Défauts actuelsCurrent shortcomings

  • Hiérarchies d’héritage qui incluent détenus types d’entité ne sont pas pris en charge.Inheritance hierarchies that include owned entity types are not supported
  • Types d’entité détenus ne peut pas être pointés par une propriété de navigation de collection (seule référence navigations sont actuellement gérées)Owned entity types cannot be pointed at by a collection navigation property (only reference navigations are currently supported)
  • Navigations à détenus types d’entité ne peut pas être null, sauf si elles sont explicitement mappées à une table distincte du propriétaireNavigations to owned entity types cannot be null unless they are explicitly mapped to a separate table from the owner
  • Instances de types d’entité détenus ne peut pas être partagées par plusieurs propriétaires (il s’agit d’un scénario bien connu pour les objets de valeur qui ne peut pas être implémenté à l’aide des types d’entité détenus)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)

Restrictions par conceptionBy-design restrictions

  • Vous ne pouvez pas créer un DbSet<T>You cannot create a DbSet<T>
  • Vous ne pouvez pas appeler Entity<T>() avec un type détenu sur ModelBuilderYou cannot call Entity<T>() with an owned type on ModelBuilder