如何在EntityFramework.Core中解锁Sqlite数据库文件
问题说明
运行在net6.0上的项目使用Microsoft.EntityFrameworkCore.Sqlite 6.0.5版本时,预期DbContext释放后SQLite数据库文件会自动解除锁定,但实际观测到DbContext被释放、甚至终结器执行完成后,数据库文件仍处于锁定状态。
使用的DbContext代码如下:
public class MyContext : DbContext { ~MyContext() { Console.WriteLine("Finaliser was called."); } public override void Dispose() { base.Dispose(); Console.WriteLine("Dispose was called."); } public static readonly string DbFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "_temp.db"); public DbSet<Foo> Summaries { get; set; } protected override void OnModelCreating(ModelBuilder builder) { builder.Entity<Foo>().HasKey(nameof(Foo.Id)); } protected override void OnConfiguring(DbContextOptionsBuilder options) { options.UseSqlite($"Data Source={DbFile}"); } }
调用上下文的代码如下:
public static void AddItem() { using var ctx = new MyContext(); ctx.Database.EnsureCreated(); ctx.Summaries.Add(new Foo {Bar = "Foo"}); ctx.SaveChanges(); }
问题原因
这个行为是Microsoft.Data.Sqlite默认的连接池机制设计导致的:DbContext释放时,关联的数据库连接不会直接关闭销毁,而是回到连接池中等待复用,因此数据库文件的句柄会一直被进程持有,锁不会随DbContext释放自动解除。
解决方案
根据业务场景二选一即可:
- 方案1:全局禁用连接池
在SQLite连接字符串中追加Pooling=False参数,关闭连接池能力,DbContext释放时会直接关闭底层连接,立即释放文件锁:
该方案适合数据库访问频次不高、不需要连接池提升性能的场景。protected override void OnConfiguring(DbContextOptionsBuilder options) { options.UseSqlite($"Data Source={DbFile};Pooling=False"); } - 方案2:按需手动清空连接池
如果需要保留连接池的性能收益,不需要全局关闭池化,可以在需要释放文件锁的节点手动清空连接池:- 首先引入对应命名空间:
using Microsoft.Data.Sqlite; - 确认所有相关
DbContext都已经释放后,在操作数据库文件(删除、移动、备份等)之前调用静态方法:SqliteConnection.ClearAllPools();
- 首先引入对应命名空间:
额外优化建议
当前代码中重写Dispose的写法不符合.NET的Dispose模式规范,会导致对象终结器必然被执行,产生不必要的性能开销,建议补充GC.SuppressFinalize调用:
public override void Dispose() { base.Dispose(); GC.SuppressFinalize(this); Console.WriteLine("Dispose was called."); }
内容的提问来源于stack exchange,提问作者Sean Kearon
相关产品推荐
相关产品推荐

