Sqlite内存模式下EnsureCreated()创建Northwind测试库异常问题
看起来你遇到的是EF Core在SQLite内存数据库环境下的模型构建或表创建逻辑冲突问题,结合你的描述(SQL Server正常、仅多表时触发、Region仅定义一次),下面是几个针对性的排查方向和解决方案:
1. 先排查模型是否存在重复表映射
虽然你确认Region只配置了一次,但有可能其他实体的映射不小心指向了Region表(比如复制粘贴代码时的疏忽)。可以在测试中添加代码,打印所有实体对应的表名,验证是否有重复:
// 在context.Database.EnsureCreated()之前添加 var model = context.Model; foreach (var entityType in model.GetEntityTypes()) { Console.WriteLine($"实体: {entityType.ClrType.Name} -> 表名: {entityType.GetTableName()}"); }
如果输出中发现多个实体对应Region表,那就是问题根源,找到那个错误配置的实体修正即可。
2. 解决EF Core模型缓存导致的冲突
EF Core会默认缓存DbContext的模型,在测试场景下,如果多个测试方法共享了模型缓存,可能会出现旧模型的残留配置(比如之前测试中临时添加的表映射)。可以通过自定义模型缓存键工厂,强制每次测试生成全新的模型:
首先定义一个自定义的缓存键工厂:
public class UniqueTestModelCacheKeyFactory : IModelCacheKeyFactory { public object Create(DbContext context) { // 每次返回全新的对象,避免复用缓存的模型 return Guid.NewGuid(); } }
然后在测试的DbContext配置中替换服务:
var builder = new DbContextOptionsBuilder<NorthwindContext>() .UseSqlite(connection) .ReplaceService<IModelCacheKeyFactory, UniqueTestModelCacheKeyFactory>();
3. 调整SQLite内存数据库的使用方式
SQLite内存数据库的生命周期和连接绑定:只有当连接保持打开时,内存数据库才存在,而EnsureDeleted()在内存模式下的行为可能和你预期不符(因为删除表后,连接依然存在,后续操作可能触发奇怪的重建逻辑)。可以简化测试代码,利用using自动管理连接和上下文,去掉不必要的EnsureDeleted():
public async Task UpdateCategoryWithCorrectEtag(Category expectedCategory) { // 使用using自动释放连接 using var connection = new SqliteConnection("DataSource=:memory:"); connection.Open(); var builder = new DbContextOptionsBuilder<NorthwindContext>() .UseSqlite(connection); // 使用using自动释放上下文 using var context = new NorthwindContext(builder.Options); context.Database.EnsureCreated(); var x = context.Categories.Count(); // 无需手动调用EnsureDeleted或Close,using会自动处理 }
4. 排查实体关联导致的表创建顺序问题
如果你的实体之间有外键关联(比如其他表引用了Region),EF Core在SQLite下创建表的顺序可能触发重复创建表的bug。可以尝试手动创建表来验证:
// 替换context.Database.EnsureCreated()为手动执行SQL await context.Database.ExecuteSqlRawAsync(@" CREATE TABLE Region ( RegionID INTEGER NOT NULL PRIMARY KEY, RegionDescription TEXT NOT NULL ); -- 这里添加其他表的创建SQL,比如Categories表 CREATE TABLE Categories ( CategoryID INTEGER NOT NULL PRIMARY KEY, CategoryName TEXT NOT NULL, Description TEXT, Picture BLOB ); ");
如果手动创建表没有报错,说明是EnsureCreated()的自动生成逻辑在SQLite下的问题,此时可以考虑使用EF Core迁移(context.Database.Migrate())代替EnsureCreated(),迁移的表创建逻辑更稳定。
内容的提问来源于stack exchange,提问作者Toad

