Types d’entités avec des constructeursEntity types with constructors

Note

Cette fonctionnalité est une nouveauté d’EF Core 2.1.This feature is new in EF Core 2.1.

À compter d’EF Core 2.1, il est désormais possible de définir un constructeur avec des paramètres et appeler ce constructeur lorsque vous créez une instance de l’entité de EF Core.Starting with EF Core 2.1, it is now possible to define a constructor with parameters and have EF Core call this constructor when creating an instance of the entity. Les paramètres du constructeur peuvent être liés à la propriété mappée, ou à différents types de services pour faciliter les comportements, tels que chargement différé.The constructor parameters can be bound to mapped properties, or to various kinds of services to facilitate behaviors like lazy-loading.

Note

À compter d’EF Core 2.1, tous les liaison constructeur est par convention.As of EF Core 2.1, all constructor binding is by convention. Configuration des constructeurs spécifiques à utiliser est prévue pour une version ultérieure.Configuration of specific constructors to use is planned for a future release.

Liaison aux propriétés mappéesBinding to mapped properties

Considérez un modèle de billet de Blog/classique :Consider a typical Blog/Post model:

public class Blog
{
    public int Id { get; set; }

    public string Name { get; set; }
    public string Author { get; set; }

    public ICollection<Post> Posts { get; } = new List<Post>();
}

public class Post
{
    public int Id { get; set; }

    public string Title { get; set; }
    public string Content { get; set; }
    public DateTime PostedOn { get; set; }

    public Blog Blog { get; set; }
}

Quand EF Core crée des instances de ces types, comme pour les résultats d’une requête, il sera tout d’abord appeler le constructeur sans paramètre par défaut et définissez chaque propriété sur la valeur à partir de la base de données.When EF Core creates instances of these types, such as for the results of a query, it will first call the default parameterless constructor and then set each property to the value from the database. Toutefois, si EF Core recherche un constructeur paramétrable avec des noms de paramètre et les types qui correspondent à ceux de mappé des propriétés, puis il appelle à la place du constructeur paramétré avec des valeurs pour ces propriétés et ne définit pas explicitement de chaque propriété.However, if EF Core finds a parameterized constructor with parameter names and types that match those of mapped properties, then it will instead call the parameterized constructor with values for those properties and will not set each property explicitly. Exemple :For example:

public class Blog
{
    public Blog(int id, string name, string author)
    {
        Id = id;
        Name = name;
        Author = author;
    }

    public int Id { get; set; }

    public string Name { get; set; }
    public string Author { get; set; }

    public ICollection<Post> Posts { get; } = new List<Post>();
}

public class Post
{
    public Post(int id, string title, DateTime postedOn)
    {
        Id = id;
        Title = title;
        PostedOn = postedOn;
    }

    public int Id { get; set; }

    public string Title { get; set; }
    public string Content { get; set; }
    public DateTime PostedOn { get; set; }

    public Blog Blog { get; set; }
}

Voici quelques éléments à noter :Some things to note:

  • Toutes les propriétés ne doivent avoir des paramètres du constructeur.Not all properties need to have constructor parameters. Par exemple, la propriété de Post.Content n’est pas définie par n’importe quel paramètre de constructeur, donc EF Core est la valeur après l’appel du constructeur de façon normale.For example, the Post.Content property is not set by any constructor parameter, so EF Core will set it after calling the constructor in the normal way.
  • Les noms et types de paramètres doivent correspondre à des types de propriété et les noms, à ceci près que les propriétés peuvent être casse Pascal tandis que les paramètres sont de casse mixte.The parameter types and names must match property types and names, except that properties can be Pascal-cased while the parameters are camel-cased.
  • EF Core ne peut pas définir les propriétés de navigation (par exemple, Blog ou les billets ci-dessus) à l’aide d’un constructeur.EF Core cannot set navigation properties (such as Blog or Posts above) using a constructor.
  • Le constructeur peut être public, privé, ou avoir n’importe quelle autre accessibilité.The constructor can be public, private, or have any other accessibility.

