gpt4 book ai didi

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

转载 作者:IT王子 更新时间:2023-10-29 04:12:18 24 4
gpt4 key购买 nike

我有一个C#应用程序,它从运行在不稳定状态的SQL Server中获取数据。我无法解决环境问题,因此我需要尽可能优雅地处理它们。

为此,我想重试由于网络故障,由于重新引导而导致SQL服务器脱机,查询超时等基础架构故障等结果的操作。同时,我不想重试查询是否因逻辑错误而失败。我只希望那些将异常带给客户。

我的问题是:区分环境问题(连接断开,超时)和其他种类的异常(即使环境稳定也可能发生的逻辑错误)的最佳方法是什么?

C#中是否有一种常用的模式来处理这样的事情?例如,是否存在可以检查SqlConnection对象的属性以检测失败的连接?如果不是,解决此问题的最佳方法是什么?

对于它的值(value),我的代码没有什么特别的:

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

最佳答案

一个SqlException(可能)包装了多个SQL Server错误。您可以使用Errors属性遍历它们。每个错误都是SqlError:

foreach (SqlError error in exception.Errors)

每个 SqlError都有一个 Class属性,您可以使用该属性来大致确定是否可以重试(如果必须重新创建连接,则可以重试)。从 MSDN:
  • Class <10是针对您传递的信息中的错误的,然后(可能)如果您首先不正确输入,则无法重试。
  • 从11到16的Class是“由用户生成的”,那么如果用户首先没有更正他的输入,那么很可能您将无能为力。请注意,类16包含许多临时错误,而类13则用于死锁(由于EvZ),因此如果您逐一处理它们,则可以排除这些类。
  • 范围从17到24的Class是通用硬件/软件错误,您可以重试。当Class为20或更高时,您还必须重新创建连接。 22和23可能是严重的硬件/软件错误,24表示介质错误(应该警告用户,但如果只是“临时”错误,您可以重试)。

  • 您可以找到每个类 here的更详细的描述。

    通常,如果您使用其类处理错误,则不需要确切地知道每个错误(使用 error.Number属性或 exception.Number,这只是该列表中第一个 SqlError的快捷方式)。这样做的缺点是您可以在无用的情况下重试(否则错误将无法恢复)。我建议使用 两步方法:
  • 检查已知的错误代码(使用SELECT * FROM master.sys.messages列出错误代码)以查看要处理的内容(知道如何操作)。该 View 包含所有受支持语言的消息,因此您可能需要按msglangid列进行过滤(例如,英语为1033)。
  • 对于其他所有依赖错误类的内容,当Class为13或高于16时重试(如果20或更高则重新连接)。
  • 严重性高于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;
    }
    }

    将所有内容包装在 try/ finally中以正确处理连接。使用这个简单的,天真的 CanRetry()函数:
    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...
    }

    查找非严重错误列表 here的一些非常棘手的方法。

    通常,我将所有这些(样板)代码嵌入具有此签名的一种方法中(在其中我可以隐藏创建/处置/重新创建连接所做的所有脏事):
    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
    }
    });

    请注意,当您不使用SQL Server时,也可以使用框架(重试错误)(实际上,它可以用于许多其他操作,例如I/O和网络相关的东西),所以我建议编写一个通用函数并大量重复使用)。

    关于c# - 从C#调用SQL Server时知道何时重试或失败?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24041062/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com