4단계: ADO.NET을 사용하여 탄력적으로 SQL에 연결
이 항목에서는 사용자 지정 다시 시도 논리를 보여 주는 C# 코드 샘플을 제공합니다. 다시 시도 논리는 안정성을 제공합니다. 재시도 논리는 프로그램이 잠시 기다렸다가 재시도하는 경우 없어지는 경향이 있는 임시 오류 또는 일시적 오류를 정상적으로 처리하도록 디자인되었습니다.
일시적인 오류의 원인은 다음과 같습니다.
- 인터넷을 지원하는 네트워킹의 짧은 오류입니다.
- 클라우드 시스템은 쿼리가 전송되는 순간 리소스의 부하를 분산시킬 수 있습니다.
로컬 Microsoft SQL Server에 연결하기 위한 ADO.NET 클래스를 통해 Azure SQL Database에도 연결할 수 있습니다. 그러나 ADO.NET 클래스는 프로덕션 사용에 필요한 모든 견고성과 안정성을 자체적으로 제공할 수 없습니다. 클라이언트 프로그램은 일시적 오류가 발생할 수 있으며, 이 오류는 자동으로, 정상적으로 복구되고 자체적으로 계속되어야 합니다.
1단계: 일시적인 오류 식별
프로그램은 일시적인 오류와 영구 오류를 구분해야 합니다. 일시적 오류는 일시적인 네트워크 문제와 같이 단기간에 정리될 수 있는 오류 조건입니다. 영구 오류의 예는 프로그램에 대상 데이터베이스 이름의 철자가 잘못된 경우가 있습니다. 이러한 경우 "이러한 데이터베이스를 찾을 수 없음" 오류가 지속되며 짧은 기간 내에 지울 수 없습니다.
일시적인 오류로 분류되는 오류 번호 목록은 SQL Database 클라이언트 응용 프로그램의 오류 메시지에서 사용할 수 있습니다.
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 Database에 맞춰 조정되었으며, 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++)
{
try
{
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단계: 테스트 예외 Throw
샘플 코드에는 다음 항목이 포함됩니다.
- Number라는 속성이 이있는 TestSqlException이라는 이름의 소형 두 번째 클래스입니다.
//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 Database: 링크, 모범 사례 및 디자인 지침 커넥트 연결에 방문하세요.