Perché usare "virtuale" per le proprietà della classe nelle definizioni del modello Entity Framework?


223

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

Il blog contiene il seguente esempio di codice:

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; }
}

Qual è lo scopo dell'utilizzo virtualquando si definisce una proprietà in una classe? Che effetto ha?


9
Stai chiedendo di comprendere lo scopo generale della parola chiave "virtuale" in C # o come riguarda specificamente Entity Framework?
M.Babcock,

2
@ M.Babcock: chiedo quale sia lo scopo per quanto riguarda le proprietà, perché non l'ho mai visto prima.
Gary Jones,

1
Se hai familiarità con il modo in cui la parola chiave virtuale influenza il polimorfismo nei metodi, è la stessa per le proprietà.
M.Babcock,

20
@ M.Babcock: come avrei potuto renderlo più evidente? La domanda si intitola "Perché usare 'virtual' per le proprietà nelle classi?".
Gary Jones,

2
@Gary - le proprietà getter / setter sono effettivamente compilate staticamente in metodi. Quindi non sono campi di classe tradizionali come la "cena virtuale pubblica";
Shan Plourde,

Risposte:


248

Consente a Entity Framework di creare un proxy attorno alla proprietà virtuale in modo che la proprietà possa supportare il caricamento lento e un rilevamento delle modifiche più efficiente. Vedere Quali effetti possono avere prima la parola chiave virtuale nel codice POCO di Entity Framework 4.1? per una discussione più approfondita.

Modifica per chiarire "crea un proxy in giro": per "crea un proxy in giro" mi riferisco specificamente a ciò che fa Entity Framework. Entity Framework richiede che le proprietà di navigazione siano contrassegnate come virtuali in modo da supportare il caricamento lento e il rilevamento efficiente delle modifiche. Vedi Requisiti per la creazione di proxy POCO .
Entity Framework utilizza l'ereditarietà per supportare questa funzionalità, motivo per cui richiede che determinate proprietà siano contrassegnate come virtuali nei POCO della classe base. Crea letteralmente nuovi tipi che derivano dai tuoi tipi POCO. Quindi il tuo POCO agisce come un tipo di base per le sottoclassi create dinamicamente di Entity Framework. Questo è ciò che intendevo per "creare un proxy in giro".

