为什么在Entity Framework模型定义中使用'virtual'作为类属性?

Gar*_*nes 214 c# virtual properties class

在以下博客中: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; }
}
Run Code Online (Sandbox Code Playgroud)

virtual在类中定义属性时使用的目的是什么?它有什么影响?

Sha*_*rde 240

它允许实体框架围绕虚拟属性创建代理,以便该属性可以支持延迟加载和更有效的更改跟踪.请参阅虚拟关键字在Entity Framework 4.1 POCO Code First中可以有哪些效果?进行更彻底的讨论.

编辑以澄清"创建代理": 通过"创建代理",我具体指的是实体框架的功能.实体框架要求将导航属性标记为虚拟,以便支持延迟加载和高效更改跟踪.请参阅创建POCO代理的要求.
实体框架使用继承来支持此功能,这就是为什么它要求在基类POCO中将某些属性标记为虚拟.它从字面上创建了源自POCO类型的新类型.因此,您的POCO充当了Entity Framework动态创建的子类的基本类型.这就是我所说的"创造代理".

在运行时使用Entity Framework时,Entity Framework创建的动态创建的子类变得明显,而不是在静态编译时.并且仅当您启用实体框架的延迟加载或更改跟踪功能时.如果您选择永远不使用实体框架的延迟加载或更改跟踪功能(这不是默认设置),则您无需将任何导航属性声明为虚拟.然后,您负责自己加载这些导航属性,使用实体框架所指的"急切加载",或者在多个数据库查询中手动检索相关类型.在许多情况下,您可以而且应该为导航属性使用延迟加载和更改跟踪功能.

如果您要创建一个独立类并将属性标记为虚拟,并且只是在您自己的应用程序中构建和使用这些类的实例,完全超出实体框架的范围,那么您的虚拟属性将无法获得任何关于它们的内容.拥有.

编辑以描述为什么属性将标记为虚拟

属性如:

 public ICollection<RSVP> RSVPs { get; set; }
Run Code Online (Sandbox Code Playgroud)

不是字段,不应该被认为是这样.这些被称为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;
Run Code Online (Sandbox Code Playgroud)

这就是为什么它们被标记为虚拟以供在实体框架中使用,它允许动态创建的类覆盖内部生成的getset函数.如果您的导航属性getter/setter在您的Entity Framework用法中正在为您工作,请尝试将它们修改为仅属性,重新编译,并查看实体框架是否仍能正常运行:

 public virtual ICollection<RSVP> RSVPs;
Run Code Online (Sandbox Code Playgroud)

  • “在周围创建代理”是什么意思?这到底是怎么回事? (2认同)
  • 嗨,加里,我修改了我的答案以澄清我所说的“在周围创建代理”的意思。希望那些对你有帮助。 (2认同)
  • 说"属性......不是属性"是非常无益的.所有属性都实现为getter和/或setter方法,因此说"此属性实际上是getter和setter方法而不是属性"是没有意义的. (2认同)
  • 感谢您的反馈,Ben,我应该澄清“属性不是字段”。如果您有任何其他反馈或问题,请告诉我。 (2认同)

M.B*_*ock 72

virtualC#中的关键字使子类可以覆盖方法或属性.有关更多信息,请参阅有关'virtual'关键字的MSDN文档

更新:这不能回答当前要求的问题,但是我会留在这里给任何寻找原始的非描述性问题的简单答案的人.

  • @Hooch这没有被标记为正确,因为被认为是"正确的"并不仅仅取决于问题标题.我想大多数人,包括我自己和OP,首先通过实体框架来处理"虚拟"属性 - 即使它在OP的标题中并不明确.接受的答案是这样的,因为它涉及实体框架方面的事情,以及在该上下文中如何/为什么使用`virtual`属性. (22认同)

Nat*_*gue 20

我理解OP的挫败感,虚拟的这种用法不适用于defacto虚拟修饰符有效的模板化抽象.

如果有任何人仍在努力解决这个问题,我会提出我的观点,因为我试图将解决方案简单化并将行话保持在最低限度:

实体框架在一个简单的部分确实利用延迟加载,这相当于为将来执行准备一些东西.这适合'虚拟'修饰符,但还有更多.

在Entity Framework中,使用虚拟导航属性允许您将其表示为SQL中可为空的外键的等效项.在执行查询时,您不必急切地加入每个键控表,但是当您需要信息时 - 它变得需求驱动.

我还提到了nullable,因为许多导航属性一开始并不相关.即在客户/订单方案中,您不必等到订单处理创建客户的那一刻.您可以,但如果您有一个多阶段流程来实现这一目标,您可能会发现需要保留客户数据以便以后完成或部署到将来的订单.如果实现了所有导航属性,则必须在保存时建立每个外键和关系字段.这实际上只是将数据设置回内存,从而破坏了持久性的作用.

因此虽然在运行时的实际执行中看起来有些神秘,但我发现使用的最佳经验法则是:如果输出数据(读入View模型或可序列化模型)并且在引用之前需要值,请不要使用虚拟; 如果您的范围是收集可能不完整或需要搜索的数据而不需要为搜索完成每个搜索参数,那么代码将充分利用引用,类似于使用可空值属性int?长?.此外,从数据集合中抽象业务逻辑直到需要注入它具有许多性能优势,类似于实例化对象并在null处启动它.实体框架使用大量的反射和动态,这会降低性能,并且需要具有可扩展到需求的灵活模型对于管理性能至关重要.

对我来说,这总是比使用代理,委托,处理程序等重载技术术语更有意义.一旦你达到你的第三或第四编程语言,它可能会弄乱这些.


Has*_*man 13

将模型中的导航属性定义为虚拟是很常见的.当导航属性定义为虚拟时,它可以利用某些实体框架功能.最常见的是延迟加载.

延迟加载是许多ORM的一个很好的功能,因为它允许您从模型中动态访问相关数据.在实际访问之前不会不必要地获取相关数据,从而减少了对数据库中数据的预先查询.

从书"ASP.NET MVC 5与Bootstrap和Knockout.js"


Sha*_*ain 12

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

  • 我喜欢这个简洁的答案。 (2认同)