EF codefirst: Dois-je initialiser les propriétés de navigation?

J’avais vu des livres (par exemple, Julia Lerman, le code-cadre de l’entité de programmation ) définir leurs classes de domaine (POCO) sans initialisation des propriétés de navigation comme:

public class User { public int Id { get; set; } public ssortingng UserName { get; set; } public virtual ICollection
Address { get; set; } public virtual License License { get; set; } }

certains autres livres ou outils (par exemple, Entity Framework Power Tools ) lors de la génération de POCO initialisent les propriétés de navigation de la classe, comme:

 public class User { public User() { this.Addresses = new IList
(); this.License = new License(); } public int Id { get; set; } public ssortingng UserName { get; set; } public virtual ICollection
Addresses { get; set; } public virtual License License { get; set; } }

Q1: Lequel est le meilleur? Pourquoi? Avantages et inconvénients?

Modifier:

 public class License { public License() { this.User = new User(); } public int Id { get; set; } public ssortingng Key { get; set; } public DateTime Expirtion { get; set; } public virtual User User { get; set; } } 

Q2: Dans la seconde approche, il y aurait un dépassement de stack si la classe `License` a aussi une référence à la classe` User`. Cela signifie que nous devrions avoir une référence à sens unique. (?) Comment devrions-nous décider laquelle des propriétés de navigation devrait être supprimée?

Collections: Peu importe

Il existe une différence nette entre les collections et les références en tant que propriétés de navigation. Une référence est une entité. Une collection contient des entités. Cela signifie que l’initialisation d’une collection n’a pas de sens en termes de logique métier: elle ne définit pas une association entre entités. Définir une référence fait.

Il est donc préférable de savoir si vous initialisez ou non les listes intégrées.

En ce qui concerne le “comment”, certaines personnes préfèrent l’initialisation paresseuse:

 private ICollection
