Pourquoi utiliser «virtuel» pour les propriétés de classe dans les définitions de modèle Entity Framework?


223

Dans le blog suivant: http://weblogs.asp.net/scottgu/archive/2010/07/16/code-first-development-with-entity-framework-4.aspx

Le blog contient l'exemple de code suivant:

public class Dinner
{
   public int DinnerID { get; set; }
   public string Title { get; set; }
   public DateTime EventDate { get; set; }
   public string Address { get; set; }
   public string HostedBy { get; set; }
   public virtual ICollection<RSVP> RSVPs { get; set; }
}

public class RSVP
{
   public int RsvpID { get; set; }
   public int DinnerID { get; set; }
   public string AttendeeEmail { get; set; }
   public virtual Dinner Dinner { get; set; }
}

Quel est le but de l'utilisation virtuallors de la définition d'une propriété dans une classe? Quel effet cela a-t-il?


9
Demandez-vous de comprendre l'objectif général du mot clé «virtuel» en C # ou comment il se rapporte spécifiquement à Entity Framework?
M.Babcock

2
@ M.Babcock: Je demande quel est le but en ce qui concerne les propriétés, car je n'ai jamais vu cela auparavant.
Gary Jones

1
Si vous savez comment le mot-clé virtuel affecte le polymorphisme dans les méthodes, il en va de même pour les propriétés.
M.Babcock

20
@ M.Babcock: comment aurais-je pu le rendre plus évident? La question est intitulée "Pourquoi utiliser 'virtuel' pour les propriétés des classes?".
Gary Jones

2
@Gary - les propriétés getter / setter sont en fait compilées statiquement en méthodes. Ce ne sont donc pas des domaines de classe traditionnels comme le «dîner virtuel public»;
Shan Plourde

Réponses:


248

Il permet à Entity Framework de créer un proxy autour de la propriété virtuelle afin que la propriété puisse prendre en charge le chargement différé et un suivi des modifications plus efficace. Voir Quel (s) effet (s) le mot-clé virtuel peut-il avoir dans Entity Framework 4.1 POCO Code First? pour une discussion plus approfondie.

Modifier pour clarifier "créer un proxy autour": Par "créer un proxy autour", je me réfère spécifiquement à ce que fait Entity Framework. Entity Framework nécessite que vos propriétés de navigation soient marquées comme virtuelles afin que le chargement paresseux et le suivi efficace des modifications soient pris en charge. Voir Exigences pour la création de proxys POCO .
Entity Framework utilise l'héritage pour prendre en charge cette fonctionnalité, c'est pourquoi il nécessite que certaines propriétés soient marquées virtuelles dans vos POCO de classe de base. Il crée littéralement de nouveaux types qui dérivent de vos types POCO. Votre POCO agit donc comme un type de base pour les sous-classes créées dynamiquement d'Entity Framework. C'est ce que je voulais dire par «créer un proxy autour».

Les sous-classes créées dynamiquement que Entity Framework crée deviennent évidentes lors de l'utilisation de Entity Framework au moment de l'exécution, et non au moment de la compilation statique. Et seulement si vous activez le chargement paresseux de l'Entity Framework ou les fonctionnalités de suivi des modifications. Si vous choisissez de ne jamais utiliser les fonctions de chargement différé ou de suivi des modifications de Entity Framework (qui n'est pas la valeur par défaut), vous n'avez pas besoin de déclarer l'une de vos propriétés de navigation comme virtuelle. Vous êtes ensuite responsable du chargement de ces propriétés de navigation vous-même, soit en utilisant ce que Entity Framework appelle le «chargement rapide», soit en récupérant manuellement les types associés dans plusieurs requêtes de base de données. Cependant, vous pouvez et devez utiliser les fonctionnalités de chargement paresseux et de suivi des modifications pour vos propriétés de navigation dans de nombreux scénarios.

Si vous deviez créer une classe autonome et marquer des propriétés comme virtuelles, et simplement construire et utiliser des instances de ces classes dans votre propre application, complètement en dehors de la portée d'Entity Framework, alors vos propriétés virtuelles ne vous apporteraient rien posséder.

Modifier pour décrire pourquoi les propriétés seraient marquées comme virtuelles

Des propriétés telles que:

 public ICollection<RSVP> RSVPs { get; set; }

Ne sont pas des domaines et ne doivent pas être considérés comme tels. Celles-ci sont appelées getters et setters et au moment de la compilation, elles sont converties en méthodes.

//Internally the code looks more like this:
public ICollection<RSVP> get_RSVPs()
{
    return _RSVPs;
}

public void set_RSVPs(RSVP value)
{
    _RSVPs = value;
}

