Навіщо використовувати "віртуальний" для властивостей класу в визначеннях моделі Entity Framework?


223

У наступному блозі: http://weblogs.asp.net/scottgu/archive/2010/07/16/code-first-development-with-entity-framework-4.aspx

Блог містить такий зразок коду:

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

Яка мета використання virtualпри визначенні властивості в класі? Який ефект це має?


9
Ви просите зрозуміти загальне призначення "віртуального" ключового слова в C # або як воно стосується конкретно Entity Framework?
М.Бабкок

2
@ M. Babcock: Я запитую, яка мета, як це стосується властивостей, тому що я ніколи цього не бачив.
Гері Джонс

1
Якщо ви знайомі з тим, як віртуальне ключове слово впливає на поліморфізм методів, то це те саме для властивостей.
М.Бабкок

20
@ M.Babcock: як я міг зробити це більш очевидним? Питання має назву "Навіщо використовувати" віртуальний "для властивостей у класах?".
Гері Джонс

2
Властивості @Gary - getter / setter насправді статично складені в методи. Отже, вони не є традиційними класовими полями, такими як "громадська віртуальна вечеря";
Shan Plourde

Відповіді:


248

Це дозволяє Entity Framework створити проксі-сервер навколо віртуальної власності, щоб властивість може підтримувати ледачу завантаження та більш ефективне відстеження змін. Дивіться, який ефект (-ів) може мати віртуальне ключове слово в Entity Framework 4.1 Перш за все POCO Code? для більш ретельного обговорення.

Змінити, щоб уточнити "створити проксі навколо": Під "створити проксі навколо" я маю на увазі конкретно те, що робить Entity Framework. Entity Framework вимагає, щоб ваші навігаційні властивості були позначені як віртуальні, щоб підтримувалося ледаче завантаження та ефективне відстеження змін. Див. Вимоги до створення проксі-серверів POCO .
Entity Framework використовує успадковування для підтримки цієї функціональності, тому для певних властивостей потрібно позначати віртуальними у POCO базового класу. Він буквально створює нові типи, що походять від ваших типів POCO. Таким чином, ваш POCO виступає базовим типом динамічно створених підкласів Entity Framework. Це я мав на увазі під «створити проксі навколо».

Динамічно створені підкласи, створені Entity Framework, стають очевидними при використанні Entity Framework під час виконання, а не в статичний час компіляції. І лише в тому випадку, якщо ви ввімкнули ледачі функції відстеження Entity Framework або зміни. Якщо ви вирішили ніколи не використовувати функції відстежування завантаження чи зміни відстеження Entity Framework (що не є за замовчуванням), вам не потрібно оголошувати будь-які свої навігаційні властивості як віртуальні. Потім ви несете відповідальність за самостійне завантаження цих навігаційних властивостей, використовуючи те, що Entity Framework називає "нетерплячим завантаженням", або за ручне отримання відповідних типів у кількох запитах до бази даних. Ви можете та маєте використовувати функції лінивого завантаження та зміни функції відстеження для своїх властивостей навігації у багатьох сценаріях.

Якби ви створили окремий клас і позначили властивості як віртуальні, а просто сконструювали та використали екземпляри цих класів у власному додатку, повністю поза рамками Entity Framework, то ваші віртуальні властивості не принесуть вам нічого на їх власний.

Змінити, щоб описати, чому властивості будуть позначені як віртуальні

Такі властивості, як:

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

Не є полями і не слід їх думати як такі. Вони називаються геттерами та сеттерами, і під час компіляції вони перетворюються на методи.

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

Ось чому вони позначені як віртуальні для використання в Entity Framework, це дозволяє динамічно створюваним класам переосмислювати внутрішньо генеровані getта setфункції. Якщо ваші користувачі / налаштування навігації працюють для вас під час використання вашого Entity Framework, спробуйте переглянути їх лише для властивостей, перекомпілюйте та перевірте, чи здатна система Entity все ще функціонувати належним чином:

 public virtual ICollection<RSVP> RSVPs;

2
Що ви маєте на увазі під "створити проксі навколо"? Що насправді відбувається тут?
Гері Джонс

2
Привіт, Гері, я переглянув свою відповідь, щоб уточнити, що я маю на увазі під «створення проксі навколо». Сподіваюся, що трохи допомагає.
Shan Plourde

2
Говорити "властивості ... не властивості" - це зовсім недоцільно. Усі властивості реалізуються як методи getter та / або setter, тому немає сенсу говорити, що "ця властивість дійсно є методом getter і setter, а не властивістю".
Ben Voigt

1
Дякую за Ваш відгук Бен, я повинен був уточнити, що "властивості не поля". Повідомте мене, якщо у вас є інші відгуки або запитання.
Shan Plourde

Я змінив формулювання і додав ще один приклад коду, щоб допомогти пояснити "властивості не властивості" трохи краще, будь ласка, відкатуйтесь, якщо ви цього не хочете.
Скотт Чемберлен

75

virtualКлючове слово в C # дозволяє метод або властивість бути перевизначені дочірніми класами. Для отримання додаткової інформації зверніться до документації MSDN про "віртуальне" ключове слово

ОНОВЛЕННЯ: Це не відповідає на запитання, яке задається в даний час, але я залишу його для тих, хто шукає просту відповідь на оригінальне запитання, що не описується.


23
@Hooch це не позначається як правильне, оскільки те, що вважається "правильним", не залежить лише від назви питання. Я думаю, що більшість людей, включаючи мене та ОП, спочатку мають справу з virtualвластивостями через Entity Framework - навіть якщо це не є явним у назві ОП. Прийнята відповідь полягає в тому, що вона торкається речей Entity Framework, а також те, як / чому virtualвластивості використовуються в цьому контексті.
Дон Чідл

