当前位置: 首页 > 知识库问答 >
问题:

知道从C#调用SQL Server时何时重试或失败吗?

苏波涛
2023-03-14

就其价值而言,我的代码并没有什么特别之处:

using (SqlConnection connection = new SqlConnection(myConnectionString))
using (SqlCommand command = connection.CreateCommand())
{
  command.CommandText = mySelectCommand;
  connection.Open();

  using (SqlDataReader reader = command.ExecuteReader())
  {
    while (reader.Read())
    {
      // Do something with the returned data.
    }
  }
}

共有1个答案

尉迟轶
2023-03-14

单个sqlexception(may)包装多个SQL Server错误。可以使用errors属性遍历它们。每个错误都是sqlerror:

foreach (SqlError error in exception.Errors)

每个sqlerror都有一个class属性,您可以使用该属性大致确定是否可以重试(以及在必须重新创建连接的情况下重试)。来自MSDN:

  • <10用于传递的信息中的错误,如果第一次没有更正输入,则(可能)不能重试。
  • 从11到16都是“由用户生成的”,那么如果用户首先没有纠正他的输入,那么您可能又不能做任何事情。请注意,类16包括许多临时错误,类13用于死锁(多亏了EvZ),所以如果逐个处理它们,可以排除这些类。
  • 从17到24是一般的硬件/软件错误,您可以重试。当为20或更高时,还必须重新创建连接。22和23可能是严重的硬件/软件错误,24表示媒体错误(应该警告用户,但如果只是“临时”错误,您可以重试)。
  • 检查已知错误代码(用select*FROM master.sys.messages)列出错误代码,以查看要处理的内容(知道如何处理)。该视图包含所有受支持语言的消息,因此可能需要按msglangid列进行筛选(例如,1033,用于英语)。
  • 对于其他一切都依赖错误类,当为13或高于16时重试(如果为20或高于16则重新连接)。
  • 严重程度高于21(22、23和24)的错误是严重错误,很少等待也无法修复该问题(数据库本身也可能被损坏)。

一个关于高级阶级的词。如何处理这些错误并不简单,它取决于许多因素(包括应用程序的风险管理)。作为简单的第一步,在尝试写操作时,我不会重试22、23和24:如果数据库、文件系统或媒体严重损坏,那么写新数据可能会进一步恶化数据完整性(SQL Server非常小心,即使在关键情况下也不会损害查询的数据库)。损坏的服务器(取决于您的DB网络体系结构)甚至可能被热交换(在指定的时间之后,或者在触发指定的触发器时自动进行)。总是咨询和工作接近您的DBA。

重试的策略取决于您处理的错误:释放资源、等待挂起的操作完成、采取替代操作等。通常,只有当所有错误都“可重试”时,您才应该重试:

bool rebuildConnection = true; // First try connection must be open

for (int i=0; i < MaximumNumberOfRetries; ++i) {
    try {
        // (Re)Create connection to SQL Server
        if (rebuildConnection) {
            if (connection != null)
                connection.Dispose();

            // Create connection and open it...
        }

        // Perform your task

        // No exceptions, task has been completed
        break;
    }
    catch (SqlException e) {
        if (e.Errors.Cast<SqlError>().All(x => CanRetry(x))) {
            // What to do? Handle that here, also checking Number property.
            // For Class < 20 you may simply Thread.Sleep(DelayOnError);

            rebuildConnection = e.Errors
                .Cast<SqlError>()
                .Any(x => x.Class >= 20);

            continue; 
        }

        throw;
    }
}
private static readonly int[] RetriableClasses = { 13, 16, 17, 18, 19, 20, 21, 22, 24 };

private static bool CanRetry(SqlError error) {
    // Use this switch if you want to handle only well-known errors,
    // remove it if you want to always retry. A "blacklist" approach may
    // also work: return false when you're sure you can't recover from one
    // error and rely on Class for anything else.
    switch (error.Number) {
        // Handle well-known error codes, 
    }

    // Handle unknown errors with severity 21 or less. 22 or more
    // indicates a serious error that need to be manually fixed.
    // 24 indicates media errors. They're serious errors (that should
    // be also notified) but we may retry...
    return RetriableClasses.Contains(error.Class); // LINQ...
}

