web-dev-qa-db-fra.com

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

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 d'utiliser virtual lors de la définition d'une propriété dans une classe? Quel est son effet?

205
Gary Jones

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

Modifier pour clarifier "créer un proxy autour": Par "créer un proxy autour", je fais spécifiquement référence à ce que fait Entity Framework. Entity Framework exige 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 Conditions requises pour la création de procurations POCO .
Entity Framework utilise l'héritage pour prendre en charge cette fonctionnalité. C'est pourquoi certaines propriétés doivent être marquées comme virtuelles dans vos POCO de classe de base. Il crée littéralement de nouveaux types dérivés de vos types POCO. Votre POCO agit donc comme un type de base pour les sous-classes créées de manière dynamique par Entity Framework. C'est ce que je voulais dire par "créer un proxy autour".

Les sous-classes créées dynamiquement créées par Entity Framework deviennent évidentes lors de l'utilisation d'Entity Framework au moment de l'exécution et non au moment de la compilation statique. Et uniquement si vous activez les fonctionnalités de suivi ou de chargement paresseux d'Entity Framework. Si vous choisissez de ne jamais utiliser le chargement paresseux ou les fonctionnalités de suivi des modifications d'Entity Framework (qui ne sont pas les paramètres par défaut), vous ne devez déclarer aucune 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, en utilisant soit ce que Entity Framework appelle "un 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 des 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 les propriétés comme virtuelles, et simplement construire et utiliser des instances de ces classes dans votre propre application, en dehors du cadre de Entity Framework, vos propriétés virtuelles ne vous rapporteraient rien. posséder.

Editer 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 champs 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 elles sont marquées comme virtuelles pour une utilisation dans Entity Framework. Elles permettent aux classes créées de manière dynamique de remplacer les fonctions get et set générées en interne. Si votre getter/setters de propriété de navigation fonctionne pour vous dans votre utilisation de Entity Framework, essayez de les modifier en propriétés uniquement, recompilez-les et voyez si Entity Framework peut toujours fonctionner correctement:

 public virtual ICollection<RSVP> RSVPs;
233
Shan Plourde

Le mot clé virtual de C # permet de remplacer une méthode ou une propriété par des classes enfant. Pour plus d'informations, veuillez vous référer à la documentation MSDN sur le mot clé 'virtual'

UPDATE: Cela ne répond pas à la question telle qu'elle est actuellement posée, mais je la laisserai ici à ceux qui recherchent une réponse simple à la question original , non descriptive posée.

71
M.Babcock

Je comprends la frustration des PO, cette utilisation du virtuel n’est pas destinée à l’abstraction basée sur un modèle pour laquelle le modificateur virtuel defacto est efficace.

Si certains ont encore des difficultés avec cela, je voudrais donner mon point de vue, en essayant 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 que 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 ne devez PAS associer avec empressement toutes les tables à clés lorsque vous effectuez une requête, mais lorsque vous avez besoin des informations, celles-ci 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. En d'autres termes, dans un scénario client/commandes, vous n'avez pas besoin d'attendre le moment où une commande est traitée pour créer un client. Vous pouvez le faire, mais si vous disposiez d'un processus en plusieurs étapes pour y parvenir, vous pourriez avoir besoin de persister les données du client pour les compléter ultérieurement ou pour les déployer sur des commandes futures. Si toutes les propriétés de navigation étaient implémentées, vous auriez à établir chaque clé étrangère et chaque champ relationnel lors de la sauvegarde. En réalité, cela ne fait que remettre les données en mémoire, ce qui annule le rôle de la persistance.

Ainsi, bien que cela puisse sembler cryptique lors de l'exécution réelle au moment de l'exécution, j'ai trouvé que la meilleure règle à utiliser serait la suivante: si vous exportez des données (lecture dans un modèle d'affichage ou un modèle sérialisable) et que vous avez besoin de valeurs avant références, ne le faites pas. utiliser virtuel; Si votre portée collecte des données pouvant être incomplètes ou nécessitant une recherche et ne nécessitant pas que tous les paramètres de recherche soient remplis pour une recherche, le code fera bon usage de la référence, comme si vous utilisiez des propriétés de valeur nullable int? longue?. En outre, extraire votre logique métier de votre collection de données jusqu'à ce que vous ayez besoin de l'injecter présente de nombreux avantages en termes de performances, comme l'instanciation d'un objet et son démarrage à la valeur null. Entity Framework utilise beaucoup de réflexion et de dynamique, ce qui peut nuire aux performances, et la nécessité de disposer d'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é, tel que des mandataires, des délégués, des gestionnaires, etc. Une fois que vous atteignez votre troisième ou quatrième langue de programmation, cela peut devenir compliqué.

20
Nathan Teague

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

Le chargement différé est une fonctionnalité intéressante de nombreux ORM car il vous permet d'accéder de manière dynamique aux données associées d'un modèle. Il ne récupérera pas inutilement les données associées jusqu'à ce que l'utilisateur y ait réellement accès, réduisant ainsi l'interrogation initiale des données de la base de données.

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

13
Hassan Rahman

Dans le contexte de EF, marquer une propriété comme virtuel 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 par 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 différé ne fonctionnera pas.

2
Shakeer Hussain

Le mot-clé virtual 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 toute classe qui en hérite:

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

Vous ne pouvez pas utiliser le modificateur virtuel avec les modificateurs static, abstract, private ou override. 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";
            }
        }
    }
}
1
FatalMan