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

Note

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

À compter de 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 aux propriétés mappées, à différents types de services pour faciliter les comportements like ou 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 de EF Core 2.1, toute liaison de 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/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; }
}

Lorsque EF Core crée des instances de ces types, tels que pour les résultats d’une requête, il sera tout d’abord appeler le constructeur sans paramètre par défaut, puis définissez chaque propriété à 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. Par 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; }
}

Certains points à 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 un paramètre de constructeur, donc EF Core définirai ces informations après avoir appelé le 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 principal ne peut pas définir les propriétés de navigation (par exemple, le Blog ou publications 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 certains éléments à prendre en compte :EF Core supports this, but there are some things to look out for:

  • Propriétés sans méthodes setter ne sont pas mappées par convention.Properties without setters are not mapped by convention. (Cela a tendance à mapper les 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 des valeurs de clés générées automatiquement nécessite une propriété de clé qui est en lecture-écriture, étant donné que 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 éléments consiste à utiliser les méthodes setter privé.An easy way to avoid these things is to use private setters. Par 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é en lecture-écriture, ce qui signifie que toutes les propriétés sont mappées comme avant, 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é consiste à définir des propriétés réellement 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és.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 EF ou lire à partir de la base de données, puis il n’est pas nécessaire d’inclure 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 de type simple et indique clairement qu’il ne doit pas être défini explicitement lors de la création de nouveaux blogs ou des publications.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 génère avertissement '169' indiquant que le champ n’est jamais utilisé.This code will result in compiler warning '169' indicating that the field is never used. Cela peut être ignoré, car il est 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 des servicesInjecting services

EF Core pouvez injecter également un « services » dans le constructeur du type d’une entité.EF Core can also inject "services" into an entity type's constructor. Par exemple, les éléments suivants peuvent être ajoutés :For example, the following can be injected:

  • DbContext -l’instance de contexte actuel, qui peut également être de type votre type dérivé de DbContextDbContext - 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é de 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 principales associées à ce type d’entitéIEntityType - the EF Core metadata associated with this entity type

Note

À compter de EF Core 2.1, uniquement les services connus EF Core peuvent être ajoutées.As of EF Core 2.1, only services known by EF Core can be injected. Prise en charge pour l’injection des 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 servir à sélectivement accéder à la base de données pour obtenir des informations sur les entités associées 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 publications de blog de 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 à noter à ce sujet :A few things to notice about this:

  • Le constructeur est privé, car il 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 défense contre lui étant null pour gérer les cas où EF Core n’est pas création de l’instance.The code using the injected service (i.e. 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

Injection le DbContext comme cela est souvent considéré un anti-modèle, car il associe vos types d’entité directement à 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 des services comme suit.Carefully consider all options before using service injection like this.