在以下博客: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的目的是什么?它有什么影响?


当前回答

谈论虚拟成员时不能不提到多态性。事实上,基类中标记为virtual的函数、属性、索引器或事件将允许从派生类重写。

默认情况下,类的成员是非虚的,并且不能被标记为静态、抽象、私有或覆盖修饰符。

例子 让我们考虑System.Object中的ToString()方法。因为这个方法是System的成员。对象,它在所有类中继承,并将为所有类提供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

假设我们想要改变从System继承的ToString()方法的标准行为。对象的Company类。要实现这个目标,使用override关键字声明该方法的另一个实现就足够了。

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

现在,当调用虚方法时,运行时将检查其派生类中的覆盖成员,如果存在则调用它。我们的应用程序的输出将是:

Name: Microsoft

事实上,如果你检查系统。对象类时,您会发现该方法被标记为virtual。

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

其他回答

它允许实体框架围绕虚拟属性创建一个代理,这样属性就可以支持延迟加载和更有效的更改跟踪。请参阅虚拟关键字在实体框架4.1 POCO代码中有什么影响?进行更深入的讨论。

Edit to clarify "create a proxy around": By "create a proxy around", I'm referring specifically to what the Entity Framework does. The Entity Framework requires your navigation properties to be marked as virtual so that lazy loading and efficient change tracking are supported. See Requirements for Creating POCO Proxies. The Entity Framework uses inheritance to support this functionality, which is why it requires certain properties to be marked virtual in your base class POCOs. It literally creates new types that derive from your POCO types. So your POCO is acting as a base type for the Entity Framework's dynamically created subclasses. That's what I meant by "create a proxy around".

The dynamically created subclasses that the Entity Framework creates become apparent when using the Entity Framework at runtime, not at static compilation time. And only if you enable the Entity Framework's lazy loading or change tracking features. If you opt to never use the lazy loading or change tracking features of the Entity Framework (which is not the default), then you needn't declare any of your navigation properties as virtual. You are then responsible for loading those navigation properties yourself, either using what the Entity Framework refers to as "eager loading", or manually retrieving related types across multiple database queries. You can and should use lazy loading and change tracking features for your navigation properties in many scenarios though.

如果您要创建一个独立的类并将属性标记为虚的,并在您自己的应用程序中简单地构造和使用这些类的实例,完全超出实体框架的范围,那么您的虚属性本身不会为您带来任何好处。

编辑以描述将属性标记为虚拟的原因

属性如:

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

不是场,不应该被认为是场。它们被称为getter和setter,在编译时,它们被转换为方法。

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

这就是为什么它们在实体框架中被标记为虚拟的原因;它允许动态创建的类覆盖内部生成的get和set函数。如果你的导航属性getter/setter在你的实体框架使用中为你工作,试着修改它们为属性,重新编译,看看实体框架是否仍然能够正常工作:

 public virtual ICollection<RSVP> RSVPs;

c#中的virtual关键字使方法或属性可以被子类覆盖。要了解更多信息,请参考MSDN文档中关于“virtual”关键字的文档

更新:这并没有回答目前提出的问题,但我将把它留在这里,供那些寻找原始的、非描述性问题的简单答案的人使用。

It’s quite common to define navigational properties in a model to be virtual. When a navigation property is defined as virtual, it can take advantage of certain Entity Framework functionality. The most common one is lazy loading. Lazy loading is a nice feature of many ORMs because it allows you to dynamically access related data from a model. It will not unnecessarily fetch the related data until it is actually accessed, thus reducing the up-front querying of data from the database.

摘自《ASP。NET MVC 5与Bootstrap和Knockout.js

我理解OPs的挫败感,虚拟的这种使用并不是为了模板化抽象,而事实上的虚拟修饰符是有效的。

如果有人还在为这个问题而挣扎,我将提供我的观点,因为我试图保持解决方案的简单性,并将术语降至最低:

实体框架在一个简单的部分中确实利用了惰性加载,这相当于为将来的执行做准备。这符合“虚拟”修饰符,但还有更多。

在实体框架中,使用虚拟导航属性允许您将其表示为等同于SQL中的可空外键。在执行查询时,您不必急切地连接每个键表,但当您需要信息时——它就变成了需求驱动的。

I also mentioned nullable because many navigation properties are not relevant at first. i.e. In a customer / Orders scenario, you do not have to wait until the moment an order is processed to create a customer. You can, but if you had a multi-stage process to achieve this, you might find the need to persist the customer data for later completion or for deployment to future orders. If all nav properties were implemented, you'd have to establish every Foreign Key and relational field on the save. That really just sets the data back into memory, which defeats the role of persistence.

So while it may seem cryptic in the actual execution at run time, I have found the best rule of thumb to use would be: if you are outputting data (reading into a View Model or Serializable Model) and need values before references, do not use virtual; If your scope is collecting data that may be incomplete or a need to search and not require every search parameter completed for a search, the code will make good use of reference, similar to using nullable value properties int? long?. Also, abstracting your business logic from your data collection until the need to inject it has many performance benefits, similar to instantiating an object and starting it at null. Entity Framework uses a lot of reflection and dynamics, which can degrade performance, and the need to have a flexible model that can scale to demand is critical to managing performance.

对我来说,这总是比使用过多的技术术语(如代理、委托、处理程序等)更有意义。一旦你接触到第三或第四个编程语言,这些就会变得很混乱。

在EF的上下文中,将属性标记为virtual允许EF使用延迟加载来加载它。为了使延迟加载工作,EF必须创建一个代理对象,该对象覆盖您的虚拟属性,并在首次访问时加载被引用实体的实现。如果您没有将属性标记为virtual,那么惰性加载将无法使用它。