Warum sollte 'virtual' für Klasseneigenschaften in Entity Framework-Modelldefinitionen verwendet werden?

223

Im folgenden Blog: http://weblogs.asp.net/scottgu/archive/2010/07/16/code-first-development-with-entity-framework-4.aspx

Der Blog enthält das folgende Codebeispiel:

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

Was ist der Zweck virtualbeim Definieren einer Eigenschaft in einer Klasse? Welchen Effekt hat es?

Gary Jones
quelle
9
Möchten Sie den allgemeinen Zweck des Schlüsselworts "virtuell" in C # verstehen oder wissen, wie es speziell für Entity Framework gilt?
M.Babcock
2
@ M.Babcock: Ich frage, was der Zweck in Bezug auf Eigenschaften ist, weil ich das noch nie gesehen habe.
Gary Jones
1
Wenn Sie wissen, wie sich das virtuelle Schlüsselwort auf den Polymorphismus in Methoden auswirkt, gilt dies auch für Eigenschaften.
M.Babcock
20
@ M.Babcock: Wie hätte ich es deutlicher machen können? Die Frage trägt den Titel "Warum 'virtuell' für Eigenschaften in Klassen verwenden?".
Gary Jones
2
@ Gary - Getter / Setter-Eigenschaften werden tatsächlich statisch in Methoden kompiliert. Sie sind also keine traditionellen Klassenfelder wie "öffentliches virtuelles Abendessen";
Shan Plourde

Antworten:

248

Damit kann das Entity Framework einen Proxy um die virtuelle Eigenschaft erstellen, sodass die Eigenschaft das verzögerte Laden und eine effizientere Änderungsverfolgung unterstützen kann. Siehe Welche Auswirkungen kann das virtuelle Schlüsselwort in Entity Framework 4.1 POCO Code First haben? für eine gründlichere Diskussion.

Bearbeiten, um zu verdeutlichen, dass "Proxy erstellen": Mit "Proxy erstellen" verweise ich speziell auf die Funktionen des Entity Framework. Für das Entity Framework müssen Ihre Navigationseigenschaften als virtuell markiert sein, damit ein verzögertes Laden und eine effiziente Änderungsverfolgung unterstützt werden. Siehe Anforderungen zum Erstellen von POCO-Proxys .
Das Entity Framework verwendet Vererbung, um diese Funktionalität zu unterstützen. Aus diesem Grund müssen bestimmte Eigenschaften in den POCOs Ihrer Basisklasse als virtuell markiert werden. Es werden buchstäblich neue Typen erstellt, die von Ihren POCO-Typen abgeleitet sind. Ihr POCO fungiert also als Basistyp für die dynamisch erstellten Unterklassen des Entity Framework. Das habe ich mit "Proxy erstellen" gemeint.

Die dynamisch erstellten Unterklassen, die das Entity Framework erstellt, werden deutlich, wenn das Entity Framework zur Laufzeit und nicht zur statischen Kompilierungszeit verwendet wird. Und nur, wenn Sie die Funktionen zum verzögerten Laden oder Ändern von Entity Framework aktivieren. Wenn Sie die Funktionen zum verzögerten Laden oder Ändern der Nachverfolgung des Entity Frameworks (die nicht die Standardeinstellung sind) niemals verwenden möchten, müssen Sie keine Ihrer Navigationseigenschaften als virtuell deklarieren. Sie sind dann dafür verantwortlich, diese Navigationseigenschaften selbst zu laden, indem Sie entweder das verwenden, was das Entity Framework als "eifriges Laden" bezeichnet, oder verwandte Typen manuell über mehrere Datenbankabfragen hinweg abrufen. In vielen Szenarien können und sollten Sie jedoch Funktionen zum verzögerten Laden und Ändern der Nachverfolgung für Ihre Navigationseigenschaften verwenden.

Wenn Sie eine eigenständige Klasse erstellen und Eigenschaften als virtuell markieren und einfach Instanzen dieser Klassen in Ihrer eigenen Anwendung erstellen und verwenden würden, die vollständig außerhalb des Bereichs des Entity Frameworks liegen, würden Ihre virtuellen Eigenschaften Ihnen nichts davon bringen besitzen.

Bearbeiten, um zu beschreiben, warum Eigenschaften als virtuell markiert werden

Eigenschaften wie:

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

Sind keine Felder und sollten nicht als solche betrachtet werden. Diese werden als Getter und Setter bezeichnet und zur Kompilierungszeit in Methoden konvertiert.

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

Aus diesem Grund werden sie für die Verwendung im Entity Framework als virtuell markiert. Dadurch können die dynamisch erstellten Klassen die intern generierten getund setFunktionen überschreiben . Wenn Ihre Getter / Setter für Navigationseigenschaften in Ihrer Entity Framework-Verwendung für Sie arbeiten, versuchen Sie, sie auf Eigenschaften zu überarbeiten, neu zu kompilieren und zu überprüfen, ob das Entity Framework noch ordnungsgemäß funktioniert:

 public virtual ICollection<RSVP> RSVPs;
