EF Core DbContext Concurrency Issue with XUnit Tests

c# entity-framework-core xunit

Question

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
                }
            }
1
1
5/26/2019 8:28:39 AM

Accepted Answer

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().

2
5/26/2019 9:33:17 AM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow