EF Core DbContext Concurrency Issue with XUnit Tests - c#

I seem to have a strange concurrency issue I can't seem to put my finger on.
When I construct my implementation of a DbContext I inject the Entities I want to be built by the modelbuilder (don't worry about why). This is only done once by my app at runtime and runs fine, but when I'm integration testing DB integration, I inject only the test entities I need for my InMemoryDatabase.
Now I seem to have a weird issue where two unit tests in different class files needing different entities seem to get crossed over.
I keep running the unit tests and the first test will pass, but the second test will fail saying that TestObjectB doesn't exist in the model. When I inspect the model it says TestObjectA exists instead, even though it wasn't injected on this test. So as if the implementation of the DataContext was static and overwritten...These are different files and fresh constructors for the context, I don't understand how they are crossing paths? If I run the unit test that fails on it's own, it passes.
Note the following code has been simplified for your view.
DB Context:
public class DataContext : DbContext
{
private readonly List<IEntity> _entities;
public DataContextA(List<IEntity> entities, DbContextOptions options) : base(options)
{
_entities = entities;
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
foreach (var entity in _entities)
{
modelBuilder.Entity(entity.GetType());
}
}
}
Test Implementation 1:
[Fact]
public void CheckUniqueFieldA()
{
var options = new DbContextOptionsBuilder<DataContext>();
options.UseInMemoryDatabase(Guid.NewGuid().ToString());
using (var context = new DataContext(new List<IEntity> { new TestObjectA() }, options.Options))
{
//Do Something
}
}
Test Implementation2:
[Fact]
public void CheckUniqueFieldB()
{
var options = new DbContextOptionsBuilder<DataContext>();
options.UseInMemoryDatabase(Guid.NewGuid().ToString());
using (var context = new DataContext(new List<IEntity> { new TestObjectB() }, options.Options))
{
//Do Something
}
}

The reason is EF Core model caching described in Alternating between multiple models with the same DbContext type documentation topic:
...the model caching mechanism EF uses to improve the performance by only invoking OnModelCreating once and caching the model.
By default EF assumes that for any given context type the model will be the same.
The link also contains an example how to solve it. You'd need to create custom implementation of IModelCacheKeyFactory interface and replace the default EF Core implementation using ReplaceService inside OnConfiguring. The implementation should return an object representing the unique cache key for a given DbContext instance. The default implementation simply returns context.GetType().

Related

Setting up an Autofixture for In-memory DbContext

I am currently trying to use Autofixture to create a pre-defined fixture as an implementation of ICustomization for ApplicationDbContext using In-Memory provider.
public class ApplicationDbContextFixture : ICustomization
{
public void Customize(IFixture fixture)
{
var specimenFactory = new SpecimenFactory<ApplicationDbContext>(CreateDbContext);
fixture.Customize<ApplicationDbContext>(
composer =>
composer.FromFactory(specimenFactory)
);
}
/// <summary>
/// Private factory method to create a new instance of <see cref="ApplicationDbContext"/>
/// </summary>
private ApplicationDbContext CreateDbContext()
{
var dbContextOptions = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseInMemoryDatabase("SomeDatabaseName")
.Options;
var dbContext = new ApplicationDbContext(dbContextOptions);
return dbContext;
}
}
Then, I will apply that customization to my Fixture as follows:
[Fact]
public void TestAddUsersToEmptyDatabase()
{
// Arrange
// Fixture for ApplicationDbContext
var fixture = FixtureFactory.CreateFixture();
var applicationDatabaseFixture = new ApplicationDbContextFixture();
fixture.Customize(applicationDatabaseFixture);
// Fixture for users
var randomUser = fixture.Create<AppUser>();
var normalUser = fixture.Create<AppUser>();
var adminUser = fixture.Create<AppUser>();
// Act & Assert
// Run the test against one instance of the context
// Use a clean instance of the context for each operation too
using (var dbContext = fixture.Create<ApplicationDbContext>())
{
Assert.Empty(dbContext.Users);
dbContext.Users.Add(randomUser);
dbContext.SaveChanges();
}
using (var dbContext = fixture.Create<ApplicationDbContext>())
{
dbContext.Users.AddRange(normalUser, adminUser);
dbContext.SaveChanges();
}
using (var dbContext = fixture.Create<ApplicationDbContext>())
{
Assert.NotEmpty(dbContext.Users);
Assert.NotNull(dbContext.Users.SingleOrDefault(_ => _.Id == randomUser.Id));
Assert.NotNull(dbContext.Users.SingleOrDefault(_ => _.Id == normalUser.Id));
Assert.NotNull(dbContext.Users.SingleOrDefault(_ => _.Id == adminUser.Id));
}
}
FixtureFactory.CreateFixture implementation
/// <summary>
/// Factory method to declare a single <see cref="IFixture"/> for unit tests applications
/// </summary>
internal static class FixtureFactory
{
internal static IFixture CreateFixture()
{
var fixture = new Fixture().Customize(
new AutoMoqCustomization { ConfigureMembers = true });
return fixture;
}
}
Now in my unit test, asserting the Assert.Empty(dbContext.Users); will throw System.NotImplementedException : The method or operation is not implemented. because the DbSet<AppUser> Users generated from Autofixture is a DynamicProxy.
See image dbContext.Users as DynamicProxy
Oddly enough if I inspect the breakpoints from the factory method (ie. CreateDbContext()) called from the fixture.Create<ApplicationDbContext>(), the DbSet Users is of the expected type.
See image dbContext.Users as InternalDbSet
Optionally, I do aware that I can replace all the usage of dbContext.Users to dbContext.Set<User>() and that would make the unit test pass but the problem is that in the actual class, I am using the dbContext.Users for IQueryables and database operations, so I still need to stick with it if possible.
Hence, I would need help to know why does AutoFixture used my factory method to generate the instance for my ApplicationDbContext but all the DbSet<> properties inside it are mocked when resolved by the ISpecimenBuilder. Is there a way to remedy this?
I've post the similar question in their Github but it has been not active recently, so i also asked here.
Kindly please understand I only started to use Autofixture 2 days ago. So if there's something that I write wrong or there's a misconception in any Design Patterns, please kindly wrote a comment so that I can take it as a lesson.
Update 1:
So i tried to use initialized a plain fixture without any AutoMoq customization (ie. fixture = new Fixture()) and this time it throws a AutoFixture.ObjectCreationExceptionWithPath exception, complaining that it is unable to resolve DbSet property within the ApplicationDbContext. At this point, I was thinking if anyone know how to use a Relay or ISpecimenBuilder to tell Autofixture to use/call/implement all DbSet<T> properties within the ApplicationDbContext with dbContext.Set<T> because that would work if I replace all usage of DbSets in my unit tests, but as I mentioned, all IQueryable are return from DbSets so i cannot simply just replace it in ApplicationDbContext.
Update 2:
I remove and simplify the creation of ApplicationDbContext from my factory method CreateDbContext() since it will cause confusion from the code complexity.
It is hard to understand what you are trying to achieve from your post.
I think what you actually need, is to test your code that happens to use EntityFramework.
If that's the case you might want to have a look at this library, I have created EntityFrameworkCore.AutoFixture. It uses the In-Memory database provider as well as SQLite in-memory provider.
Have a look at the readme for some code examples. If you have any questions drop me a message or open an issue on GitHub.

Implementing a Simple Repository, Unit of Work with Dependency Injection

I have been trying to create a Repository Pattern along with Dependency injection, But Looks like I am missing some simple step. Here is my code
public class HomeController
{
private readonly ILoggingRepository _loggingRepository;
public HomeController(ILoggingRepository loggingRepository)
{
_loggingRepository = loggingRepository;
}
public void MyMethod()
{
string message = "MyMessage Called";
_loggingRepository .LogMessage(message);
}
}
// ILoggingRepository.cs
public interface ILoggingRepository
{
void LogMessage(string message);
}
// LoggingRepository.cs
public class LoggingRepository : ILoggingRepository
{
public void LogMessage(string message)
{
using (var dbContext = new DbContext())
{
var serviceLog = new Log() { Message = message, Logged = DateTime.UtcNow };
dbContext.Logs.Add(serviceLog);
dbContext.SaveChanges();
}
}
}
This works perfectly all right so far, but the problem arises when i make more than one repository calls.
Now I know that Entity framework 6.0 has inbuilt unit of work representation so I didn't created a UnitofWork Interface or class
But the problem appears when I do something like this in two different transactions. Lets say
Area area = _areaRepository.GetArea(); // Line 1
area.Name = "NewArea"; // Line 2
_areaRepository.SaveArea(area); // Line 3
now because it _areaRepository creates a new DbContext in Line 3, it doesn't changes the name of area as it doesn't consider EntityState.Modified
I have to explicitly set that, which isn't correct.
So I guess I need to do all this in single Transaction, Where I am doing wrong here ?
What is the correct and best way to achieve this, Should I inject my DbContext also into the repository?
This is how I doit all times:
If dont use Repository or Unit of Work layers, because Entity Framework db Context already implements those patterns. So, I only have a Service layer:
public interface IBaseService<VO, ENT>{
IQueryable<VO> GetAll();
VO Get(object id);
}
public abstract class BaseService<VO, ENT> : IBaseService<VO, ENT>{
MyContext db;
public BaseService(MyContext db){
this.db = db;
}
public IQueryable<VO> GetAll(){
return db.Set<ENT>().ProjectTo<VO>();
}
}
A service class have a dbContext injected in the constructor. This classes are located in a Service library. Then, how the dbContext and the service are resolved is a problem of the project who will be using them. The ProjectTo method is an extension for IQueryable from the Automapper Nuget. For example:
A Windows Service needs all services instance in the same thread shares the same dbContext. So, in the windows service project, I use Ninject https://www.nuget.org/packages/Ninject/4.0.0-beta-0134, this library is a dependency resolver, wich I use to configure how dependencies are builded, creating a Kernel, like this:
var kernel = new StandardKernel();
kernel.Bind<MyContext>().ToSelf().InThreadScope();
kernel.Bind<IServiceImplInterface>().To<ServiceImplClass>().InThreadScope();
I you are creating a Web project, you will need to install a aditional nuget (Ninject.WebCommon, Ninject.Web.COmmon.WebHost, Ninject.MVC5) to provide a .InRequestScope() method to the binding configuration, like this:
var kernel = new StandardKernel();
kernel.Bind<MyContext>().ToSelf().InRequestScope();
kernel.Bind<IServiceImplInterface>().To<ServiceImplClass>().InRequestScope();
You need setup those kernel when the app startup. In a web project is in the global.asax, in a windows service project, should be in the Service constructor:
You can visit www.ninject.org/learn.html to learn more about ninject. But, there are othres like Autofac or Caste Windsor, it is up to you. If you like to keep using the repository pattern, just use Ninject inject them into the Service layer, like i did with the dbContext.
The best approach is to have one instance of DbContext, injecting it on each repository implementation. That way you will have a single instance of the database context, so EF will be able to detect changes on the entity objects.
If you need to use isolated dbContexts as in your example, then you need to explicitly set the state of the object as Modified.
Depending on the type of project, you should set the context on a specific scope. For example, for web applications one option is to use instance per Web request (per lifetime scope). Check this url where you can see a good explanation of the different instance scopes.
The using statement simply creates a new scope, executing the Dispose() method after the code block. EF does a lot on the background to maintain the UoW and state of the objects, but in your case, with the using, you are not using this fature.
First, a DbContext is a repository. If you want to wrap it in a custom repository, they should have the same lifecycle.
Second, your Unit-of-work is your controller. The repository should be scoped to unit-of-work.
This means that your repository needs to be Disposable, since the DbContext is.
So something like:
public interface ILoggingRepository : IDisposable
{
void LogMessage(string message);
}
// LoggingRepository.cs
public class LoggingRepository : ILoggingRepository
{
MyDbContext db;
public LoggingRepository(MyDbContext db)
{
this.db = db;
}
public void Dispose()
{
db.Dispose();
}
public void LogMessage(string message)
{
var serviceLog = new MonitoringServiceLog() { Message = message, Logged = DateTime.UtcNow };
db.MonitoringServiceLogs.Add(serviceLog);
db.SaveChanges();
}
}
If your ILoggingRepository wan't a database, it might be a file or something else that is expensive to create or open and needs to be closed.

ObjectContext Not Disposed

I'm having an issue in Entity Framework 6 where an exception is consistently thrown. For the most part the application works perfectly fine until I try adding a user to a role via a linking table.
The error being thrown is the following:
The relationship between the two objects cannot be defined because they are attached to different ObjectContext objects.
The functionality will happily add the user to the role virtually but as soon as SaveChanges() is called the process falls over.
I'm aware of the how and why for the above error and after doing some research it's due to the the context not being disposed of correctly. So following on from that and looking into the DbContext setup I've realised IDisposable wasn't added to the configuration. Unfortunately, no matter what I've tried incorporating IDisposable at any point within the application still doesn't dispose of the contexts correctly.
So after spending a fair bit of time and having no luck via Google I'm wondering if any of you have a solution or are able to point me in the right direction.
The following is a cutdown version of the Data Layer classes I've implemented:
public class GenericRepository<T> : WebsiteContext, IGenericRepository<T> where T : class
{
public virtual void Commit()
{
SaveChanges();
}
public virtual void Delete(int id)
{
var record = Set<T>().Find(id);
if (record == null)
throw new Exception("Some Message");
Set<T>().Remove(record);
}
// ... ETC
}
public interface IGenericRepository<T> where T : class
{
void Commit();
// ... ETC
}
public class WebsiteContext : DbContext, IWebsiteContext
{
static WebsiteContext()
{
Database.SetInitializer<WebsiteContext>(null);
}
public WebsiteContext() : base("Name=WebsiteContext") { }
public IDbSet<User> Users { get; set; }
// ... ETC
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// ... ETC
}
}
This implementation is loosely based around the following Stackoverflow question.
Entity Framework 6 Code First - Is Repository Implementation a Good One?
The following is a condensed version of the Service Layer class and method which is causing the issue.
private IGenericRepository<User> _userRepository;
private IGenericRepository<ApplicationUserSetting> _userSettingRepository;
private IGenericRepository<ApplicationRole> _roleRepository;
public UserManagementService()
{
_userRepository = new GenericRepository<User>();
_roleRepository = new GenericRepository<ApplicationRole>();
_userSettingRepository = new GenericRepository<ApplicationUserSetting>();
}
public void AssignUserRole(AssignRoleModel model)
{
var user = _userRepository.GetById(model.UserId);
if (user == null)
return;
var role = _roleRepository.GetById(model.RoleId);
if (role == null)
return;
user.Roles.Add(role);
_userRepository.Commit();
}
The issue, just like the error states, is because you have multiple instances of the type DbContext fetching your entities for you. Each fetched entity is then associated with the DbContext instance that retrieved it. If you want to persist changes to these entities it has to occur on the DbContext instance that it is associated with OR you have to attach it to the DbContext instance it is not associated with.
If you are trying to keep it simple I recommend you implement a DI framework like AutoFac. You can then have a single DbContext instance created per request and have it injected everywhere you need it. It will allow you to keep your existing structure (I am not going to comment on that as I consider that out of scope for this question), the end result would be that each injected GenericRepository instance has an injected WebsiteContext instance but the WebsiteContext instances are shared (all the same instance). The upside of that is no more error but the downside is you do have to be aware that any changes to any entities will result in those changes being persisted as soon as you execute the Save functionality.
Using multiple repositories causes the issue. Just use one repository (= one db context) and have different methods for getting the individual types.
E.g. _repository.Get(id)
It's way out of scope to point out how your current implementation could be made to work, but if you did want to use more than one context, you can despite what others have said.
If you do, you will have to detach the entity from the previous context first.