这里有一些非常棘手的方法来查找非关键错误列表。

通常,我会用这个签名将所有这些(样板)代码嵌入到一个方法中(在这个方法中,我可以隐藏为创建/释放/重新创建连接所做的所有肮脏的事情):

public static void Try(
    Func<SqlConnection> connectionFactory,
    Action<SqlCommand> performer);

像这样使用:

Try(
    () => new SqlConnection(connectionString),
    cmd => {
             cmd.CommandText = "SELECT * FROM master.sys.messages";
             using (var reader = cmd.ExecuteReader()) {
                 // Do stuff
         }
    });
 类似资料:
  • 问题内容: 现在,我使用一个静态布尔值来告诉初始化何时发生。有没有更简单的方法知道我已经调用了initialize? 谢谢!!! 解决了!!!!非常感谢您的评论。您需要在扩展应用程序的类中初始化解析,然后将其作为应用程序(而不是其他活动)添加到清单文件中。 :) 这是我使用Parse的课程: 这是我的android清单文件 问题答案: 创建一个应用程序类,然后在onCreate中初始化解析。 在此

  • 问题内容: 假设我以这种方式配置了一个触发器: 触发器必须与另一个应用程序连接,并且如果存在任何问题(例如连接失败),则触发器应每10分钟重试任务五次,直到成功为止。有什么方法可以配置触发器使其工作? 问题答案: 来源 :自动重试Quartz中失败的作业 如果您想要一项不断尝试直到成功的工作,您要做的就是抛出一个带有标志的JobExecutionException,以通知调度程序在失败时再次触发它

  • 我使用SpringBoot和Webflux的反应式编程。我想重复对endpoint的一些调用,直到数据可用为止(将返回一些内容)。 我想调用直到displayCommand返回状态为。如何告诉Webflux我的链的这一部分应该被调用5次,例如,因为我的数据库中的数据应该在5-10秒后出现。。。 我认为当前代码会导致再次调用整个链,而不仅仅是调用我链的适当部分(

  • 当失去焦点时,我需要捕捉,我搜索了其他问题,但没有找到答案。 我这样使用 但是,这对我不起作用。

  • Android片段的onresume/onpause方法与主机活动的生命周期紧密耦合,如图所示。 我想知道的是如何检测碎片从/返回到应用程序的导航流内部。 示例: 假设我有主要活动和片段A、B和C。 编辑: 澄清:我想知道从片段B(类似于活动与onPause和onResume的工作方式)

  • 问题内容: 我有一个带有行的表格样式页面。每行都有一个复选框。我可以选中所有/很多复选框,然后单击“提交”,这是对每一行的Jquery ajax调用。 基本上,我为每一行都有一个表单,并且遍历所有选中的行并提交执行jquery ajax调用的表单。 所以我有一个按钮,它可以: 那么每一行都有: 该表格提交给processRow: 我想知道的是,通过这种方法,我可以判断出我所有的Ajax调用是否均已

  • 我有一些mocha/chai/chai-http测试,它们遵循下面的结构,但是,每当一个测试失败时,我会得到一个 ,我似乎不知道它的来源。 UnhandledPromiserEjectionWarning:未处理的承诺拒绝。此错误可能是由于不带catch块的异步函数内部引发的,或者是由于拒绝了未用。catch()处理的承诺。 我尝试在 后面添加一个 承诺.reject(err)/code>,但也不

  • 我想检查蒙哥数据库中是否已存在电子邮件ID。如果设置了 req.body.email,它将触发MongoDB查询,如果结果为真,则它应该在json中发送状态:true。 我已经尝试从外部发送res.json()User.findOne()仍然不起作用。我已经将接受标头设置为应用程序/json。仍然不起作用。 NodeJS上的代码 前端代码 错误日志