Propriétés en lecture seuleRead-only properties

Une fois que les propriétés sont définies via le constructeur, il peut être judicieux pour rendre certains d'entre eux en lecture seule.Once properties are being set via the constructor it can make sense to make some of them read-only. EF Core prend en charge, mais il existe quelques éléments à rechercher :EF Core supports this, but there are some things to look out for:

  • Propriétés sans accesseurs Set ne sont pas mappées par convention.Properties without setters are not mapped by convention. (Cela a tendance à mapper des propriétés qui ne doivent pas être mappées, telles que les propriétés calculées.)(Doing so tends to map properties that should not be mapped, such as computed properties.)
  • À l’aide de valeurs de clés générées automatiquement nécessite une propriété de clé qui est en lecture-écriture, dans la mesure où la valeur de clé doit être définie par le Générateur de clé lors de l’insertion de nouvelles entités.Using automatically generated key values requires a key property that is read-write, since the key value needs to be set by the key generator when inserting new entities.

Un moyen simple pour éviter ces choses consiste à utiliser les méthodes Set privées.An easy way to avoid these things is to use private setters. Exemple :For example:

public class Blog
{
    public Blog(int id, string name, string author)
    {
        Id = id;
        Name = name;
        Author = author;
    }

    public int Id { get; private set; }

    public string Name { get; private set; }
    public string Author { get; private set; }

    public ICollection<Post> Posts { get; } = new List<Post>();
}

public class Post
{
    public Post(int id, string title, DateTime postedOn)
    {
        Id = id;
        Title = title;
        PostedOn = postedOn;
    }

    public int Id { get; private set; }

    public string Title { get; private set; }
    public string Content { get; set; }
    public DateTime PostedOn { get; private set; }

    public Blog Blog { get; set; }
}

EF Core voit une propriété avec un setter privée en lecture-écriture, ce qui signifie que toutes les propriétés sont mappées comme auparavant et la clé peut toujours être générées par le magasin.EF Core sees a property with a private setter as read-write, which means that all properties are mapped as before and the key can still be store-generated.

Une alternative à l’utilisation de méthodes setter privée consiste à rendre les propriétés vraiment en lecture seule et ajouter un mappage plus explicite dans OnModelCreating.An alternative to using private setters is to make properties really read-only and add more explicit mapping in OnModelCreating. De même, certaines propriétés peuvent être complètement supprimées et remplacées par uniquement des champs.Likewise, some properties can be removed completely and replaced with only fields. Par exemple, considérez ces types d’entités :For example, consider these entity types:

public class Blog
{
    private int _id;

    public Blog(string name, string author)
    {
        Name = name;
        Author = author;
    }

    public string Name { get; }
    public string Author { get; }

    public ICollection<Post> Posts { get; } = new List<Post>();
}

public class Post
{
    private int _id;

    public Post(string title, DateTime postedOn)
    {
        Title = title;
        PostedOn = postedOn;
    }

    public string Title { get; }
    public string Content { get; set; }
    public DateTime PostedOn { get; }

    public Blog Blog { get; set; }
}

Et cette configuration dans OnModelCreating :And this configuration in OnModelCreating:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>(
        b =>
        {
            b.HasKey("_id");
            b.Property(e => e.Author);
            b.Property(e => e.Name);
        });

    modelBuilder.Entity<Post>(
        b =>
        {
            b.HasKey("_id");
            b.Property(e => e.Title);
            b.Property(e => e.PostedOn);
        });
}

Points à noter :Things to note:

  • La clé « property » est désormais un champ.The key "property" is now a field. Il n’est pas un readonly afin que les clés générées par le magasin peuvent être utilisées.It is not a readonly field so that store-generated keys can be used.
  • Les autres propriétés sont en lecture seule défini uniquement dans le constructeur.The other properties are read-only properties set only in the constructor.
  • Si la valeur de clé primaire est toujours définie par Entity Framework ou lire à partir de la base de données, il est inutile d’inclure cela dans le constructeur.If the primary key value is only ever set by EF or read from the database, then there is no need to include it in the constructor. Cela laisse la clé « property » comme un champ simple et permet d’identifier clairement qu’il ne doit pas être défini explicitement lors de la création de nouveaux blogs ou les billets.This leaves the key "property" as a simple field and makes it clear that it should not be set explicitly when creating new blogs or posts.

