Po co używać „wirtualnego” dla właściwości klas w definicjach modelu Entity Framework?

223

W następującym blogu: http://weblogs.asp.net/scottgu/archive/2010/07/16/code-first-development-with-entity-framework-4.aspx

Blog zawiera następujący przykładowy kod:

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

Jaki jest cel użycia virtualpodczas definiowania właściwości w klasie? Jaki to ma wpływ?

Gary Jones
źródło
9
Czy chcesz zrozumieć ogólny cel słowa kluczowego „wirtualnego” w języku C # lub jak odnosi się on konkretnie do Entity Framework?
M.Babcock
2
@ M.Babcock: Pytam, jaki jest cel, ponieważ dotyczy właściwości, ponieważ nigdy wcześniej tego nie widziałem.
Gary Jones
1
Jeśli znasz sposób, w jaki wirtualne słowo kluczowe wpływa na polimorfizm w metodach, jest ono takie samo dla właściwości.
M.Babcock,
19
@ M.Babcock: jak mogłem uczynić to bardziej oczywistym? Pytanie brzmi „Po co używać„ wirtualnego ”dla właściwości w klasach?”.
Gary Jones
2
@Gary - właściwości getter / setter są faktycznie kompilowane statycznie w metody. Nie są to więc tradycyjne pola klasowe, takie jak „publiczna wirtualna kolacja”;
Shan Plourde,

Odpowiedzi:

248

Pozwala Entity Framework na utworzenie proxy wokół wirtualnej właściwości, aby ta właściwość mogła obsługiwać leniwe ładowanie i bardziej wydajne śledzenie zmian. Zobacz Jakie efekty może mieć wirtualne słowo kluczowe w kodzie POCO Entity Framework 4.1? dla dokładniejszej dyskusji.

Edytuj, aby wyjaśnić „utwórz proxy wokół”: Poprzez „utwórz proxy wokół” Mam na myśli konkretnie to, co robi Entity Framework. Entity Framework wymaga, aby twoje właściwości nawigacyjne były oznaczone jako wirtualne, aby obsługiwane było opóźnione ładowanie i efektywne śledzenie zmian. Zobacz Wymagania dotyczące tworzenia proxy POCO .
Entity Framework wykorzystuje dziedziczenie do obsługi tej funkcji, dlatego wymaga, aby niektóre właściwości były oznaczone wirtualnie w POCO klasy podstawowej. Dosłownie tworzy nowe typy, które wywodzą się z typów POCO. Zatem Twoje POCO działa jako typ podstawowy dla dynamicznie tworzonych podklas Entity Framework. Właśnie to miałem na myśli mówiąc „utwórz proxy”.

Dynamicznie tworzone podklasy tworzone przez Entity Framework stają się widoczne, gdy używa się Entity Framework w czasie wykonywania, a nie w czasie kompilacji statycznej. I tylko jeśli włączysz leniwe ładowanie lub zmianę funkcji śledzenia w Entity Framework. Jeśli zdecydujesz się nigdy nie używać leniwych funkcji ładowania lub zmiany śledzenia Entity Framework (co nie jest ustawieniem domyślnym), nie musisz deklarować żadnych właściwości nawigacji jako wirtualnych. Następnie jesteś odpowiedzialny za samodzielne ładowanie tych właściwości nawigacji, albo za pomocą tego, co Entity Framework określa jako „chętne ładowanie”, lub za ręczne wyszukiwanie powiązanych typów w wielu zapytaniach do bazy danych. Możesz i powinieneś używać leniwych funkcji ładowania i zmiany śledzenia dla swoich właściwości nawigacyjnych w wielu scenariuszach.

Jeśli miałbyś stworzyć samodzielną klasę i oznaczyć właściwości jako wirtualne, a także po prostu skonstruować i użyć instancji tych klas we własnej aplikacji, całkowicie poza zakresem Entity Framework, to twoje wirtualne właściwości nie zyskałyby nic na ich posiadać.

Edytuj, aby opisać, dlaczego właściwości mają być oznaczone jako wirtualne

Właściwości takie jak:

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

Nie są polami i nie powinny być traktowane jako takie. Są to tak zwane metody pobierające i ustawiające, aw czasie kompilacji są konwertowane na metody.

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

