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?
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.
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!
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.
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.
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); } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With