首页 文章

为什么在实体框架模型定义中使用'virtual'用于类属性?

提问于
浏览
197

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

5 回答

  • 222

    它允许实体框架围绕虚拟属性创建代理,以便属性可以支持延迟加载和更有效的更改跟踪 . 有关更全面的讨论,请参见What effect(s) can the virtual keyword have in Entity Framework 4.1 POCO Code First? .

    Edit to clarify "create a proxy around": By "create a proxy around"我具体指的是实体框架的作用 . 实体框架要求将导航属性标记为虚拟,以便支持延迟加载和高效更改跟踪 . 见Requirements for Creating POCO Proxies .
    实体框架使用继承来支持此功能,这就是为什么它要求在基类POCO中将某些属性标记为虚拟 . 它实际上创建了源自POCO类型的新类型 . 所以你的POCO作为实体框架's dynamically created subclasses. That'的基本类型,我的意思是"create a proxy around" .

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

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

    Edit to describe why properties would be marked as virtual

    属性如:

    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;
    

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

    public virtual ICollection<RSVP> RSVPs;
    
  • 71

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

  • 12

    C#中的 virtual 关键字允许子类重写方法或属性 . 欲了解更多信息,请参阅the MSDN documentation on the 'virtual' keyword

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

  • 21

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

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

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

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

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

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

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

  • 1

    将模型中的导航属性定义为虚拟是很常见的 . 当导航属性定义为虚拟时,它可以利用某些实体框架功能 . 最常见的是延迟加载 . 延迟加载是许多ORM的一个很好的功能,因为它允许您从模型中动态访问相关数据 . 在实际访问之前,它不会不必要地获取相关数据,从而减少了对数据库中数据的预先查询 .

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

相关问题