Dlatego są oznaczone jako wirtualne do użytku w Entity Framework, pozwala dynamicznie tworzonym klasom przesłonić wewnętrznie generowane geti setfunkcje. Jeśli narzędzie pobierające / ustawiające właściwości nawigacji działa dla Ciebie w ramach korzystania z Entity Framework, spróbuj zmienić je tak, aby zawierały tylko właściwości, rekompiluj i sprawdź, czy Entity Framework jest w stanie nadal poprawnie działać:

 public virtual ICollection<RSVP> RSVPs;
Shan Plourde
źródło
2
Co rozumiesz przez „tworzenie proxy wokół”? Co tu się właściwie dzieje?
Gary Jones
2
Cześć Gary, poprawiłem swoją odpowiedź, aby wyjaśnić, co rozumiem przez „utwórz proxy”. Mam nadzieję, że to trochę pomoże.
Shan Plourde,
2
Mówienie „właściwości ... nie są właściwościami” jest dość nieprzydatne. Wszystkie właściwości są implementowane jako metody pobierające i / lub ustawiające, więc nie ma sensu mówić „ta właściwość jest tak naprawdę metodą pobierającą i ustawiającą, a nie właściwością”.
Ben Voigt
1
Dziękuję za opinię, Ben, powinienem był wyjaśnić, że „właściwości nie są polami”. Daj mi znać, jeśli masz jakieś uwagi lub pytania.
Shan Plourde,
Zmieniłem sformułowanie i dodałem inny przykład kodu, aby nieco lepiej wyjaśnić „właściwości nie są właściwościami”. Jeśli nie chcesz, wycofaj.
Scott Chamberlain,
75

Słowo virtualkluczowe w języku C # umożliwia zastąpienie metody lub właściwości przez klasy potomne. Aby uzyskać więcej informacji, zapoznaj się z dokumentacją MSDN słowa kluczowego „virtual”

AKTUALIZACJA: To nie odpowiada na pytanie, jakie jest obecnie zadawane, ale zostawię to tutaj każdemu, kto szuka prostej odpowiedzi na pierwotne , nieopisowe pytanie.

M.Babcock
źródło
23
@Hooch nie jest to oznaczone jako poprawne, ponieważ to, co jest uważane za „poprawne”, nie zależy tylko od tytułu pytania. Wyobrażam sobie, że większość ludzi, w tym ja i OP, najpierw zajmuje się virtualnieruchomościami za pomocą Entity Framework - nawet jeśli nie jest to wyraźnie określone w tytule OP. Przyjęta odpowiedź jest taka, ponieważ dotyczy ona elementów Entity Framework oraz tego, w jaki sposób / dlaczego virtualwłaściwości są używane w tym kontekście.
Don Cheadle
22

Rozumiem frustrację PO, to użycie wirtualnego nie jest przeznaczone do szablonowej abstrakcji, na którą działa modyfikator wirtualny defacto.

Jeśli ktoś nadal ma z tym problem, oferuję punkt widzenia, ponieważ staram się, aby rozwiązania były proste, a żargon minimalny:

Entity Framework w prostym kawałku wykorzystuje leniwe ładowanie, co jest odpowiednikiem przygotowania czegoś do przyszłego wykonania. To pasuje do modyfikatora „wirtualnego”, ale jest coś więcej.

W Entity Framework użycie wirtualnej właściwości nawigacji pozwala oznaczyć ją jako odpowiednik dopuszczalnego klucza obcego w SQL. Nie musisz chętnie dołączać do każdej tabeli z kluczami podczas wykonywania zapytania, ale kiedy potrzebujesz informacji - staje się to zależne od zapotrzebowania.

Wspomniałem również o wartości zerowej, ponieważ wiele właściwości nawigacji nie jest na początku istotnych. tzn. w scenariuszu klienta / Zamówienia nie trzeba czekać do momentu przetworzenia zamówienia, aby utworzyć klienta. Możesz, ale jeśli miałeś wieloetapowy proces, aby to osiągnąć, możesz potrzebować trwać dane klienta do późniejszego zakończenia lub do wdrożenia przyszłych zamówień. Jeśli wszystkie właściwości nawigacji zostałyby zaimplementowane, trzeba będzie ustanowić każde pole klucza obcego i pole relacyjne w składowaniu. To naprawdę po prostu przywraca dane do pamięci, co eliminuje rolę trwałości.

