EF Core现有事务中执行无模型原生SQL查询遇MySQL连接占用问题
哎,这个坑我之前用Pomelo的时候也踩过!咱们先说说问题出在哪,再给你两种靠谱的解决办法。
问题根源
你代码里的核心问题是没有正确释放DbDataReader资源——你直接调用了ExecuteReader()但没把它放进using块里,导致数据库连接一直被这个未关闭的reader占用着。Pomelo的MySQL连接池对连接状态的管控特别严格,这种未释放的资源会让EF在提交事务时误以为连接还在被使用,于是就抛出了那个讨厌的"This MySqlConnection is already in use"异常。
另外,手动直接操作GetDbConnection()的时候,如果和EF自身的连接管理逻辑配合不好,也容易出现这类冲突。
解决办法一:修正现有代码,正确释放资源
把你的代码改成这样,重点是给DbDataReader加上using包裹,同时不要手动关闭EF管理的连接:
object[] values = Array.Empty<object>(); var connection = context.Database.GetDbConnection(); try { // 确保连接处于打开状态(EF可能已经打开了,但保险起见检查一下) if (connection.State != ConnectionState.Open) { connection.Open(); } using (var command = connection.CreateCommand()) { command.CommandText = sql; // 关联当前EF事务 command.Transaction = context.Database.CurrentTransaction?.GetDbTransaction(); // 关键:用using包裹reader,确保执行完自动关闭释放资源 using (var reader = command.ExecuteReader()) { values = new object[reader.FieldCount]; if (reader.Read()) { reader.GetValues(values); } } } } finally { // 划重点:不要手动调用connection.Close()!EF会自己管理连接生命周期 // 手动关闭会导致后续EF操作出错 } return values;
解决办法二:用EF原生方法简化操作(更推荐)
其实EF Core已经提供了更贴合它自身连接/事务管理的原生SQL执行方法,完全不用手动折腾DbConnection和DbCommand。只要你引用了Microsoft.EntityFrameworkCore.Relational包(Pomelo已经依赖这个了,所以你项目里肯定有),就可以用ExecuteSqlReader方法:
object[] values = Array.Empty<object>(); // ExecuteSqlReader会自动复用当前DbContext的连接和事务 using (var reader = context.Database.ExecuteSqlReader(sql)) { values = new object[reader.FieldCount]; if (reader.Read()) { reader.GetValues(values); } } return values;
这个方法的好处是:
- 自动关联当前的EF事务,不用手动设置
command.Transaction - EF全程管控连接的打开/关闭和资源释放,彻底避免连接冲突
- 代码更简洁,不用处理连接状态的各种细节
最后再提醒一句
不管用哪种方法,所有实现了IDisposable的ADO.NET对象(比如DbCommand、DbDataReader)都必须用using包裹,这是避免资源泄漏和连接冲突的基本原则。尤其是Pomelo的MySQL驱动,对这类资源的释放要求比其他驱动更严格。
内容的提问来源于stack exchange,提问作者betelgewse

