如何配置Entity Framework Core内存数据库

时间:2017-10-20 14:06:45

标签: entity-framework unit-testing dependency-injection .net-core entity-framework-core

我想在每个单元测试中创建干净的内存数据库。 当我运行多个测试时,先前测试的数据仍保留在数据库中。如何处置现有的内存数据库?

我使用以下代码初始化每个测试:

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

我使用.NET Core 2.0和Entity Framework Core 2.0

修改 我无法使用标准注册:Services.AddDbContext<InMemoryMyDbContext>(...),因为

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) { } 
}

2 个答案:

答案 0 :(得分:1)

好的,我的解决方案是调用 DbContextOptionsBuilder.UseApplicationServiceProvider()

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

当您使用usuall方式设置ServiceCollection时会自动调用此方法,因此在以下情况下,每次都从头开始创建数据库

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

最后,我能够修改MyDbContext,以便我调用上面的行:

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 :(得分:0)

考虑使用Nuget包努力

这是一个简单的快速内存数据库,非常适合单元测试

您可以使用空数据库启动它;如果需要,可以使用数据库播种器填充它,或者将其填入测试CSV文件中的值。

请参阅Tutorials Effort - Entity Framework Unit Testing Tool

您的DbContext可能类似于:

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

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

只需添加一个构造函数,就可以像使用原始数据库一样使用内存数据库:

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

您将DbConnection传递给测试数据库,此连接将传递给DbContext类。 true参数表示当DbContext被释放时,DbConnection也应该是Disposed。

用法是:

[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
    }
}

可以找到具有使用一对多关系的Effort数据库的控制台程序的简单复制粘贴代码示例here on StackOverFlow