Chociaż może się to wydawać tajemnicze w rzeczywistym wykonaniu w czasie wykonywania, znalazłem najlepszą praktyczną zasadę: jeśli wysyłasz dane (wczytujesz do View Model lub Serializable Model) i potrzebujesz wartości przed referencjami, nie rób używać wirtualnego; Jeśli twój zakres zbiera dane, które mogą być niekompletne lub wymagają wyszukiwania i nie wymagają uzupełnienia każdego parametru wyszukiwania, kod dobrze wykorzysta odwołanie, podobnie jak użycie właściwości wartości zerowej int? długie?. Ponadto, abstrahując logikę biznesową od gromadzenia danych, dopóki nie trzeba jej wstrzykiwać, ma wiele korzyści związanych z wydajnością, podobnie jak tworzenie instancji obiektu i rozpoczynanie go od zera. Entity Framework wykorzystuje wiele refleksji i dynamiki, które mogą obniżyć wydajność, a potrzeba posiadania elastycznego modelu, który można skalować do zapotrzebowania, ma kluczowe znaczenie dla zarządzania wydajnością.

Dla mnie zawsze miało to większy sens niż używanie przeciążonego żargonu technologicznego, takiego jak serwery proxy, delegaci, osoby obsługujące i tym podobne. Gdy uderzysz w swój trzeci lub czwarty język programowania, może się z nimi bałaganić.

Nathan Teague
źródło
14

Dość często definiuje się właściwości nawigacyjne w modelu wirtualnym. Gdy właściwość nawigacji jest zdefiniowana jako wirtualna, może korzystać z niektórych funkcji Entity Framework. Najczęstszym z nich jest leniwe ładowanie.

Leniwe ładowanie jest fajną funkcją wielu ORM, ponieważ umożliwia dynamiczny dostęp do powiązanych danych z modelu. Nie będzie niepotrzebnie pobierać powiązanych danych, dopóki nie zostanie faktycznie uzyskany dostęp, zmniejszając w ten sposób wstępne zapytania danych z bazy danych.

Z książki „ASP.NET MVC 5 z Bootstrap i Knockout.js”

Hassan Rahman
źródło
3

W kontekście EF oznaczenie właściwości jako wirtualnej umożliwia EF użycie opóźnionego ładowania do załadowania. Aby leniwe ładowanie działało, EF musi utworzyć obiekt proxy, który przesłania właściwości wirtualne, za pomocą implementacji, która ładuje obiekt, do którego istnieje odwołanie, przy pierwszym dostępie. Jeśli nie oznaczysz właściwości jako wirtualnej, leniwe ładowanie nie będzie z nią działać.

Shakeer Hussain
źródło
0

Wirtualne słowo kluczowe służy do modyfikowania metody, właściwości, indeksatora lub deklaracji zdarzenia i umożliwia zastąpienie go w klasie pochodnej. Na przykład tę metodę można zastąpić dowolną dziedziczącą klasą:

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

Nie można używać modyfikatora wirtualnego z modyfikatorami statycznymi, abstrakcyjnymi, prywatnymi lub zastępującymi. Poniższy przykład pokazuje wirtualną właściwość:

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
źródło
To całkowicie nie na temat, stary.
Eru
0

Nie możemy rozmawiać o wirtualnych członkach bez odwoływania się do polimorfizmu . W rzeczywistości funkcja, właściwość, indeksator lub zdarzenie w klasie bazowej oznaczonej jako wirtualna pozwoli zastąpić klasę pochodną.

Domyślnie członkowie klasy nie są wirtualni i nie można ich oznaczyć jako modyfikatory statyczne, abstrakcyjne, prywatne lub zastępujące.

Przykład Rozważmy metodę ToString () w System.Object . Ponieważ ta metoda jest członkiem System.Object, jest dziedziczona we wszystkich klasach i zapewni metody ToString () wszystkim.

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

Dane wyjściowe poprzedniego kodu to:

VirtualMembersArticle.Company

Rozważmy, że chcemy zmienić standardowe zachowanie metod ToString () odziedziczonych z System.Object w naszej klasie Company. Aby osiągnąć ten cel, wystarczy użyć słowa kluczowego override, aby zadeklarować kolejną implementację tej metody.

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

Teraz, gdy wywoływana jest metoda wirtualna, środowisko wykonawcze sprawdzi, czy istnieje nadrzędny element w klasie pochodnej i wywoła ją, jeśli jest obecna. Dane wyjściowe naszej aplikacji będą wówczas:

Name: Microsoft

W rzeczywistości, jeśli zaznaczysz klasę System.Object, przekonasz się, że metoda jest oznaczona jako wirtualna.

namespace System
{
    [NullableContextAttribute(2)]
    public class Object
    {
        ....
        public virtual string? ToString();
        ....
    }
}
Ivan Porta
źródło