Note

Ce code entraîne '169' indiquant que le champ n’est jamais utilisé d’avertissement du compilateur.This code will result in compiler warning '169' indicating that the field is never used. Cela peut être ignoré, car en réalité EF Core utilise le champ de manière extralinguistic.This can be ignored since in reality EF Core is using the field in an extralinguistic manner.

Injection de servicesInjecting services

EF Core peut également injecter des « services » dans le constructeur du type d’une entité.EF Core can also inject "services" into an entity type's constructor. Par exemple, ce qui suit peut être injecté :For example, the following can be injected:

  • DbContext -l’instance de contexte actuel, ce qui peut également être tapé sous forme votre type DbContext dérivéeDbContext - the current context instance, which can also be typed as your derived DbContext type
  • ILazyLoader -le service de chargement différé, consultez le chargement différé documentation pour plus d’informationsILazyLoader - the lazy-loading service--see the lazy-loading documentation for more details
  • Action<object, string> -un délégué sur le chargement différé, consultez le chargement différé documentation pour plus d’informationsAction<object, string> - a lazy-loading delegate--see the lazy-loading documentation for more details
  • IEntityType -les métadonnées de EF Core associées à ce type d’entitéIEntityType - the EF Core metadata associated with this entity type

Note

À compter d’EF Core 2.1, seuls les services connus par EF Core peuvent être injectées.As of EF Core 2.1, only services known by EF Core can be injected. Prise en charge d’injection de services d’application est envisagée pour une version ultérieure.Support for injecting application services is being considered for a future release.

Par exemple, un DbContext injecté peut être utilisé pour accéder de façon sélective la base de données pour obtenir des informations sur les entités connexes sans charger toutes les.For example, an injected DbContext can be used to selectively access the database to obtain information about related entities without loading them all. Dans l’exemple ci-dessous, cela est utilisé pour obtenir le nombre de billets dans un blog sans charger les billets de :In the example below this is used to obtain the number of posts in a blog without loading the posts:

public class Blog
{
    public Blog()
    {
    }

    private Blog(BloggingContext context)
    {
        Context = context;
    }

    private BloggingContext Context { get; set; }

    public int Id { get; set; }
    public string Name { get; set; }
    public string Author { get; set; }

    public ICollection<Post> Posts { get; set; }

    public int PostsCount
        => Posts?.Count
           ?? Context?.Set<Post>().Count(p => Id == EF.Property<int?>(p, "BlogId"))
           ?? 0;
}

public class Post
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }
    public DateTime PostedOn { get; set; }

    public Blog Blog { get; set; }
}

Choses à remarquer à ce sujet :A few things to notice about this:

  • Le constructeur est privé, dans la mesure où elle est toujours appelée par EF Core, et il existe un autre constructeur public pour une utilisation générale.The constructor is private, since it is only ever called by EF Core, and there is another public constructor for general use.
  • Le code qui utilise le service injecté (autrement dit, le contexte) est la défense contre lui en cours null pour gérer les cas où EF Core n’est pas création de l’instance.The code using the injected service (that is, the context) is defensive against it being null to handle cases where EF Core is not creating the instance.
  • Étant donné que le service est stocké dans une propriété en lecture/écriture, elle sera réinitialisée lorsque l’entité est attachée à une nouvelle instance de contexte.Because service is stored in a read/write property it will be reset when the entity is attached to a new context instance.

Avertissement

Injecter le DbContext, comme cela est souvent considéré comme un anti-modèle dans la mesure où elle associe vos types d’entité directement vers EF Core.Injecting the DbContext like this is often considered an anti-pattern since it couples your entity types directly to EF Core. Étudiez attentivement toutes les options avant d’utiliser l’injection de service comme suit.Carefully consider all options before using service injection like this.