private RSVP _RSVPs;

C'est pourquoi ils sont marqués comme virtuels pour une utilisation dans Entity Framework, cela permet aux classes créées dynamiquement de remplacer les fonctions getet les setfonctions générées en interne . Si votre getter / setters de propriétés de navigation fonctionne pour vous dans votre utilisation d'Entity Framework, essayez de les réviser en propriétés, recompilez-les et voyez si Entity Framework est toujours en mesure de fonctionner correctement:

 public virtual ICollection<RSVP> RSVPs;

2
Qu'entendez-vous par «créer un proxy autour»? Que se passe-t-il réellement ici?
Gary Jones

2
Bonjour Gary, j'ai révisé ma réponse pour clarifier ce que je veux dire par "créer un proxy autour". J'espère que ça aidera un peu.
Shan Plourde

2
Dire "les propriétés ... ne sont pas des propriétés" est tout à fait inutile. Toutes les propriétés sont implémentées en tant que méthodes getter et / ou setter, donc cela n'a aucun sens de dire "cette propriété est vraiment une méthode getter et setter pas une propriété".
Ben Voigt

1
Merci pour vos commentaires Ben, j'aurais dû préciser que "les propriétés ne sont pas des champs". Faites-moi savoir si vous avez d'autres commentaires ou questions.
Shan Plourde

J'ai changé le libellé et ajouté un autre exemple de code pour aider à expliquer un peu mieux les "propriétés ne sont pas des propriétés", veuillez revenir en arrière si vous ne le souhaitez pas.
Scott Chamberlain le

75

Le virtualmot clé en C # permet à une méthode ou à une propriété d'être remplacée par des classes enfants. Pour plus d'informations, reportez-vous à la documentation MSDN sur le mot-clé «virtuel»

MISE À JOUR: Cela ne répond pas à la question telle qu'elle est actuellement posée, mais je la laisserai ici pour quiconque cherche une réponse simple à la question d' origine non descriptive posée.


23
@Hooch ceci n'est pas marqué comme correct parce que ce qui est considéré comme "correct" ne dépend pas simplement du titre de la question. J'imagine que la plupart des gens, moi et OP inclus, traitent d'abord les virtualpropriétés via Entity Framework - même si ce n'est pas explicite dans le titre de OP. La réponse acceptée est ainsi car elle touche au côté Entity Framework des choses, et comment / pourquoi les virtualpropriétés sont utilisées dans ce contexte.
Don Cheadle

22

Je comprends la frustration des OP, cette utilisation du virtuel n'est pas pour l'abstraction basée sur des modèles pour laquelle le modificateur virtuel de facto est efficace.

Si certains luttent toujours avec cela, je voudrais offrir mon point de vue, car j'essaie de garder les solutions simples et le jargon au minimum:

Entity Framework dans une pièce simple utilise le chargement paresseux, ce qui équivaut à préparer quelque chose pour une exécution future. Cela correspond au modificateur «virtuel», mais il y a plus à cela.

Dans Entity Framework, l'utilisation d'une propriété de navigation virtuelle vous permet de la désigner comme l'équivalent d'une clé étrangère nullable en SQL. Vous n'avez PAS à joindre avec impatience chaque table à clés lorsque vous effectuez une requête, mais lorsque vous avez besoin des informations, elles dépendent de la demande.

J'ai également mentionné nullable car de nombreuses propriétés de navigation ne sont pas pertinentes au premier abord. Par exemple, dans un scénario client / commandes, vous n'avez pas à attendre le moment où une commande est traitée pour créer un client. Vous pouvez, mais si vous aviez un processus en plusieurs étapes pour y parvenir, vous pourriez trouver la nécessité de conserver les données client pour une exécution ultérieure ou pour le déploiement sur des commandes futures. Si toutes les propriétés de navigation ont été implémentées, vous devez établir chaque clé étrangère et champ relationnel lors de la sauvegarde. Cela remet vraiment les données en mémoire, ce qui détruit le rôle de la persistance.

Donc, même si cela peut sembler cryptique dans l'exécution réelle au moment de l'exécution, j'ai trouvé que la meilleure règle empirique à utiliser serait: si vous générez des données (lecture dans un modèle de vue ou un modèle sérialisable) et que vous avez besoin de valeurs avant les références, ne le faites pas utiliser virtuel; Si votre portée recueille des données qui peuvent être incomplètes ou un besoin de recherche et ne nécessitent pas tous les paramètres de recherche complétés pour une recherche, le code fera bon usage de la référence, similaire à l'utilisation des propriétés de valeur nullable int? longue?. En outre, l'abstraction de votre logique métier de votre collecte de données jusqu'à la nécessité de l'injecter présente de nombreux avantages en termes de performances, similaires à l'instanciation d'un objet et son démarrage à null. Entity Framework utilise beaucoup de réflexion et de dynamique, ce qui peut dégrader les performances, et la nécessité d'avoir un modèle flexible pouvant s'adapter à la demande est essentielle à la gestion des performances.