EF 6.1 how to swap migration configurations for testing?

I am trying to use Integration testing using EF 6.1 and run into a problem that my migration configuration settings are used where I dont need them. And I cant figure out how to swap them out for testing.
Here is my Test Class:
[TestClass]
public class SXSeasonConverterTests
{
public void RecreateDatabaseForTesting()
{
Database.SetInitializer(new TestDatabaseSeedingInitializer());
using (var context = new BaseNFLContext("NFLContextIntegrationTests"))
{
context.Database.Initialize(true);
}
}
public SXSeasonConverterTests()
{
RecreateDatabaseForTesting();
}
}
Here is my Initializer class:
public class TestDatabaseSeedingInitializer : DropCreateDatabaseAlways<BaseNFLContext>
{
protected override void Seed(BaseNFLContext context)
{
//Add Teams
context.Teams.Add(new Team { Code = "ARZ", Name = "Arizona Cardinals" });
context.Teams.Add(new Team { Code = "ATL", Name = "Atlanta Falcons" });
...
}
}
However when I try to run the test, I get the error that my AutomaticMigrations are disabled. When I looked further I found that It uses this code on Initialize:
internal sealed class NFLConfiguration : DbMigrationsConfiguration<BaseNFLContext>
{
public NFLConfiguration()
{
AutomaticMigrationsEnabled = false;
AutomaticMigrationDataLossAllowed = false;
}
}
This code is obviously there for production. However when doing testing how can I swap those migration configurations and set AutomaticMigrationsEnabled = true;?
I used to test my EF stuff using a special unittesting database and executed the tests in a TransactionScope which was rolled back at the end of the test. This way, no data was actually stored in the database.
I wasn't fast, but it suited our purpose.
You should create a separate project for testing and have a separate Db context that points to a test database. You can create something like a IDbContext interface that tells you which object models need to be tested. Also, the data access layer needs to allow you to inject this test Db context as a dependency.

