DB ConnectionState = Open但context.SaveChanges抛出“连接已损坏”异常

时间:2016-11-10 02:20:17

标签: c# database-connection entity-framework-5 sql-server-2014

在我的服务中,我有一个后台线程,可以尽最大努力保存某个实体类型的对象流。代码大致如下:

        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中)

所以,基本上,有一个无限循环,试图从一些列表中读取项目并将它们保存到Db。如果我们检测到与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。因此,当上下文报告其数据库连接已打开时,我们处于一种状态,但我们无法针对该上下文运行查询。重新启动服务可以解决问题(以及在调试器中弄乱AllowRun变量以强制重新创建上下文)。所以问题是因为我不能信任上下文的连接状态,我如何验证我可以对DB运行查询?

此外,是否有一种干净的方法可以确定连接是否处于“健康”状态?我的意思是,EntityException本身并不表示我应该重置连接,只有当它的InnerException是InvalidOperationException时带有一些特定的消息,然后是,是时候重置它了。但是,现在我想在ConnectionState指示一切正常时会有其他情况,但我无法查询数据库。我可以主动抓住这些,而不是等到它开始咬我吗?

3 个答案:

答案 0 :(得分:1)

日志频率是多少?

如果此循环花费的时间超过连接超时,则在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();

答案 1 :(得分:0)

根据我从事类似服务的经验,垃圾收集不会发生,直到使用块结束。

如果有很多Pending日志要写,这可能会占用大量内存,但我也猜测它可能会使dbConnection池匮乏。

您可以使用RedGate ANTS或类似工具分析内存使用情况,并使用此StackOverflow问题中的以下脚本检查打开的dbConnections:how to see active SQL Server connections?

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
        }
    }

答案 2 :(得分:0)

我建议通过以下网址: 当sql查询运行时间过长时,通常会抛出Timeout Expired。

听起来像SQL作业正在运行,备份?这可能是锁定表或重新启动服务。

<强> ADONET async execution - connection broken error