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

Posted

技术标签:

【中文标题】如何处置 Entity Framework Core 内存数据库【英文标题】:How to dispose Entity Framework Core in-memory database 【发布时间】: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 【参考方案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 ***找到一个带有 Effort 数据库的控制台程序的简单复制粘贴代码示例,该数据库使用一对多关系。

【讨论】:

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

以上是关于如何处置 Entity Framework Core 内存数据库的主要内容,如果未能解决你的问题,请参考以下文章

如何使用Entity Framework仅更新一个字段?

C# Entity-Framework:如何在模型对象上组合 .Find 和 .Include?

Entity Framework 3.5 - 如何加载孩子

如何扩展 Entity Framework 6 模型

如何使用 Entity Framework 7 记录查询?

如何在 Entity Framework Core 中运行存储过程?