unit testing a unit of work

new to unit testing. I have a unit of work that I am trying to unit test. I am probably missing something simple here. I am trying to unit test the Commit method. I am using nunit and moq.
public class UnitOfWork : IUnitOfWork
{
private readonly DbContext _context;
public UnitOfWork(DbContext ctx)
{
_context = ctx;
}
public void Commit()
{
_context.SaveChanges();
}
}
What do I need to do to test this?
You would insert a mock of the DBContext and then verify that the SaveChanges method is called on commit.
[Test]
public void Will_call_save_changes() {
var mockContext = new Mock<DBContext>();
var unitOfWork = new UnitOfWork(mockContext.Object);
unitOfWork.Commit();
mockContext.Verify(x => x.SaveChanges());
}
You'll need to mock the DbContext, and then verify that SaveChanges was called. Something like Moq can help you here.
That's one way of doing it.
An alternative I've come across is:
Create your edmx file, remove the custom tool so it doesn't autogenerate the entities.
Open the edmx file, right click and add code generation item - go to online templates under database and select the EF POCO mockobject generator. This creates two T4 templates (one for entities and another for the object context and mock object context).
The one T4 template will generate your poco entities for you. The other T4 template will create an interface you can extend to be used as a unit of work which is implemented in an actual object context and a mock object context. Extending it just requires you modify the T4 template to include an additional method on the generated interface (void SaveChanges()) and the implementation of that method on the mock object context.
I've found it to work very well.
Albeit for unit testing purposes, you wouldn't want to test your unit of work (unless verifying certain objects are added/deleted etc.). You would instead test repositories with predefined responsibilities - usually defined within context (ex. patient appointments).
You'd do something like this:
public class PatientAppointmentRepository : IPatientAppointmentRepository
{
//Injected via IOC in constructor
private readonly IUnitOfWork _unitOfWork;
private readonly IPatientAppointmentLogic _patientAppointmentLogic;
public void CreateAppointment(PatientAppointmentModel model)
{
var appointment = ModelMapper.Instance.To<PatientAppointment>(model);
var appointmentAdded = _patientAppointmentLogic.Add(appointment);
if(appointmentAdded)
_unitOfWork.SaveChanges();
}
}
public class PatientAppointmentLogic : IPatientAppointmentLogic
{
private readonly IUnitOfWork _unitOfWork; //Set via constructor
private readonly PatientLogic _patientLogic;
public bool Validate(PatientAppointment appointment)
{
if(appointment == null)
throw new ArgumentNullException("appointment");
//perform some logic here
return true;
}
public void Add(PatientAppointment appointment)
{
if(appointment == null)
throw new ArgumentNullException("appointment");
if(!Validate(appointment)) return; //Or throw an exception, up to you
var patient = _patientLogic.GetById(appointment.PatientId);
if(patient == null) return;
patient.PatientAppointments.Add(appointment);
}
}
It's really up to your to structure it appropiately. You could have another AppointmentLogic repository that has a base validation as an example.
Ideally, generic validation should not depend on external resources (such as a database).
You should be able to create a validation context in one swoop that would be used in further validation (first valid 'cheaply' before you validate 'expensively').
Sometimes all the 'values' you need for the validation is inside an entity you would need anyway, then use that as the validation context.
Best of luck!

Categories