Entity Framework Core 7数据库连接未关闭问题及解决方案疑问
为啥原代码的using没起作用?
核心问题是你手动创建并打开了SqlConnection,然后把这个现成的连接传给DbContext时,EF Core不会自动接管这个连接的生命周期。
原代码里的FetchSalesContext方法,你自己实例化了SqlConnection还调用了Open(),再把这个已打开的连接塞给UseSqlServer。EF Core的规则是:如果连接是外部提供且已经打开的,DbContext被Dispose的时候,只会清理自身资源,不会主动关闭这个外部连接——它默认认为连接的生命周期该由创建它的调用方负责。
你以为using语句会帮你关闭连接,实际上using只是销毁了DbContext,但DbContext没碰你手动打开的那个连接,导致连接一直被占用不释放,最后连接池耗尽、服务宕机。
另外原代码里还有个明显笔误:connection.Open()是同步方法,根本没有Result属性,这里应该直接传connection,不是connection.Result。
你的修复为啥有效?
你在finally里主动调用了CloseConnection(),直接关闭了手动打开的连接并释放回连接池,自然解决了连接耗尽的问题。但这其实是绕开了EF的生命周期管理,不算最优解。
正确的做法应该是这样
别自己手动处理连接的打开和关闭,让EF Core自动处理才是最省心可靠的:
方法1:让EF自动管理连接(强烈推荐)
直接把连接字符串传给UseSqlServer,EF会自动从连接池获取连接、使用后释放回连接池,完全不需要手动创建或打开连接:
public SalesContext FetchSalesContext(String fullConnStr) { var opt = new DbContextOptionsBuilder<SalesContext>(); opt.UseSqlServer(fullConnStr); // 直接传连接字符串,EF自动搞定连接生命周期 return new SalesContext(opt.Options); } public async Task<Sale> FetchSale(Guid salesId) { using (var dbContext = FetchSalesContext()) { Sale sale = await dbContext.Sales.FindAsync(salesId); if (sale != null) await dbContext.Entry(sale).Reference(prop => prop.Status).LoadAsync(); return sale; } }
这种情况下,using销毁DbContext时,EF会自动把连接归还连接池,不会出现连接泄漏。
方法2:必须手动管理连接时的正确姿势
如果因为特殊原因必须自己创建连接,那要确保连接本身也被纳入生命周期管理,或者明确让DbContext接管连接:
public async Task<Sale> FetchSale(Guid salesId, string fullConnStr) { // 把连接也包在using里,确保它会被正确销毁 using var connection = new SqlConnection(fullConnStr); await connection.OpenAsync(); // 用异步方法避免阻塞 var opt = new DbContextOptionsBuilder<SalesContext>(); opt.UseSqlServer(connection); // 传递连接,让EF协助管理 using var dbContext = new SalesContext(opt.Options); Sale sale = await dbContext.Sales.FindAsync(salesId); if (sale != null) await dbContext.Entry(sale).Reference(prop => prop.Status).LoadAsync(); return sale; }
这里连接和DbContext都在using块中,双重保证连接会被释放。
总结一下:
- 优先让EF Core自动管理连接,省心且不易出错
- 若必须手动创建连接,记得把连接也放进
using块,避免连接泄漏
内容的提问来源于stack exchange,提问作者Andy