Shan Plourde
quelle
2
Was meinst du mit "Proxy erstellen"? Was ist hier eigentlich los?
Gary Jones
2
Hallo Gary, ich habe meine Antwort überarbeitet, um zu verdeutlichen, was ich unter "Proxy erstellen" verstehe. Hoffe das hilft ein bisschen.
Shan Plourde
2
Zu sagen "Eigenschaften ... sind keine Eigenschaften" ist nicht hilfreich. Alle Eigenschaften sind als Getter- und / oder Setter-Methoden implementiert, daher ist es nicht sinnvoll zu sagen, dass "diese Eigenschaft wirklich eine Getter- und Setter-Methode ist, keine Eigenschaft".
Ben Voigt
1
Vielen Dank für Ihr Feedback Ben, ich hätte klarstellen sollen, dass "Eigenschaften keine Felder sind". Lassen Sie mich wissen, wenn Sie weitere Rückmeldungen oder Fragen haben.
Shan Plourde
Ich habe den Wortlaut geändert und ein weiteres Codebeispiel hinzugefügt, um zu erklären, dass "Eigenschaften sind keine Eigenschaften" etwas besser. Bitte rollen Sie zurück, wenn Sie dies nicht möchten.
Scott Chamberlain
75

Das virtualSchlüsselwort in C # ermöglicht das Überschreiben einer Methode oder Eigenschaft durch untergeordnete Klassen. Weitere Informationen finden Sie in der MSDN-Dokumentation zum Schlüsselwort 'virtual'

UPDATE: Dies beantwortet die aktuell gestellte Frage nicht, aber ich lasse sie hier für alle, die nach einer einfachen Antwort auf die ursprüngliche , nicht beschreibende Frage suchen .

M.Babcock
quelle
23
@Hooch Dies wird nicht als korrekt markiert, da das, was als "korrekt" eingestuft wird, nicht nur vom Fragentitel abhängt. Ich stelle mir vor, dass die meisten Menschen, ich selbst und OP eingeschlossen, sich zuerst virtualüber Entity Framework mit Eigenschaften befassen - auch wenn dies im Titel von OP nicht explizit angegeben ist. Die akzeptierte Antwort ist so, weil sie die Entity Framework-Seite der Dinge berührt und wie / warum virtualEigenschaften in diesem Kontext verwendet werden.
Don Cheadle
22

Ich verstehe die Frustration der OP, diese Verwendung von virtuell ist nicht für die Vorlagenabstraktion gedacht, für die der virtuelle Modifikator defacto wirksam ist.

Wenn noch jemand damit zu kämpfen hat, würde ich meinen Standpunkt darlegen, da ich versuche, die Lösungen einfach und den Jargon auf ein Minimum zu beschränken:

Das Entity Framework in einem einfachen Teil verwendet das verzögerte Laden, was dem Vorbereiten von etwas für die zukünftige Ausführung entspricht. Das passt zum "virtuellen" Modifikator, aber dazu gehört noch mehr.

Wenn Sie in Entity Framework eine virtuelle Navigationseigenschaft verwenden, können Sie diese als Äquivalent eines nullbaren Fremdschlüssels in SQL bezeichnen. Sie MÜSSEN nicht eifrig jeder Schlüsseltabelle beitreten, wenn Sie eine Abfrage ausführen, aber wenn Sie die Informationen benötigen, werden sie bedarfsgesteuert.

Ich habe auch nullable erwähnt, da viele Navigationseigenschaften zunächst nicht relevant sind. dh In einem Kunden- / Bestellszenario müssen Sie nicht bis zu dem Moment warten, in dem eine Bestellung bearbeitet wird, um einen Kunden zu erstellen. Sie können, aber wenn Sie einen mehrstufigen Prozess hatte dies zu erreichen, können Sie die Notwendigkeit finden , beharren die Kundendaten für eine spätere Fertigstellung oder für die Bereitstellung auf zukünftige Aufträge. Wenn alle Nav-Eigenschaften implementiert wären, müssten Sie beim Speichern jeden Fremdschlüssel und jedes relationale Feld einrichten. Das setzt die Daten wirklich nur wieder in den Speicher zurück, was die Rolle der Persistenz zunichte macht.

