Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to dispose Entity Framework Core in-memory database

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) { } 
}
like image 927
Liero Avatar asked Oct 20 '17 14:10

Liero


People also ask

How do I dispose of dbContext in EF core?

When the controller is being disposed, call dispose on your repository and that should dispose the context. If you are using a service layer and not talking to the repository directly from the controller, then call dispose on the service which will call dispose on repo which will dispose the context.


2 Answers

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 : MyDbContext
{
    public InMemoryMyDbContext (DbContextOptions<InMemoryTacsDbContext> options) 
        : base(options) { } 
}

Services.AddDbContext<InMemoryMyDbContext>(options => 
    options.UseInMemoryDatabase("Test"), ServiceLifetime.Transient);
Services.AddTransient<MyDbContext>(sp => sp.GetService<InMemoryMyDbContext>());
like image 164
Liero Avatar answered Oct 11 '22 01:10

Liero


Consider using the Nuget package Effort

It is a simple and 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 from 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

like image 38
Harald Coppoolse Avatar answered Oct 11 '22 01:10

Harald Coppoolse