Pour moi, cela avait toujours plus de sens que d'utiliser un jargon technologique surchargé comme les mandataires, les délégués, les gestionnaires et autres. Une fois que vous avez atteint votre troisième ou quatrième langue de programmation, cela peut devenir compliqué.


14

Il est assez courant de définir des propriétés de navigation dans un modèle pour qu'elles soient virtuelles. Lorsqu'une propriété de navigation est définie comme virtuelle, elle peut tirer parti de certaines fonctionnalités d'Entity Framework. Le plus courant est le chargement paresseux.

Le chargement paresseux est une fonctionnalité intéressante de nombreux ORM car il vous permet d'accéder dynamiquement aux données connexes d'un modèle. Il ne récupérera pas inutilement les données associées jusqu'à ce qu'elles soient réellement accédées, réduisant ainsi l'interrogation initiale des données de la base de données.

Extrait du livre "ASP.NET MVC 5 avec Bootstrap et Knockout.js"


3

Dans le contexte d'EF, le marquage d'une propriété comme virtuelle permet à EF d'utiliser le chargement différé pour la charger. Pour que le chargement paresseux fonctionne, EF doit créer un objet proxy qui remplace vos propriétés virtuelles avec une implémentation qui charge l'entité référencée lors de son premier accès. Si vous ne marquez pas la propriété comme virtuelle, le chargement paresseux ne fonctionnera pas avec.


0

Le mot-clé virtuel est utilisé pour modifier une méthode, une propriété, un indexeur ou une déclaration d'événement et permettre son remplacement dans une classe dérivée. Par exemple, cette méthode peut être remplacée par n'importe quelle classe qui en hérite:

public virtual double Area() 
{
    return x * y;
}

Vous ne pouvez pas utiliser le modificateur virtuel avec les modificateurs statiques, abstraits, privés ou de remplacement. L'exemple suivant montre une propriété virtuelle:

class MyBaseClass
{
    // virtual auto-implemented property. Overrides can only
    // provide specialized behavior if they implement get and set accessors.
    public virtual string Name { get; set; }

    // ordinary virtual property with backing field
    private int num;
    public virtual int Number
    {
        get { return num; }
        set { num = value; }
    }
}


class MyDerivedClass : MyBaseClass
{
    private string name;

    // Override auto-implemented property with ordinary property
    // to provide specialized accessor behavior.
    public override string Name
    {
        get
        {
            return name;
        }
        set
        {
            if (value != String.Empty)
            {
                name = value;
            }
            else
            {
                name = "Unknown";
            }
        }
    }
}

C'est totalement hors sujet bro.
Eru

0

Nous ne pouvons pas parler de membres virtuels sans faire référence au polymorphisme . En fait, une fonction, une propriété, un indexeur ou un événement dans une classe de base marquée comme virtuelle permettra le remplacement d'une classe dérivée.

Par défaut, les membres d'une classe ne sont pas virtuels et ne peuvent pas être marqués comme cela s'il s'agit de modificateurs statiques, abstraits, privés ou de substitution.

Exemple Considérons la méthode ToString () dans System.Object . Parce que cette méthode est membre de System.Object, elle est héritée dans toutes les classes et fournira les méthodes ToString () à toutes.

namespace VirtualMembersArticle
{
    public class Company
    {
        public string Name { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Company company = new Company() { Name = "Microsoft" };
            Console.WriteLine($"{company.ToString()}");
            Console.ReadLine();
        }   
    }
}

La sortie du code précédent est:

VirtualMembersArticle.Company

Considérons que nous voulons changer le comportement standard des méthodes ToString () héritées de System.Object dans notre classe Company. Pour atteindre cet objectif, il suffit d'utiliser le mot clé override pour déclarer une autre implémentation de cette méthode.

public class Company
{
    ...
    public override string ToString()
    {
        return $"Name: {this.Name}";
    }         
}

Désormais, lorsqu'une méthode virtuelle est invoquée, le moteur d'exécution recherche un membre prioritaire dans sa classe dérivée et l'appelle s'il est présent. Le résultat de notre application sera alors:

Name: Microsoft

En fait, si vous vérifiez la classe System.Object, vous constaterez que la méthode est marquée comme virtuelle.

namespace System
{
    [NullableContextAttribute(2)]
    public class Object
    {
        ....
        public virtual string? ToString();
        ....
    }
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.