Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the difference between DbSet<> and virtual DbSet<>?

In Entity Framework Code First, when I declare entities I have to use DbSet<> type of properties for that. For example:

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

Recently I've met DbSet<> declared as virtual.

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

What is the difference? What EF functionalities is enabled?

like image 254
magos Avatar asked Jun 06 '14 10:06

magos


People also ask

What is DbSet <>?

A DbSet represents the collection of all entities in the context, or that can be queried from the database, of a given type. DbSet objects are created from a DbContext using the DbContext.

What is the difference between DbSet and DbContext?

Intuitively, a DbContext corresponds to your database (or a collection of tables and views in your database) whereas a DbSet corresponds to a table or view in your database. So it makes perfect sense that you will get a combination of both!

What is the use of virtual in Entity Framework?

In Entity Framework, using a virtual navigation property allows you to denote it as the equivalent of a nullable Foreign Key in SQL. You do not HAVE to eagerly join every keyed table when performing a query, but when you need the information -- it becomes demand-driven.

What is DbSet in Entity Framework Core?

In Entity Framework Core, the DbSet represents the set of entities. In a database, a group of similar entities is called an Entity Set. The DbSet enables the user to perform various operations like add, remove, update, etc. on the entity set.


1 Answers

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.

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

While the virtual keyword on the derived DbContext class (virtual DbSet<>) is used for testing purpose (mocking the DbSet property), virtual keyword in this case is not related to lazy loading.

===== update =====

Usually we are doing the testing against the service / logic, for example we have another layer for the account type service as follow. And the service accepts the db context instance using some kind of dependency injection through the constructor.

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

And now we need to test the account type service, in this case I used mstest and automoq to create the mock class.

[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);     } } 
like image 56
Yuliam Chandra Avatar answered Sep 18 '22 19:09

Yuliam Chandra