2014-06-06 65 views
19

实体框架代码首先,当我声明实体时,我必须使用DbSet类型的属性。例如:DbSet <>和虚拟DbSet <>有什么区别?

public DbSet<Product> Products { get; set; } 
public DbSet<Customer> Customers { get; set; } 

最近我遇到DbSet <>声明为虚拟。

public virtual DbSet<Product> Products { get; set; } 
public virtual DbSet<Customer> Customers { get; set; } 

有什么区别?什么EF功能启用?

回答

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

导航属性中的virtual关键字用于启用延迟加载机制,但必须启用配置的LazyLoadingEnabled属性。

AccountType :: AccountCodes导航属性上的virtual关键字会在db context仍处于活动状态时以编程方式访问该属性的那一刻加载所有帐户代码。

using (var context = new AppContext()) 
{ 
    var accountType = context.AccountTypes.FirstOrDefault(); 
    var accountCodes = accountType.AccountCodes; 
} 

虽然被用于测试目的(嘲笑DbSet属性),虚拟关键字在这种情况下所导出的DbContext类virtual关键字(虚拟DbSet <>)是不相关的延迟加载。

=====更新=====

一般我们在做反对服务/逻辑测试,例如我们有帐户类型服务如下的另一层。并且该服务通过构造函数使用某种依赖注入来接受db上下文实例。

public class AccountTypeService 
{ 
    public AppContext _context; 

    public AccountTypeService(AppContext context) 
    { 
     _context = context; 
    } 

    public AccountType AddAccountType(string name) 
    { 
     var accountType = new AccountType { Id = Guid.NewGuid(), Name = name }; 
     _context.AccountTypes.Add(accountType); 
     _context.SaveChanges(); 
     return accountType; 
    } 
} 

现在我们需要测试帐户类型服务,在这种情况下,我使用mstest和automoq来创建模拟类。

[TestClass] 
public class AccountTypeServiceTest 
{ 
    [TestMethod] 
    public void AddAccountType_NormalTest() 
    { 
     // Arranges. 
     var accountTypes = new List<AccountType>(); 
     var accountTypeSetMock = new Mock<DbSet<AccountType>>(); 
     accountTypeSetMock.Setup(m => m.Add(It.IsAny<AccountType>())).Callback<AccountType>(accountType => accountTypes.Add(accountType)); 

     var appContextMock = new Mock<AppContext>(); 
     appContextMock.Setup(m => m.AccountTypes).Returns(accountTypeSetMock.Object); 
     var target = new AccountTypeService(appContextMock.Object); 

     // Acts. 
     var newAccountType = target.AddAccountType("test"); 

     // Asserts. 
     accountTypeSetMock.Verify(m => m.Add(It.IsAny<AccountType>()), Times.Once()); 
     appContextMock.Verify(m => m.SaveChanges(), Times.Once()); 
     Assert.AreEqual(1, accountTypes.Count); 
     Assert.IsNotNull(newAccountType); 
     Assert.AreNotEqual(Guid.Empty, newAccountType.Id); 
     Assert.AreEqual("test", newAccountType.Name); 
    } 
} 
0

注意,在EF核心(目前1.0和2.0)仍然不支持惰性加载方案,与“虚拟”或不所以使用不会使一个不同。

仅供参考。脚手架生成“虚拟”关键字可能在EF Core的未来版本中支持LazyLoading技术!

(据我所知在EF2.1开发团队可能会增加对惰性加载支持)

+0

小学虚拟意味着它可以在derriving类中重写。 – Nikolaus

相关问题