Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Il est possible de définir un constructeur avec des paramètres et d’appeler ce constructeur EF Core lors de la création d’une instance de l’entité. Les paramètres du constructeur peuvent être liés à des propriétés mappées ou à différents types de services pour faciliter les comportements tels que le chargement différé.
Note
Actuellement, toutes les liaisons de constructeur sont par convention. La configuration des constructeurs spécifiques à utiliser est prévue pour une version ultérieure.
Liaison aux propriétés mappées
Prenez un modèle de blog/article de blog courant :
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, elle appelle d’abord le constructeur sans paramètre par défaut, puis définit chaque propriété sur la valeur de la base de données. Toutefois, si EF Core trouve un constructeur paramétrable avec des noms de paramètres et des types qui correspondent à ceux des propriétés mappées, il appelle plutôt le constructeur paramétrable avec des valeurs pour ces propriétés et ne définit pas explicitement chaque propriété. Par exemple:
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; }
}
Quelques éléments à prendre en compte :
- Toutes les propriétés ne doivent pas avoir de paramètres de constructeur. Par exemple, la propriété Post.Content n’est définie par aucun paramètre de constructeur. EF Core l’définit donc après l’appel du constructeur de la manière normale.
- Les types de paramètres et les noms doivent correspondre aux types de propriétés et aux noms, sauf que les propriétés peuvent utiliser la casse Pascal tandis que les paramètres utilisent la casse camel.
- EF Core ne peut pas définir les propriétés de navigation (telles que blog ou billets ci-dessus) à l’aide d’un constructeur.
- Le constructeur peut être public, privé ou avoir toute autre accessibilité. Toutefois, les proxies de lazy loading nécessitent que le constructeur soit accessible à partir de la classe proxy dérivée. Cela signifie généralement qu’il est public ou protégé.
Propriétés en lecture seule
Une fois que les propriétés sont définies via le constructeur, il peut être judicieux de rendre certaines d'entre elles en lecture seule. EF Core prend en charge cela, mais il existe quelques éléments à surveiller.
- Les propriétés sans setters ne sont pas mappées par convention. (Cela tend à mapper les propriétés qui ne doivent pas être mappées, telles que les propriétés calculées.)
- L’utilisation de valeurs de clé générées automatiquement nécessite une propriété de clé en lecture-écriture, car la valeur de clé doit être définie par le générateur de clés lors de l’insertion de nouvelles entités.
Un moyen simple d’éviter ces choses est d’utiliser des setters privés. Par exemple:
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 considère une propriété avec un setter privé comme étant en lecture-écriture, ce qui signifie que toutes les propriétés sont ajustées comme auparavant et que la clé peut toujours être générée automatiquement par la base de données.
Une alternative à l’utilisation de setters privés consiste à rendre les propriétés en lecture seule et à ajouter un mappage plus explicite dans OnModelCreating. De même, certaines propriétés peuvent être supprimées complètement et remplacées par des champs uniquement. Par exemple, tenez compte de ces types d’entités :
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 :
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 à retenir :
- La clé « property » est maintenant un champ. Il ne s’agit pas d’un
readonlychamp afin que les clés générées par le magasin puissent être utilisées. - Les autres propriétés sont des propriétés en lecture seule définies uniquement dans le constructeur.
- Si la valeur de la clé primaire est uniquement définie par EF et lue à partir de la base de données, il n’est pas nécessaire de l’inclure dans le constructeur. Cela laisse la clé « propriété » en tant que champ simple et indique clairement qu’elle ne doit pas être définie explicitement lors de la création de blogs ou de billets.
Note
Ce code génère un avertissement du compilateur « 169 » indiquant que le champ n’est jamais utilisé. Cela peut être ignoré, car dans la réalité EF Core utilise le champ de manière extralinguistique.
Injection de services
EF Core peut également injecter des « services » dans le constructeur d’un type d’entité. Par exemple, vous pouvez injecter les éléments suivants :
-
DbContext- l’instance de contexte actuelle, qui peut également être typée comme votre type de DbContext dérivé -
ILazyLoader- le service de chargement différé — voir la documentation sur le chargement différé pour plus de détails -
Action<object, string>- un délégué lazy-loading—consultez la documentation lazy-loading pour plus de détails -
IEntityType- métadonnées EF Core associées à ce type d’entité
Note
Actuellement, seuls les services connus par EF Core peuvent être injectés. La prise en charge de l’injection de services applicatifs est envisagée pour une prochaine version.
Par exemple, une dbContext injectée peut être utilisée pour accéder de manière sélective à la base de données afin d’obtenir des informations sur les entités associées sans les charger toutes. Dans l’exemple ci-dessous, il est utilisé pour obtenir le nombre de billets dans un blog sans charger les billets :
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; }
}
Voici quelques points à noter :
- Le constructeur est privé, car il n'est appelé que par EF Core, et il existe un autre constructeur public pour une utilisation générale.
- Le code utilisant le service injecté (autrement dit, le contexte) est défensif contre celui-ci
nullpour gérer les cas où EF Core ne crée pas l’instance. - Étant donné que le service est stocké dans une propriété en lecture/écriture, il sera réinitialisé lorsque l’entité est attachée à une nouvelle instance de contexte.
Avertissement
L’injection de DbContext comme ceci est souvent considérée comme un anti-modèle, car elle couple vos types d’entités directement à EF Core. Examinez attentivement toutes les options avant d’utiliser l’injection de service comme celle-ci.