_addresses; public virtual ICollection
Addresses { get { return this._addresses ?? (this._addresses = new HashSet
()); }

Il empêche les exceptions de référence NULL, ce qui facilite les tests unitaires et la manipulation de la collection, mais empêche également une initialisation inutile. Ce dernier peut faire la différence quand une classe a relativement beaucoup de collections. L’inconvénient est qu’il faut relativement beaucoup de plomberie, en particulier. par rapport aux propriétés automatiques sans initialisation. En outre, l’avènement de l’opérateur de propagation nulle en C # a rendu l’initialisation des propriétés de collection moins urgente.

… sauf si un chargement explicite est appliqué

La seule chose est que l’initialisation des collections rend difficile de vérifier si une collection a été chargée par Entity Framework. Si une collection est initialisée, une déclaration comme …

 var users = context.Users.ToList(); 

… créera User objects User ayant des collections d’ Addresses vides et non nulles (chargement différé mis de côté). Vérifier si la collection est chargée nécessite du code comme …

 var user = users.First(); var isLoaded = context.Entry(user).Collection(c => c.Addresses).IsLoaded; 

Si la collection n’est pas initialisée, une vérification null simple fera l’affaire. Ainsi, lorsque le chargement explicite sélectif est une partie importante de votre pratique de codage, c’est-à-dire …

 if (/*check collection isn't loaded*/) context.Entry(user).Collection(c => c.Addresses).Load(); 

… il peut être plus pratique de ne pas initialiser les propriétés de la collection.

Propriétés de référence: Ne pas

Les propriétés de référence sont des entités. Par conséquent, leur atsortingbuer un object vide est significatif .

Pire encore, si vous les lancez dans le constructeur, EF ne les écrasera pas lors de la matérialisation de votre object ou par chargement paresseux. Ils auront toujours leurs valeurs initiales jusqu’à ce que vous les remplaciez activement . Pire encore, vous pouvez même finir par enregistrer des entités vides dans la firebase database!

Et il y a un autre effet: la correction de la relation ne se produira pas. Le correctif de relation est le processus par lequel EF connecte toutes les entités du contexte par leurs propriétés de navigation. Lorsqu’un User et une Licence sont chargés séparément, User.License sera toujours User.License et vice versa. Sauf si bien sûr, si la License été initialisée dans le constructeur. Cela est également vrai pour les associations 1: n. Si Address initialise un User dans son constructeur, User.Addresses ne sera pas User.Addresses !

Entity Framework core

Le correctif de relation dans le cœur d’Entity Framework (2.1 au moment de la rédaction) n’est pas affecté par les propriétés de navigation de référence initialisées dans les constructeurs. Autrement dit, lorsque les utilisateurs et les adresses sont extraits de la firebase database séparément, les propriétés de navigation sont renseignées.
Toutefois, le chargement différé ne remplace pas les propriétés de navigation de référence initialisées. En conclusion, dans EF-core également, l’initialisation des propriétés de navigation de référence dans les constructeurs peut causer des problèmes. Ne le fais pas Cela n’a aucun sens de toute façon,

Dans tous mes projets, je suis la règle – “Les collections ne doivent pas être nulles. Elles sont vides ou ont des valeurs.”

Le premier exemple est possible lorsque la création de ces entités relève du code tiers (par exemple, ORM) et que vous travaillez sur un projet de courte durée.

Le deuxième exemple est meilleur, car

  • vous êtes sûr que l’entité a toutes les propriétés définies
  • vous évitez idiot NullReferenceException
  • vous rendez les consommateurs de votre code plus heureux

Les personnes, qui pratiquent la conception pilotée par le domaine, exposent les collections en lecture seule et évitent les parameters sur celles-ci. (voir Quelle est la meilleure pratique pour les listes en lecture seule dans NHibernate )

Q1: Lequel est le meilleur? Pourquoi? Avantages et inconvénients?

Il est préférable d’exposer les colonnes non nulles, car vous évitez des vérifications supplémentaires dans votre code (par exemple, les Addresses ). C’est un bon contrat à avoir dans votre base de code. Mais il est OK pour moi d’exposer une référence nullable à une seule entité (par exemple, License )

Q2: Dans la seconde approche, il y aurait un dépassement de stack si la classe de License fait également référence à la classe User . Cela signifie que nous devrions avoir une référence à sens unique. (?) Comment devrions-nous décider laquelle des propriétés de navigation devrait être supprimée?

Lorsque j’ai développé moi-même un modèle de mappeur de données, j’ai essayé d’éviter les références bidirectionnelles et j’ai très rarement fait référence à un enfant.

Lorsque j’utilise des ORM, il est facile d’avoir des références bidirectionnelles.

Lorsqu’il est nécessaire de créer une entité de test pour mes tests unitaires avec un ensemble de référence bidirectionnel, suivez les étapes suivantes:

  1. Je construis parent entity avec la children collection emty children collection .
  2. Ensuite, j’ajoute un child avec une référence à parent entity dans la children collection .

L’installation d’un constructeur sans paramètre dans le type de License I nécessiterait une propriété user .

 public class License { public License(User user) { this.User = user; } public int Id { get; set; } public ssortingng Key { get; set; } public DateTime Expirtion { get; set; } public virtual User User { get; set; } } 

Il est inutile de changer la liste, car votre POCO dépend du chargement paresseux.

Le chargement différé est le processus par lequel une entité ou un ensemble d’entités est automatiquement chargé à partir de la firebase database la première fois qu’une propriété faisant référence à l’entité / aux entités est accédée. Lors de l’utilisation de types d’entité POCO, le chargement différé est réalisé en créant des instances de types de proxy dérivés, puis en remplaçant les propriétés virtuelles pour append le crochet de chargement.

Si vous supprimez le modificateur virtuel, vous désactivez le chargement différé et, dans ce cas, votre code ne fonctionne plus (car rien n’initialise la liste).

Notez que le chargement différé est une fonctionnalité prise en charge par le framework d’entités, si vous créez la classe en dehors du contexte d’un DbContext, le code dépendant souffrirait évidemment d’une NullReferenceException

HTH

Q1: Lequel est le meilleur? Pourquoi? Avantages et inconvénients

La deuxième variante lorsque les propriétés virtuelles sont définies dans un constructeur d’entité présente un problème défini appelé ” Appel de membre virtuel dans un constructeur “.

Comme pour la première variante sans initialisation des propriétés de navigation, il existe 2 situations selon qui / ce qui crée un object:

  1. Framework d’entité crée un object
  2. Le consommateur de code crée un object

La première variante est parfaitement valide lorsque Entity Framework crée un object, mais peut échouer lorsqu’un consommateur de code crée un object.

La solution pour garantir qu’un consommateur de code crée toujours un object valide consiste à utiliser une méthode d’usine statique :

  1. Rendre le constructeur par défaut protégé. Entity Framework permet de travailler avec des constructeurs protégés.

  2. Ajouter une méthode de fabrique statique qui crée un object vide, par exemple un object User , définit toutes les propriétés, par exemple, les Addresses et la License , après la création et renvoie un object User entièrement construit User

De cette manière, Entity Framework utilise un constructeur protégé par défaut pour créer un object valide à partir des données obtenues à partir de certaines sources de données et le code consommateur utilise une méthode de fabrique statique pour créer un object valide.

J’utilise la réponse de cette question Pourquoi ma collection de proxy Entity Framework Code First est-elle nulle et pourquoi ne puis-je pas la définir?

A eu des problèmes avec l’initialisation du constructeur. La seule raison pour laquelle je fais cela est de faciliter le code de test. Faire en sorte que la collecte ne soit jamais nulle me permet d’initialiser constamment dans les tests, etc.

Les autres réponses répondent parfaitement à la question, mais j’aimerais append quelque chose car cette question est toujours pertinente et apparaît dans les recherches Google.

Lorsque vous utilisez l’assistant “Code premier modèle à partir de la firebase database” dans Visual Studio, toutes les collections sont initialisées comme suit:

 public partial class SomeEntity { [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] public SomeEntity() { OtherEntities = new HashSet(); } public int Id { get; set; } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] public virtual ICollection OtherEntities { get; set; } } 

J’ai tendance à prendre la sortie de l’assistant comme étant une recommandation officielle de Microsoft, d’où pourquoi j’ajoute à cette question vieille de cinq ans. Par conséquent, HashSet toutes les collections en tant que HashSet .

Et personnellement, je pense que ce serait bien de peaufiner ce qui précède pour tirer parti des initialiseurs de propriété automatique de C # 6.0:

  public virtual ICollection OtherEntities { get; set; } = new HashSet();