Obwohl es in der tatsächlichen Ausführung zur Laufzeit kryptisch erscheint, habe ich festgestellt, dass die beste Faustregel lautet: Wenn Sie Daten ausgeben (in ein Ansichtsmodell oder ein serialisierbares Modell einlesen) und Werte vor Referenzen benötigen, tun Sie dies nicht benutze virtuell; Wenn in Ihrem Bereich Daten erfasst werden, die möglicherweise unvollständig sind oder gesucht werden müssen und nicht alle für eine Suche abgeschlossenen Suchparameter erforderlich sind, verwendet der Code die Referenz gut, ähnlich wie bei der Verwendung von Eigenschaften mit nullbaren Werten int? lange?. Das Abstrahieren Ihrer Geschäftslogik von Ihrer Datenerfassung bis zum Einfügen hat viele Leistungsvorteile, ähnlich wie das Instanziieren eines Objekts und das Starten bei Null. Entity Framework verwendet viel Reflexion und Dynamik, was die Leistung beeinträchtigen kann. Die Notwendigkeit eines flexiblen Modells, das an die Anforderungen angepasst werden kann, ist für die Verwaltung der Leistung von entscheidender Bedeutung.

Für mich war das immer sinnvoller als die Verwendung von überladenem Fachjargon wie Proxies, Delegierten, Handlern und dergleichen. Sobald Sie Ihre dritte oder vierte Programmiersprache erreicht haben, kann es mit diesen unordentlich werden.

Nathan Teague
quelle
14

Es ist durchaus üblich, Navigationseigenschaften in einem Modell als virtuell zu definieren. Wenn eine Navigationseigenschaft als virtuell definiert ist, kann sie bestimmte Entity Framework-Funktionen nutzen. Am häufigsten wird faul geladen.

Das verzögerte Laden ist eine nette Funktion vieler ORMs, da Sie damit dynamisch auf verwandte Daten eines Modells zugreifen können. Die zugehörigen Daten werden nicht unnötig abgerufen, bis tatsächlich auf sie zugegriffen wird, wodurch die Vorababfrage von Daten aus der Datenbank verringert wird.

Aus dem Buch "ASP.NET MVC 5 mit Bootstrap und Knockout.js"

Hassan Rahman
quelle
3

Wenn Sie im Kontext von EF eine Eigenschaft als virtuell markieren, kann EF sie verzögert laden. Damit das verzögerte Laden funktioniert, muss EF ein Proxy-Objekt erstellen, das Ihre virtuellen Eigenschaften mit einer Implementierung überschreibt, die die referenzierte Entität beim ersten Zugriff lädt. Wenn Sie die Eigenschaft nicht als virtuell markieren, funktioniert das verzögerte Laden nicht.

Shakeer Hussain
quelle
0

Das virtuelle Schlüsselwort wird verwendet, um eine Methode, eine Eigenschaft, einen Indexer oder eine Ereignisdeklaration zu ändern und zu ermöglichen, dass sie in einer abgeleiteten Klasse überschrieben wird. Diese Methode kann beispielsweise von jeder Klasse überschrieben werden, die sie erbt:

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

Sie können den virtuellen Modifikator nicht mit den statischen, abstrakten, privaten oder Überschreibungsmodifikatoren verwenden. Das folgende Beispiel zeigt eine virtuelle Eigenschaft:

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";
            }
        }
    }
}
FatalMan
quelle
Dies ist völlig außer Thema, Bruder.
Eru
0

Wir können nicht über virtuelle Mitglieder sprechen, ohne auf Polymorphismus Bezug zu nehmen . Tatsächlich ermöglicht eine Funktion, Eigenschaft, ein Indexer oder ein Ereignis in einer als virtuell gekennzeichneten Basisklasse das Überschreiben einer abgeleiteten Klasse.

Standardmäßig sind Mitglieder einer Klasse nicht virtuell und können nicht als solche gekennzeichnet werden, wenn statische, abstrakte, private oder Überschreibungsmodifikatoren verwendet werden.

Beispiel Betrachten wir die ToString () -Methode in System.Object . Da diese Methode Mitglied von System.Object ist, wird sie von allen Klassen geerbt und stellt allen die ToString () -Methoden zur Verfügung.

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

Die Ausgabe des vorherigen Codes lautet:

VirtualMembersArticle.Company

Angenommen, wir möchten das Standardverhalten der von System.Object in unserer Company-Klasse geerbten ToString () -Methoden ändern. Um dieses Ziel zu erreichen, reicht es aus, das Schlüsselwort override zu verwenden, um eine weitere Implementierung dieser Methode zu deklarieren.

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

Wenn nun eine virtuelle Methode aufgerufen wird, sucht die Laufzeit in ihrer abgeleiteten Klasse nach einem überschreibenden Mitglied und ruft es auf, falls vorhanden. Die Ausgabe unserer Anwendung lautet dann:

Name: Microsoft

Wenn Sie die System.Object-Klasse überprüfen, werden Sie feststellen, dass die Methode als virtuell markiert ist.

namespace System
{
    [NullableContextAttribute(2)]
    public class Object
    {
        ....
        public virtual string? ToString();
        ....
    }
}
Ivan Porta
quelle