Le sottoclassi create dinamicamente che Entity Framework crea diventano evidenti quando si utilizza Entity Framework in fase di esecuzione, non al momento della compilazione statica. E solo se si abilita il caricamento lento di Entity Framework o si modificano le funzionalità di rilevamento. Se si sceglie di non utilizzare mai il caricamento lento o modificare le funzionalità di rilevamento di Entity Framework (che non è l'impostazione predefinita), non è necessario dichiarare virtuali le proprietà di navigazione. Sei quindi responsabile del caricamento di tali proprietà di navigazione da solo, utilizzando ciò che Entity Framework definisce "caricamento desideroso" o recuperando manualmente i tipi correlati in più query di database. Tuttavia, puoi e dovresti utilizzare il caricamento lento e modificare le funzionalità di monitoraggio per le tue proprietà di navigazione in molti scenari.

Se dovessi creare una classe autonoma e contrassegnare le proprietà come virtuali e semplicemente costruire e utilizzare istanze di tali classi nella tua stessa applicazione, completamente al di fuori dell'ambito di Entity Framework, le tue proprietà virtuali non ti guadagnerebbero nulla sulla loro proprio.

Modifica per descrivere perché le proprietà verrebbero contrassegnate come virtuali

Proprietà come:

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

Non sono campi e non dovrebbero essere considerati tali. Questi sono chiamati getter e setter e al momento della compilazione vengono convertiti in metodi.

//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;

Ecco perché sono contrassegnati come virtuali per l'uso in Entity Framework, consente alle classi create dinamicamente di sovrascrivere le funzioni gete le setfunzioni generate internamente . Se il tuo getter / setter delle proprietà di navigazione funziona per te nell'utilizzo di Entity Framework, prova a modificarle in sole proprietà, ricompilare e vedere se Entity Framework è ancora in grado di funzionare correttamente:

 public virtual ICollection<RSVP> RSVPs;

2
Cosa intendi con "creare un proxy in giro"? Cosa sta succedendo qui?
Gary Jones,

2
Ciao Gary, ho rivisto la mia risposta per chiarire cosa intendo per "creare un proxy in giro". Spero che ti aiuti un po '.
Shan Plourde,

2
Dire "proprietà ... non sono proprietà" non è molto utile. Tutte le proprietà sono implementate come metodi getter e / o setter, quindi non ha senso dire "questa proprietà è in realtà un metodo getter e setter non una proprietà".
Ben Voigt,

1
Grazie per il tuo feedback Ben, avrei dovuto chiarire che "le proprietà non sono campi". Fammi sapere se hai altri feedback o domande.
Shan Plourde,

Ho modificato il testo e ho aggiunto un altro esempio di codice per aiutare a spiegare un po 'meglio le "proprietà non sono proprietà", si prega di tornare indietro se non lo si desidera.
Scott Chamberlain,

75

La virtualparola chiave in C # consente a un metodo o una proprietà di essere sovrascritti dalle classi figlio. Per ulteriori informazioni, consultare la documentazione MSDN sulla parola chiave "virtuale"

AGGIORNAMENTO: Questo non risponde alla domanda come viene attualmente posta, ma la lascerò qui per chiunque cerchi una risposta semplice alla domanda originale , non descrittiva.


23
@Hooch questo non è contrassegnato come corretto perché ciò che è considerato "corretto" non dipende semplicemente dal titolo della domanda. Immagino che la maggior parte delle persone, me e OP inclusi, affrontino prima le virtualproprietà attraverso Entity Framework - anche se non è esplicito nel titolo di OP. La risposta accettata è perché tocca il lato Entity Framework delle cose e come / perché le virtualproprietà vengono utilizzate in quel contesto.
Don Cheadle,

22

Capisco la frustrazione dei PO, questo uso del virtuale non è per l'astrazione ispirata per cui il modificatore virtuale defacto è efficace.

Se qualcuno sta ancora lottando con questo, vorrei offrire il mio punto di vista, mentre cerco di mantenere le soluzioni semplici e il gergo al minimo:

Entity Framework in un semplice pezzo utilizza un caricamento lento, che equivale a preparare qualcosa per l'esecuzione futura. Questo si adatta al modificatore "virtuale", ma c'è di più in questo.

In Entity Framework, l'utilizzo di una proprietà di navigazione virtuale consente di denotarlo come equivalente di una chiave esterna nullable in SQL. Non DEVI unirti con entusiasmo a tutte le tabelle con chiave durante l'esecuzione di una query, ma quando hai bisogno delle informazioni, queste diventano basate sulla domanda.

Ho anche menzionato nullable perché all'inizio molte proprietà di navigazione non sono rilevanti. cioè in uno scenario cliente / ordini, non è necessario attendere fino al momento in cui un ordine viene elaborato per creare un cliente. Puoi, ma se hai avuto un processo in più fasi per raggiungere questo obiettivo, potresti trovare la necessità di conservare i dati dei clienti per un successivo completamento o per la distribuzione in ordini futuri. Se fossero state implementate tutte le proprietà di navigazione, dovresti stabilire ogni chiave esterna e campo relazionale sul salvataggio. Questo in realtà riporta i dati in memoria, il che sconfigge il ruolo della persistenza.

Quindi, sebbene possa sembrare criptico nell'esecuzione effettiva in fase di esecuzione, ho trovato che la migliore regola empirica da utilizzare sarebbe: se si stanno producendo dati (lettura in un modello di visualizzazione o modello serializzabile) e sono necessari valori prima dei riferimenti, non usa virtuale; Se il tuo ambito sta raccogliendo dati che potrebbero essere incompleti o necessari per la ricerca e che non richiedono tutti i parametri di ricerca completati per una ricerca, il codice farà buon uso del riferimento, simile all'utilizzo delle proprietà con valore nulla int? lungo?. Inoltre, l'astrazione della logica aziendale dalla raccolta dei dati fino alla necessità di iniettarla presenta numerosi vantaggi in termini di prestazioni, simili all'istanza di un oggetto e all'avvio da null. Entity Framework utilizza molte riflessioni e dinamiche, che possono degradare le prestazioni e la necessità di disporre di un modello flessibile in grado di adattarsi alle esigenze è fondamentale per la gestione delle prestazioni.

Per me, ha sempre avuto più senso che usare un gergo tecnologico sovraccarico come delegati, delegati, gestori e simili. Una volta raggiunto il terzo o il quarto lang di programmazione, può diventare confuso con questi.


14

È abbastanza comune definire virtuali le proprietà di navigazione in un modello. Quando una proprietà di navigazione viene definita come virtuale, può sfruttare alcune funzionalità di Entity Framework. Il più comune è il caricamento lento.

Il caricamento lento è una funzione interessante di molti ORM perché consente di accedere in modo dinamico ai dati correlati da un modello. Non recupererà inutilmente i dati correlati fino a quando non saranno effettivamente accessibili, riducendo così l'interrogazione iniziale dei dati dal database.

Dal libro "ASP.NET MVC 5 con Bootstrap e Knockout.js"


3

Nel contesto di EF, contrassegnare una proprietà come virtuale consente a EF di utilizzare il caricamento lento per caricarla. Affinché il caricamento lento funzioni, EF deve creare un oggetto proxy che sovrascrive le proprietà virtuali con un'implementazione che carica l'entità referenziata al primo accesso. Se non contrassegni la proprietà come virtuale, il caricamento lento non funzionerà con essa.


0

La parola chiave virtuale viene utilizzata per modificare un metodo, una proprietà, un indicizzatore o una dichiarazione di evento e consentire che venga sovrascritta in una classe derivata. Ad esempio, questo metodo può essere sovrascritto da qualsiasi classe che lo eredita:

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

Non è possibile utilizzare il modificatore virtuale con i modificatori statici, astratti, privati ​​o di sostituzione. L'esempio seguente mostra una proprietà virtuale:

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";
            }
        }
    }
}

