未创建 EntityFrameworkCore SQLite 内存数据库表
Posted
技术标签:
【中文标题】未创建 EntityFrameworkCore SQLite 内存数据库表【英文标题】:EntityFrameworkCore SQLite in-memory db tables are not created 【发布时间】:2019-10-12 15:36:02 【问题描述】:对于集成测试,我正在使用 EntityFrameworkCore
SQLite
内存数据库并根据 Microsoft 文档创建其架构,但是当我尝试播种数据时会引发异常,即表不存在。
DbContext.Database.EnsureCreated();
的鼠标悬停文档:
确保上下文的数据库存在。如果存在,没有 采取行动。如果它不存在,那么数据库及其所有 架构被创建。如果数据库存在,则不执行任何操作 确保它与此上下文的模型兼容。
我读过EntityFrameworkCore
内存数据库仅在打开连接存在时才存在,因此我尝试显式创建var connection = new SqliteConnection("DataSource=:memory:");
实例并将以下代码包装在using(connection)
块中并传递连接实例options.UseSqlite(connection);
,但DbContext.Database.EnsureCreated();
仍然没有创建任何数据库对象
public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<Startup>
protected override IWebHostBuilder CreateWebHostBuilder()
return WebHost.CreateDefaultBuilder()
.UseStartup<Startup>();
protected override void ConfigureWebHost(IWebHostBuilder builder)
using (var connection = new SqliteConnection("DataSource=MySharedInMemoryDb;mode=memory;cache=shared"))
connection.Open();
builder.ConfigureServices(services =>
var serviceProvider = new ServiceCollection()
.AddEntityFrameworkSqlite()
.BuildServiceProvider();
services.AddDbContext<MyDbContext>(options =>
options.UseSqlite(connection);
options.UseInternalServiceProvider(serviceProvider);
);
var contextServiceProvider = services.BuildServiceProvider();
// we need a scope to obtain a reference to the database contexts
using (var scope = contextServiceProvider.CreateScope())
var scopedProvider = scope.ServiceProvider;
var logger = scopedProvider.GetRequiredService<ILogger<CustomWebApplicationFactory<TStartup>>>();
using (var myDb = scopedProvider.GetRequiredService<MyDbContext>())
// DEBUG CODE
// this returns script to create db objects as expected
// proving that MyDbContext is setup correctly
var script = myDb.Database.GenerateCreateScript();
// DEBUG CODE
// this does not create the db objects ( tables etc )
// this is not as expected and contrary to ms docs
var result = myDb.Database.EnsureCreated();
try
SeedData.PopulateTestData(myDb);
catch (Exception e)
// exception is thrown that tables don't exist
logger.LogError(e, $"SeedData.PopulateTestData(myDb) threw exception=[e.Message]");
);
builder.UseContentRoot(".");
base.ConfigureWebHost(builder);
请注意,在这篇文章中,我只是在问为什么DbContext.Database.EnsureCreated();
没有按预期创建架构。我没有将上述代码作为运行集成测试的一般模式。
【问题讨论】:
什么是chatDb
?应该是MyDb
?
糟糕,现在已修复。
EF Core 内存数据库(与 SQLite 内存数据库相反)没有您描述的行为。你能指出我在哪里找到这个文件以便我修复它吗?谢谢。
【参考方案1】:
使用非共享 SQLite 内存数据库
SQLite 内存数据库默认是瞬态的。作为documentation states:
数据库连接一关闭,数据库就不再存在。每个 :memory: 数据库都彼此不同。
另一方面,EF Core 的 DbContext
始终自动打开和关闭与数据库的连接,除非您传递已经打开的连接。
因此,为了在 EF Core 中跨多个调用使用相同的 SQLite 内存数据库,您需要分别创建一个 SqliteConnection
对象,然后将其传递给每个 DbContext
。
例如:
var keepAliveConnection = new SqliteConnection("DataSource=:memory:");
keepAliveConnection.Open();
services.AddDbContext<MyContext>(options =>
options.UseSqlite(keepAliveConnection);
);
请注意,SqliteConnection
并不是真正的线程安全的,因此这种方法仅适用于单线程场景。任何时候您希望拥有一个可以被多个线程访问的共享数据库(例如,在 ASP.NET Core 应用程序中,为多个请求提供服务),您都应该考虑使用磁盘数据库。
顺便说一下,这是EF Core documentation on how to use SQLite in-memory databases for testing目前使用的方法。
使用共享的 SQLite 内存数据库
SQLite 还支持命名shared in-memory databases。通过使用相同的连接字符串,多个SqliteConnection
对象可以连接到同一个数据库。然而:
当与数据库的最后一个连接关闭时,数据库会自动删除并回收内存。
因此,仍然需要维护一个单独的打开连接对象,以便数据库可以跨多个 EF Core 调用使用。例如:
var connectionString = "DataSource=myshareddb;mode=memory;cache=shared";
var keepAliveConnection = new SqliteConnection(connectionString);
keepAliveConnection.Open();
services.AddDbContext<MyContext>(options =>
options.UseSqlite(connectionString);
);
请注意,此方法不限于单个线程,因为每个 DbContext
都有自己的 SqliteConnection
实例。
【讨论】:
为什么DbContext.Database.EnsureCreated();
会涉及“在 EF Core 中的多个调用中(使用)相同的 SQLite 内存数据库”?无论如何(正如我在帖子中指出的那样),我已经尝试显式创建一个 var connection = new SqliteConnection("DataSource=:memory:");
实例并将我的代码包装在一个 using
块中并传递连接实例 options.UseSqlite(connection);
,但 DbContext.Database.EnsureCreated();
仍然没有t 创建任何数据库对象。
DbContext.Database.EnsureCreated();
是DatabaseFacade
上的一个框架方法,我希望它只对内存中的SQLite
实例进行一次调用,以执行与@ 返回的脚本相同的创建脚本987654341@
我确实仔细阅读了您的问题。我的这部分答案适用于所有找到此答案的人,因为他们正在尝试做与您类似的事情(但不完全相同)。使用 SQLite 内存数据库有一个值得指出的特定缺陷。
你的主旨完全是人为的。我的代码在 CustomWebApplicationFactory
的上下文中,这是模拟 Web 应用程序以进行集成测试所必需的。
请注意,Mode=Memory
不适用于 2.1,但已在 3.1 中修复。我得到它使用这个连接字符串"DataSource=file::memory:?cache=shared"
以上是关于未创建 EntityFrameworkCore SQLite 内存数据库表的主要内容,如果未能解决你的问题,请参考以下文章
Aspnetcore2.0中Entityframeworkcore及Autofac的使用(附Demo)(
如何使用 EntityFrameworkCore 定义父/子关系?
我应该将啥 DbContext 与 EntityFrameworkCore 一起使用?
System.TypeLoadException:“MySql.Data.EntityFrameworkCore.Query.Internal.MySQLSqlTranslatingExpressio