DB ConnectionState =打开但上下文.SaveChanges抛出“连接断开”;例外
本文关键字:断开 连接 连接断开 例外 抛出 SaveChanges ConnectionState 上下文 DB | 更新日期: 2023-09-27 18:17:16
在我的服务中,我有一个后台线程,它尽最大努力保存特定实体类型的对象流。代码大致如下:
while (AllowRun)
{
try
{
using (DbContext context = GetNewDbContext())
{
while (AllowRun && context.GetConnection().State == ConnectionState.Open)
{
TEntity entity = null;
try
{
while (pendingLogs.Count > 0)
{
lock (pendingLogs)
{
entity = null;
if (pendingLogs.Count > 0)
{
entity = pendingLogs[0];
pendingLogs.RemoveAt(0);
}
}
if (entity != null)
{
context.Entities.Add(entity);
}
}
context.SaveChanges();
}
catch (Exception e)
{
// (1)
// Log exception and continue execution
}
}
}
}
catch (Exception e)
{
// Log context initialization failure and continue execution
}
}
(这主要是实际的代码,我省略了一些不相关的部分,这些部分试图将弹出的对象保存在内存中,直到我们能够在(1)
块捕获异常时再次将内容保存到DB)
context.SaveChanges()
被调用时开始产生以下异常(捕获在(1)
块):
System.Data.EntityException: An error occurred while starting a transaction on the provider connection. See the inner exception for details. --->
System.InvalidOperationException: The requested operation cannot be completed because the connection has been broken.
错误被记录,但是当执行返回到context.GetConnection().State == ConnectionState.Open
检查时,它的评估结果为true。因此,当上下文报告其DB连接打开时,我们处于这样一种状态,但我们不能对该上下文运行查询。重新启动服务可以消除这个问题(以及在调试器中使用AllowRun
变量来强制重新创建上下文)。所以问题是,既然我不能信任上下文的连接状态,我如何验证我可以对数据库运行查询?
还有,是否有一种干净的方法来确定连接是否处于"健康"状态?我的意思是,EntityException
本身并不是一个指示,我应该重置连接,只有当它的InnerException是InvalidOperationException
与一些特定的消息,那么是的,是时候重置它了。但是,现在我猜会有其他情况,当ConnectionState表明一切都很好,但我不能查询DB。我能否主动抓住它们,而不是等到它们开始咬我?
日志频率是多少?
如果此循环时间超过连接超时时间,则在savechanges执行时关闭连接。
while (pendingLogs.Count > 0)
{
lock (pendingLogs)
{
entity = null;
if (pendingLogs.Count > 0)
{
entity = pendingLogs[0];
pendingLogs.RemoveAt(0);
}
}
if (entity != null)
{
context.Entities.Add(entity);
}
}
context.SaveChanges();
根据我在类似服务上的工作经验,直到使用块的结束时才会进行垃圾收集。
如果有很多Pending日志要写,这可能会使用很多内存,但我也猜它可能会饿死dbConnection池。
你可以使用RedGate ANTS或类似的工具分析内存使用情况,并使用以下脚本检查打开的dbConnections,从这个StackOverflow问题:如何查看活动的SQL Server连接?
SELECT
DB_NAME(dbid) as DBName,
COUNT(dbid) as NumberOfConnections,
loginame as LoginName
FROM
sys.sysprocesses
WHERE
dbid > 0
GROUP BY
dbid, loginame
;
我认为尽可能多地释放上下文是一个很好的实践,以便给GC一个清理的改变,因此您可以将循环重写为:
while (AllowRun)
{
try
{
while (pendingLogs.Count > 0)
{
using (DbContext context = GetNewDbContext())
{
while (AllowRun && context.GetConnection().State == ConnectionState.Open)
{
TEntity entity = null;
try
{
lock (pendingLogs)
{
entity = null;
if (pendingLogs.Count > 0)
{
entity = pendingLogs[0];
pendingLogs.RemoveAt(0);
}
}
if (entity != null)
{
context.Entities.Add(entity);
context.SaveChanges();
}
}
catch (Exception e)
{
// (1)
// Log exception and continue execution
}
}
}
}
}
catch (Exception e)
{
// Log context initialization failure and continue execution
}
}
我建议通过下面的url:当sql查询运行时间过长时,通常会抛出Timeout Expired。
听起来像是一个SQL作业正在运行,备份?这可能是锁表或重新启动服务。
ADONET异步执行-连接断开错误