22

Я розумію розчарування ОП, це використання віртуального не для шаблонного абстрагування, для якого діє ефективний віртуальний модифікатор дефакто.

Якщо хтось із цим все ще бореться, я б запропонував свою точку зору, тому що намагаюся звести до мінімуму рішення прості та жаргон:

Entity Framework в простому творі використовує ледачий навантаження, що є еквівалентом підготування чогось до майбутнього виконання. Це відповідає «віртуальному» модифікатору, але до цього є більше.

В Entity Framework використання властивості віртуальної навігації дозволяє позначити її як еквівалент нульового зовнішнього ключа в SQL. Ви НЕ МОЖЕТЕ охоче приєднуватися до кожної заповненої таблиці під час виконання запиту, але коли вам потрібна інформація - вона стає залежною від попиту.

Я також згадав нульовий, оскільки багато навігаційних властивостей спочатку не актуальні. тобто у сценарії "Клієнт / Замовлення" вам не доведеться чекати до моменту обробки замовлення для створення замовника. Ви можете, але якщо у вас був багатоетапний процес для досягнення цього, можливо, вам доведеться зберігати дані клієнта для подальшого завершення або для розгортання для майбутніх замовлень. Якщо всі властивості nav були реалізовані, вам доведеться встановити кожне зовнішнє ключове та реляційне поле на збереженні. Це дійсно просто повертає дані назад в пам'ять, що перемагає роль стійкості.

Тож, хоча це може здатися загадковим у фактичному виконанні під час виконання, я виявив, що найкращим правилом, яким ви користуєтесь, було б: якщо ви виводите дані (читаючи в модель перегляду або серіалізаційну модель) і потребуєте значень перед посиланнями, не робіть використовувати віртуальний; Якщо у вашому масштабі є збір даних, які можуть бути неповними або потребує пошуку, і не потрібен кожен параметр пошуку, виконаний для пошуку, код добре використає посилання, аналогічно використанню властивостей зведеного значення int? довго?. Крім того, абстрагування вашої бізнес-логіки від збору даних до її необхідності вводити її має багато переваг від продуктивності, подібно до інстанції об'єкта та запуску його на нулі. Entity Framework використовує багато роздумів і динаміки, які можуть погіршити продуктивність, а необхідність створення гнучкої моделі, яка може масштабувати попит, є критично важливою для управління продуктивністю.

Для мене це завжди мало більше сенсу, ніж використання перевантаженого технічного жаргону, як проксі, делегати, обробники тощо. Як тільки ви натиснете третій чи четвертий язик програмування, він може зіпсуватися з ними.


14

Досить часто визначати навігаційні властивості в моделі, яка буде віртуальною. Коли властивість навігації визначається як віртуальна, вона може скористатися певною функціональністю Entity Framework. Найпоширеніший - ледаче навантаження.

Ледаче завантаження - хороша особливість багатьох ORM, оскільки дозволяє динамічно отримувати доступ до пов'язаних даних із моделі. Він не буде зайвим отримувати пов’язані дані, поки не буде фактично доступ до нього, тим самим зменшивши попередній запит даних із бази даних.

З книги "ASP.NET MVC 5 з Bootstrap і Knockout.js"


3

У контексті EF позначення властивості як віртуальної дозволяє EF використовувати ледаче завантаження для його завантаження. Для ледачого завантаження на роботу EF має створити проксі-об'єкт, який переосмислює ваші віртуальні властивості з реалізацією, яка завантажує посилається об'єкт під час його першого доступу. Якщо ви не позначите властивість як віртуальне, тоді ледаче завантаження не працюватиме з ним.


0

Віртуальне ключове слово використовується для зміни методу, властивості, індексатора або оголошення оголошень події та дозволяє його переосмислити у похідному класі. Наприклад, цей метод може бути замінений будь-яким класом, який його успадковує:

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

Ви не можете використовувати віртуальний модифікатор зі статичними, абстрактними, приватними або переопрацьованими модифікаторами. Наступний приклад показує віртуальну властивість:

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

Це зовсім поза темою брато.
Еру

0

Ми не можемо говорити про віртуальних членів, не звертаючись до поліморфізму . Фактично, функція, властивість, індексатор або подія в базовому класі, позначеному як віртуальний, дозволять переосмислити похідний клас.

За замовчуванням члени класу є невіртуальними і не можуть бути позначені такими, як статичні, абстрактні, приватні або переопрацьовані модифікатори.

Приклад Розглянемо метод ToString () в System.Object . Оскільки цей метод є членом System.Object, він успадковується у всіх класах і надасть методи ToString () для всіх.

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

Вихід попереднього коду:

VirtualMembersArticle.Company

Розглянемо, що ми хочемо змінити стандартну поведінку методів ToString (), успадкованих від System.Object у нашому класі компанії. Для досягнення цієї мети досить використовувати ключове слово override для оголошення іншої реалізації цього методу.

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

Тепер, коли викликається віртуальний метод, час виконання перевірить на переважаючий член у похідному класі та зателефонує, якщо він присутній. Результатом нашої програми буде:

Name: Microsoft

Насправді, якщо ви перевірите клас System.Object, ви побачите, що метод позначений як віртуальний.

namespace System
{
    [NullableContextAttribute(2)]
    public class Object
    {
        ....
        public virtual string? ToString();
        ....
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.