Docker环境下EF Core替换SQLite文件后无法获取新值的问题
问题背景
运行环境
- .NET 6
- Microsoft.EntityFrameworkCore.Sqlite: 6.0.9
- Docker Desktop: 20.10.16
- OS: Win10
场景与代码
我创建了一个ASP.NET Core项目,通过API查询SQLite数据库,核心接口代码如下:
[ApiController] [Route("[controller]")] public class HomeController : ControllerBase { private readonly HeroDbContext _dbContext; public HomeController(HeroDbContext dbContext) { _dbContext = dbContext; } [HttpGet("{id:int}")] public async Task<IActionResult> Get(int id, CancellationToken cancellationToken) { var hero = await _dbContext.Heroes.FirstOrDefaultAsync(e => e.Id == id, cancellationToken); var response = new { Data = hero, Successed = hero != null }; return Ok(response); } }
通过WSL2部署Docker容器的命令:
docker run -d --name ddocker -p 9100:80 -v /mnt/c/Users/chiwenjun/Desktop/pdemo/docs/:/home/db/ ddocker
异常现象
- 直接修改宿主机
/mnt/c/Users/chiwenjun/Desktop/pdemo/docs目录下的SQLite文件,API能正常返回新值; - 使用rsync命令同步新SQLite文件替换旧文件后,API始终返回旧数据;
- 用Python FastAPI编写的查询同一SQLite文件的API可正常返回新值,核心代码如下:
class HeroDbContext: def __init__(self, conn_str): self.__conn_str = conn_str self.__conn = sqlite3.connect(conn_str) async def __aenter__(self): if self.__conn is None: self.__conn = sqlite3.connect(conn_str) return self async def __aexit__(self, ex_type, ex_value, ex_traceback) -> bool: if self.__conn is not None: self.__conn.close() return True def get_by_id(self, id: int) -> Union[Hero, None]: try: cur = self.__conn.cursor() r = cur.execute("SELECT * FROM Heros WHERE Id=:id", [id]) return self.__handle_db_result(r) finally: cur.close() def __handle_db_result(self, result: Cursor) -> Optional[Hero]: if result is None: return None data = result.fetchone() return None if data is None else Hero(data[0], data[1])
问题原因
EF Core Sqlite默认会保持数据库连接长时间打开(HeroDbContext在ASP.NET Core中是作用域实例,连接会被复用),当用rsync替换文件时,操作系统层面的文件句柄仍被EF Core持有,导致程序读取的还是旧文件的内存缓存或句柄指向的旧数据。
而Python代码中,若使用async with语法调用HeroDbContext,每次请求结束后连接会被关闭,下次请求会重新打开文件,因此能读取到新的SQLite文件内容。
解决方案
方案1:关闭EF Core连接池
在DbContext配置中关闭连接池,让每次请求结束后释放连接,下次查询重新打开文件:
// Program.cs/Startup.cs中配置DbContext builder.Services.AddDbContext<HeroDbContext>(options => options.UseSqlite("Data Source=/home/db/your-db-file.db", sqliteOptions => sqliteOptions.Pooling(false)));
注意:关闭连接池会降低查询性能,需根据业务场景权衡。
方案2:替换文件后重启Docker容器
这是最直接的方式,重启后EF Core会重新建立连接,读取新文件:
docker restart ddocker
方案3:修改文件替换方式
rsync的覆盖写不会触发应用感知文件变更,可先删除旧文件再复制新文件:
# 删除容器内旧文件 docker exec ddocker rm /home/db/your-db-file.db # 同步新文件 rsync -av /mnt/c/Users/chiwenjun/Desktop/pdemo/docs/your-db-file.db ddocker:/home/db/
注意:该操作会导致短暂的数据库不可用,需做好接口异常处理。
内容的提问来源于stack exchange,提问作者WENJUN CHI
相关产品推荐
相关产品推荐

