How to dispose Entity Framework Core in-memory database

.net-core dependency-injection entity-framework entity-framework-core unit-testing

Question

I want to create clean inmemory database in each unit test. When I run multiple tests, data from previous tests remains in the database. How to dispose existing inmemory database?

I initialize each test with following code:

 [TestInitialize]
 public void TestInitialize()
 {
     Services = new ServiceCollection();
     Services.AddScoped<DbContextOptions<MyDbContext>>(sp => new DbContextOptionsBuilder<TacsDbContext>()
             .UseInMemoryDatabase("MyTestDbContext")
             .Options);

     Services.AddTransient<InMemoryMyDbContext>();
     Services.AddTransient<MyDbContext>(sp => sp.GetService<InMemoryTacsDbContext>());

    ServiceProvider = Services.BuildServiceProvider();
 }

 [TestMethod]
 public void Test1()
 {
     using (var dbContext = ServiceProvider.GetService<MyDbContext>()) ...
 }

 [TestMethod]
 public void Test2()
 {
     using (var dbContext = ServiceProvider.GetService<MyDbContext>()) ...
 }

I use .NET Core 2.0, and Entity Framework Core 2.0

EDIT I wasn't able to use the standard registration: Services.AddDbContext<InMemoryMyDbContext>(...), because

public class InMemoryMyDbContext : MyDbContext
{
    public InMemoryMyDbContext(DbContextOptions<InMemoryMyDbContext> options)
      : base(options) { }  //compiler error

    public InMemoryMyDbContext(DbContextOptions<MyDbContext> options)
      : base(options) { }  //runtime IoC error
}

public class MyDbContext : DbContext
{
    public MyDbContext(DbContextOptions<MyDbContext> options)
      : base(options) { } 
}
1
5
10/20/2017 8:19:44 PM

Accepted Answer

Ok, the solution in my case was to call DbContextOptionsBuilder.UseApplicationServiceProvider()

Services.AddScoped<DbContextOptions<MyDbContext>>(sp => new DbContextOptionsBuilder<MyDbContext>()
        .UseApplicationServiceProvider(sp)
        .UseInMemoryDatabase("Test")
        .Options);

This method is called automatically when you setup ServiceCollection the usuall way, so in following case the database is created from scratch each time

Services.AddDbContext<MyDbContext>(options => options.UseInMemoryDatabase("Test"));

At the end, I was able to modify MyDbContext so that I call the line above:

public class MyDbContext : DbContext
{
    protected MyDbContext (DbContextOptions options) : base(options) { }

    public MyDbContext (DbContextOptions<MyDbContext> options) 
       : this((DbContextOptions)options) { }
}

public class InMemoryMyDbContext : TacsDbContext
{
    public InMemoryMyDbContext (DbContextOptions<InMemoryTacsDbContext> options) 
        : base(options) { } 
}

Services.AddDbContext<InMemoryMyDbContext>(options => 
    options.UseInMemoryDatabase("Test"), ServiceLifetime.Transient);
Services.AddTransient<MyDbContext>(sp => sp.GetService<InMemoryMyDbContext>());
1
10/20/2017 3:04:49 PM

Popular Answer

Consider using the Nuget package Effort

It is a simple ad fast in-memory database ideal for unit-testing

You can start it with an empty database; if desired fill it using a database seeder, or fill it with values form a test CSV file.

See Tutorials Effort - Entity Framework Unit Testing Tool

Your DbContext probably will look similar to:

class MyDbContext : DbContext
{
    public MyDbContext() : base() { } // constructor using config file
    public BloggingContext(string nameOrConnectionString) : 
        base(nameOrConnectionString) { }

    public DbSet<...> ...{ get; set; }
    public DbSet<...> ...{ get; set; }
}

Just add one constructor and you can use your in-memory database as if it was your original database:

 public MyDbContext(DbConnection connection) : base(connection, true) { }

You pass the DbConnection to the test database, this connection is passed to the DbContext class. The true parameter says that when the DbContext is disposed, that the DbConnection should also be Disposed.

Usage would be:

[TestMethod]
public void UnitTest_X()
{
    var dbConnection = Effort.DbConnectionFactory.CreateTransient();
    using (var dbContext = new MyDbContext(dbConnection)
    {
        // perform your test of MyDbContext as if it was connected to your
        // original database
    }
}

A simple copy-paste code example for a console program with an Effort database that uses a one-to-many relationship can be found here on StackOverFlow



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