上一篇文章:
步骤 3:使用 ADO.NET 连接到 SQL 的概念证明
本主题提供一个用于演示自定义重试逻辑的 C# 代码示例。 重试逻辑可提供可靠性。 重试逻辑旨在正常处理暂时错误或暂时性故障,在程序等待几秒并重试后,这种错误或故障往往会自行消失 。
暂时性故障的原因包括:
支持 Internet 的网络出现短暂故障。
云系统在收到你发送的查询后,可能会立即对其资源进行负载均衡。
用于连接本地 Microsoft SQL Server 的 ADO.NET 类也可以连接到 Azure SQL 数据库。 但是,ADO.NET 类本身无法提供生产环境中所需的稳定性和可靠性。 客户端程序可能会遇到暂时性故障,在此情况下,客户端程序应该能够自行从故障中静默正常恢复。
步骤 1:识别暂时性错误
程序必须区分暂时性错误与持久性错误。 暂时性错误是指可在短时间内消除的错误情况,如暂时性的网络问题。 举例说明持久性错误:如果你的程序的目标数据库名称拼写错误,那么在这种情况下,“找不到此类数据库”错误将持续存在,并且不会在短时间内清除。
SQL 数据库客户端应用程序的错误消息
中提供了归类为暂时性错误的错误号列表
步骤 2:创建并运行示例应用程序
本示例假定已安装 .NET Framework 4.6.2 或更高版本。 C# 代码示例包含一个名为 Program.cs 的文件。 下一节将提供其代码。
步骤 2.a:捕获和编译代码示例
可以使用以下步骤编译该示例:
在
免费的 Visual Studio Community 版本
中,基于 C# 控制台应用程序模板创建一个新项目。
“文件”>“新建”>“项目”>“已安装”>“模板”>“Visual C#”>“Windows”>“经典桌面”>“控制台应用程序”
将该项目命名为 RetryAdo2 。
打开“解决方案资源管理器”窗格。
查看项目的名称。
在项目中,在 Microsoft.Data.SqlClient 包上
添加 NuGet 依赖项
。
查看 Program.cs 文件的名称。
打开 Program.cs 文件。
将 Program.cs 文件的内容全部替换为下面的代码块中的代码。
单击菜单“生成”>“生成解决方案”。
步骤 2.b:复制并粘贴示例代码
将此代码粘贴到你的 Program.cs 文件中 。
然后,你必须编辑服务器名称、密码等字符串。 你可以在名为 GetSqlConnectionString 的方法中找到这些字符串。
注意:服务器名称的连接字符串适用于 Azure SQL 数据库,因为它包括 tcp: 的四个字符前缀。 但你可以调整服务器字符串以连接到 Microsoft SQL Server。
using System;
using System.Collections.Generic;
using Microsoft.Data.SqlClient;
using System.Threading;
namespace RetryAdo2;
public class Program
public static int Main(string[] args)
bool succeeded = false;
const int totalNumberOfTimesToTry = 4;
int retryIntervalSeconds = 10;
for (int tries = 1; tries <= totalNumberOfTimesToTry; tries++)
if (tries > 1)
Console.WriteLine(
"Transient error encountered. Will begin attempt number {0} of {1} max...",
tries,
totalNumberOfTimesToTry
Thread.Sleep(1000 * retryIntervalSeconds);
retryIntervalSeconds = Convert.ToInt32(retryIntervalSeconds * 1.5);
AccessDatabase();
succeeded = true;
break;
catch (SqlException sqlExc) {
if (TransientErrorNumbers.Contains(sqlExc.Number))
Console.WriteLine("{0}: transient occurred.", sqlExc.Number);
continue;
Console.WriteLine(sqlExc);
succeeded = false;
break;
catch (TestSqlException sqlExc) {
if (TransientErrorNumbers.Contains(sqlExc.Number))
Console.WriteLine("{0}: transient occurred. (TESTING.)", sqlExc.Number);
continue;
Console.WriteLine(sqlExc);
succeeded = false;
break;
catch (Exception e)
Console.WriteLine(e);
succeeded = false;
break;
if (!succeeded) {
Console.WriteLine("ERROR: Unable to access the database!");
return 1;
return 0;
/// <summary>
/// Connects to the database, reads,
/// prints results to the console.
/// </summary>
static void AccessDatabase() {
//throw new TestSqlException(4060); //(7654321); // Uncomment for testing.
using var sqlConnection = new SqlConnection(GetSqlConnectionString());
using var dbCommand = sqlConnection.CreateCommand();
dbCommand.CommandText =
SELECT TOP 3
ob.name,
CAST(ob.object_id as nvarchar(32)) as [object_id]
FROM sys.objects as ob
WHERE ob.type='IT'
ORDER BY ob.name;";
sqlConnection.Open();
var dataReader = dbCommand.ExecuteReader();
while (dataReader.Read())
Console.WriteLine(
"{0}\t{1}",
dataReader.GetString(0),
dataReader.GetString(1)
/// <summary>
/// You must edit the four 'my' string values.
/// </summary>
/// <returns>An ADO.NET connection string.</returns>
static private string GetSqlConnectionString()
// Prepare the connection string to Azure SQL Database.
var sqlConnectionSB = new SqlConnectionStringBuilder
// Change these values to your values.
DataSource = "tcp:myazuresqldbserver.database.windows.net,1433", //["Server"]
InitialCatalog = "MyDatabase", //["Database"]
UserID = "MyLogin", // "@yourservername" as suffix sometimes.
Password = "MyPassword",
// Adjust these values if you like. (ADO.NET 4.5.1 or later.)
ConnectRetryCount = 3,
ConnectRetryInterval = 10, // Seconds.
// Leave these values as they are.
IntegratedSecurity = false,
Encrypt = true,
ConnectTimeout = 30
return sqlConnectionSB.ToString();
static List<int> TransientErrorNumbers = new()
4060, 40197, 40501, 40613, 49918, 49919, 49920, 11001
/// <summary>
/// For testing retry logic, you can have method
/// AccessDatabase start by throwing a new
/// TestSqlException with a Number that does
/// or does not match a transient error number
/// present in TransientErrorNumbers.
/// </summary>
internal class TestSqlException : ApplicationException
internal TestSqlException(int testErrorNumber)
Number = testErrorNumber;
internal int Number { get; set; }
步骤 2.c:运行程序
RetryAdo2.exe 可执行文件未输入任何参数 。 若要运行 .exe:
打开一个控制台窗口,你已在其中编译了 RetryAdo2.exe 二进制文件。
在没有输入参数的情况下运行 RetryAdo2.exe。
database_firewall_rules_table 245575913
filestream_tombstone_2073058421 2073058421
filetable_updates_2105058535 2105058535
步骤 3:测试重试逻辑的方法
可以通过多种方式来模拟暂时性错误以测试重试逻辑。
步骤 3.a:引发测试异常
代码示例包括以下内容:
第二个小类,名为 TestSqlException ,其属性名为 Number 。
//throw new TestSqlException(4060);
,可以取消注释。
如果取消注释 throw 语句并重新编译,则下一次运行 RetryAdo2.exe 的 输出类似于下面的内容。
[C:\VS15\RetryAdo2\RetryAdo2\bin\Debug\]
>> RetryAdo2.exe
4060: transient occurred. (TESTING.)
Transient error encountered. Will begin attempt number 2 of 4 max...
4060: transient occurred. (TESTING.)
Transient error encountered. Will begin attempt number 3 of 4 max...
4060: transient occurred. (TESTING.)
Transient error encountered. Will begin attempt number 4 of 4 max...
4060: transient occurred. (TESTING.)
ERROR: Unable to access the database!
[C:\VS15\RetryAdo2\RetryAdo2\bin\Debug\]
步骤 3.b:重新测试持久性错误
若要证明代码正确地处理了持久性错误,请重新运行前面的测试,但不要使用 4060 这样的实际暂时性错误号。 请改用无意义的数字 7654321。 程序应将此视为持久性错误,并应绕过任何重试。
步骤 3.c:断开与网络的连接
从网络中断开客户端计算机。
对于台式机,请拔下网络电缆。
对于笔记本电脑,按下功能键的组合键以关闭网络适配器。
启动 RetryAdo2.exe,并等待控制台显示第一个暂时性错误,可能为 11001。
重新连接网络,RetryAdo2.exe 将继续运行。
在看到控制台报告已成功执行后续重试。
步骤 3.d:临时拼错服务器名称
暂时将 40615 作为另一个错误号添加到 TransientErrorNumbers ,然后重新编译。
在 new QC.SqlConnectionStringBuilder()
行上设置一个断点。
使用“编辑并继续” 功能在下面几行故意拼错服务器名称。
运行程序并返回到断点。
出现错误 40615。
修复拼写错误。
运行程序并成功完成。
删除 40615,然后重新编译。
若要了解其他最佳做法和设计指导原则,请访问连接到 SQL 数据库:链接、最佳做法和设计指导原则