Questo è totalmente fuori tema.
Eru,

0

Non possiamo parlare di membri virtuali senza fare riferimento al polimorfismo . In effetti, una funzione, proprietà, indicizzatore o evento in una classe base contrassegnata come virtuale consentirà l'override da una classe derivata.

Per impostazione predefinita, i membri di una classe non sono virtuali e non possono essere contrassegnati come modificatori statici, astratti, privati ​​o di sostituzione.

Esempio Consideriamo il metodo ToString () in System.Object . Perché questo metodo è un membro di System.Object è ereditato in tutte le classi e fornirà i metodi ToString () a tutti loro.

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();
        }   
    }
}

L'output del codice precedente è:

VirtualMembersArticle.Company

Consideriamo che vogliamo cambiare il comportamento standard dei metodi ToString () ereditati da System.Object nella nostra classe Company. Per raggiungere questo obiettivo è sufficiente utilizzare la parola chiave override per dichiarare un'altra implementazione di quel metodo.

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

Ora, quando viene invocato un metodo virtuale, il runtime verificherà la presenza di un membro prioritario nella sua classe derivata e lo chiamerà se presente. L'output della nostra applicazione sarà quindi:

Name: Microsoft

In effetti, se controlli la classe System.Object scoprirai che il metodo è contrassegnato come virtuale.

namespace System
{
    [NullableContextAttribute(2)]
    public class Object
    {
        ....
        public virtual string? ToString();
        ....
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.