【问题标题】:How to dispose Entity Framework Core in-memory database如何处置 Entity Framework Core 内存数据库
【发布时间】:2018-04-01 17:32:27
【问题描述】:

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

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

 [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&lt;InMemoryMyDbContext&gt;(...),因为

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

【问题讨论】:

  • 你真的需要所有的 DI 包装吗?为什么不在你的测试函数中调用new MyDbContext。
  • 我确实做到了。我的测试更复杂,我在集成测试中使用这种方法。此外,我使用 IoC 作为auto mocking container

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


【解决方案1】:

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

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

这个方法会在你设置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 : MyDbContext
{
    public InMemoryMyDbContext (DbContextOptions<InMemoryTacsDbContext> options) 
        : base(options) { } 
}

Services.AddDbContext<InMemoryMyDbContext>(options => 
    options.UseInMemoryDatabase("Test"), ServiceLifetime.Transient);
Services.AddTransient<MyDbContext>(sp => sp.GetService<InMemoryMyDbContext>());

【讨论】:

    【解决方案2】:

    考虑使用 Nuget 包Effort

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

    你可以用一个空的数据库启动它;如果需要,使用数据库播种器填充它,或者使用测试 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 也应该被释放。

    用法如下:

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

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

    【讨论】:

    • 与内置实体框架内存提供程序相比有什么优势?
    • 如果你想像提问者那样使用实体框架,并且你想对你的代码进行单元测试,你不想对数据库进行单元测试,也不想对实体框架进行单元测试。您的代码应该适用于任何适用于实体框架的东西。内存数据库的设置速度更快,并且可以立即清理,因此以前的测试不会影响您当前的测试。
    • 我同意,但是实体框架核心有内存数据库。为什么我要努力,因为它也是一样的?
    • 嗯,可能是因为我不知道它的存在。几年前,我需要一个内存数据库,stackoverflow 建议使用 Effort。显然,从那时起,实体框架扩展了内存中的 DbContext
    • Effort 不支持 EntityFramework Core。它只支持EntityFramework
    猜你喜欢
    • 2016-02-27
    • 2022-10-14
    • 1970-01-01
    • 2021-10-02
    • 1970-01-01
    • 1970-01-01
    • 2021-10-11
    • 2020-05-28
    • 1970-01-01
    相关资源
    最近更新 更多