如何处置 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<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)
【问题讨论】:
你真的需要所有的 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 内存数据库的主要内容,如果未能解决你的问题,请参考以下文章