实体框架:为什么虚拟关键字用于在DbContext中声明属性

时间:2017-10-09 11:34:13

标签: c# entity-framework

我很少使用EF。所以我有一个问题。如果我们不在dbset中使用virtual关键字,那么延迟加载将不起作用?

我从这个链接中读到了一篇文章https://stackoverflow.com/a/24114284/5314244  他们提供像

这样的代码
public class AppContext : DbContext
{
    public AppContext()
    {
        Configuration.LazyLoadingEnabled = true;
    }

    public virtual DbSet<AccountType> AccountTypes { get; set; }
}

public class AccountType
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public virtual ICollection<AccountCode> AccountCodes { get; set; }
}

public class AccountCode
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public Guid AccountTypeId { get; set; }
    public virtual AccountType AccountType { get; set; }
}
他们说:

The virtual keyword on the navigation properties are used to enable lazy loading mechanism, but the LazyLoadingEnabled property of the configuration must be enabled.

The virtual keyword on AccountType::AccountCodes navigation property will load all account codes the moment there is a programmatically access to that property while the db context are still alive

如果我们在没有虚拟关键字public DbSet<AccountType> AccountTypes { get; set; }的情况下声明此代码,那么此行execute var accountCodes = accountType.AccountCodes;将会发生什么?

将抛出错误或null将存储在accountCodes变量?

第二个问题EF中的默认值是什么?&gt;懒加载还是急切加载?有多少种类型的装载选项?

感谢

1 个答案:

答案 0 :(得分:0)

您想要实现的方法,延迟加载,意味着数据提供程序(在您的情况下为Entity Framework)在您需要时检索某些数据。例如,如果您访问所有帐户类型,则可能不需要访问这些帐户类型的所有帐户代码,因此从数据库中检索它们是不必要的。

如果您必须自己编写SQL,那么很可能会编写一个普通的select语句而不加入子关系:

SELECT * FROM AccountTypes;

另一方面,另一个用例可能需要访问这些数据,因此您最终会急切地加载它,从而导致连接操作......

SELECT * FROM AccountTypes JOIN AccountCodes ON AccountCodes.AccountTypeId = AccountTypes.Id;

......或依赖于Entity Framework的延迟加载能力。

后一种情况要求Entity Framework执行以下操作:首先,选择所有帐户类型,然后在您的用户代码访问帐户代码时,发出另一个select语句以检索相应帐户类型的代码。为此,Entity Framework需要一个钩子,即它必须知道你何时访问导航属性,这通常是不可能的,因为这需要在此属性的getter中需要额外的代码:

public class AccountType
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public virtual ICollection<AccountCode> AccountCodes
    { 
        get { /* Load child data here */ }
        set { /* ... */ }
    }
}

不幸的是,实体框架无法改变POCO类,因为它您的用户代码,但它可以做的是从您的类派生并覆盖该属性以注入它#39的神奇。为此,它创建了所谓的代理,即它在运行时创建一个派生自POCO类的类,如:

public class AccountTypeProxy : AccountType
{
    public override ICollection<AccountCode> AccountCodes
    { 
        get { /* Load child data here */ }
        set { /* ... */ }
    }
}

因此,当您检索帐户类型时,您获得的实际上是一堆代理实例,您可以在调试期间确认这些实例。

如果您不将virtual关键字添加到导航属性或密封该类,则Entity Framework无法覆盖该属性,或者根本无法从该类派生,因此延迟加载将无效。