.NET 데이터 액세스 아키텍처 가이드

Patterns and Practices home

관련 링크

patterns & practices Index (영문)

.NET: 응용 프로그램 및 서비스 디자인을 위한 응용 프로그램 아키텍처

Alex Mackman, Chris Brooks, Steve Busby, Ed Jezierski, Jason Hogg, Roberta Leibovitz(Modeled Computation) 및 Colin Campbell(Modeled Computation) Microsoft Corporation

2001년 10월

2003년 6월 업데이트

요약: 이 문서에서는 다중 계층 .NET 기반 응용 프로그램에서 ADO.NET 기반 데이터 액세스 계층을 구현하기 위한 지침을 제공합니다. 이 문서에서는 일반적인 데이터 액세스 작업 및 시나리오에 대해 집중적으로 설명하고 가장 적합한 접근 방식 및 기술 선택에 도움이 되는 지침을 제공합니다.

소개

.NET 기반 응용 프로그램을 위한 데이터 액세스를 디자인하는 경우 Microsoft ADO.NET을 데이터 액세스 모델로 사용해야 합니다. ADO.NET은 다양한 기능을 제공하며 느슨하게 연결된 다중 계층 웹 응용 프로그램 및 웹 서비스에 필요한 데이터 액세스 요구 사항을 지원합니다. 다른 다양한 기능의 개체 모델들과 같이 ADO.NET은 특정 문제를 해결할 수 있는 여러 방법을 제공합니다.

.NET 데이터 액세스 아키텍처 가이드는 가장 적합한 데이터 액세스 접근 방식을 선택하는 데 도움이 되는 정보를 제공합니다. 이 설명서에서는 폭 넓은 일반적 데이터 액세스 시나리오에 대해 설명하고 성능 팁을 제공하고 최상의 실행 방법을 처방합니다. 이 가이드에서는 또한 데이터베이스 연결 문자열을 저장하기에 가장 적합한 장소는 어디입니까? 연결 풀링을 어떻게 구현합니까? 트랜잭션 작업을 어떻게 수행합니까? 대규모 레코드에서 사용자 스크롤을 허용하려면 페이지를 어떻게 구현해야 합니까?와 같은 자주 묻는 질문에 대한 해답을 제공합니다.

이 가이드에서는 ADO.NET에 제공된 두 공급자 중 하나인 SQL Server .NET 데이터 공급자를 사용하여 Microsoft SQL Server에 액세스하기 위해 ADO.NET을 사용하는 것과 관련된 내용을 중점적으로 다룹니다. 필요에 따라 이 가이드에서는 다른 OLE DB 인식 데이터 원본에 액세스하기 위해 OLE DB .NET 데이터 공급자를 사용할 때 알고 있어야 하는 차이점들에 대해서도 자세히 다룹니다.

이 문서에 설명된 지침 및 최상의 실행 방법을 사용하여 개발된 데이터 액세스 구성 요소에 대한 확실한 구현을 위해서는 데이터 액세스 응용 프로그램 블록을 참조하십시오. 데이터 액세스 응용 프로그램 블록에는 구현에 대한 원본 코드가 포함되어 있으며 이 코드를 자신의 .NET 기반 응용 프로그램에 직접 사용할 수 있습니다.

.NET 데이터 액세스 아키텍처 가이드에는 다음 섹션이 포함됩니다.

이 문서의 대상 독자

이 문서는 .NET 기반 응용 프로그램을 작성하려는 응용 프로그램 설계자 및 엔터프라이즈 개발자를 위한 지침을 제공합니다. 자신의 업무가 다중 계층 .NET 기반 응용 프로그램의 데이터 계층을 디자인 및 개발하는 것이라면 이 문서를 읽어 보십시오.

기본 필수 지식

이 가이드를 통해 .NET 기반 응용 프로그램을 작성하려면 ActiveX Data Objects(ADO) 및/또는 OLE DB를 사용한 데이터 액세스 코드 개발 경험 뿐만 아니라 SQL Server 사용 경험이 필요합니다. 또한 .NET 플랫폼에 대한 관리 코드를 개발하는 방법을 이해해야 하며 ADO.NET 데이터 액세스 모델이 제공하는 기본적인 변경 사항에 대해서도 알고 있어야 합니다. .NET 개발에 대한 자세한 내용은 http://www.microsoft.com/korea/msdn/netframework/을 참조하십시오.

새로운 기능

이 문서 업데이트에는 데이터베이스 업데이트 수행, 형식있는 DataSets 사용 및 Null 데이터 필드 사용에 대한 섹션이 포함됩니다.

텍스트에 표시된 대로 이 가이드의 내용 중 일부는 특히 Microsoft Visual Studio 2003 개발 시스템 및 .NET Framework SDK 버전 1.1에 적용됩니다.

.NET 데이터 액세스 아키텍처 가이드 다운로드

.NET 데이터 액세스 아키텍처 가이드를 다운로드하려면 MS.com 다운로드 센터 (영문)를 클릭하십시오.

ADO.NET 소개

ADO.NET은 .NET 기반 응용 프로그램을 위한 데이터 액세스 모델입니다. ADO.NET을 사용하면 OLE DB 또는 ODBC 공급자가 있는 SQL Server 2000, Oracle 및 기타 데이터 원본과 같은 관계형 데이터베이스 시스템에 액세스할 수 있습니다. ADO.NET은 어느 정도 최신 ADO 기술의 발전을 나타냅니다. 하지만 ADO.NET은 느슨하게 연결된(그리고 내재적으로 연결이 해제된) 웹 응용 프로그램 특성을 위한 주요 변경 내용과 혁신 기술을 제공합니다. ADO와 ADO.NET을 비교하려면 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dndotnet/html/adonetprogmsdn.asp (영문)에서 MSDN 문서 "ADO 프로그래머를 위한 ADO.NET"을 참조하십시오

ADO.NET에서 제공되는 주요 변경 내용 중 하나는 ADO Recordset 개체가 DataTable, DataSet, DataAdapterDataReader 개체의 조합으로 바뀌었다는 점입니다. DataTable은 단일 테이블의 행 컬렉션을 나타내며 이러한 관점에서 볼 때 Recordset과 비슷합니다. DataSet은 여러 테이블을 하나로 바인딩하는 관계 및 제약 조건이 포함된 DataTable 개체 컬렉션을 나타냅니다. 실제로 DataSet은 XML 지원이 기본 제공되는 인-메모리 관계형 구조에 있습니다.

DataSet의 주요 특성 중 하나는 이를 채우는 데 사용될 수 있는 기본 데이터 원본에 대한 인식이 없다는 점입니다. DataSet은 데이터 컬렉션을 나타내기 위해 사용되는 연결이 끊긴 독립 실행형 엔터티이며 다중 계층 응용 프로그램의 여러 계층을 통해 구성 요소 간에 전달될 수 있습니다. 또한 XML 데이터 스트림으로 직렬화될 수 있기 때문에 유형이 다른 플랫폼 간의 데이터 전송에 이상적입니다. ADO.NET은 DataAdapter 개체를 사용하여 DataSet 및 기본 데이터 원본에 데이터를 연결합니다. DataAdapter 개체는 또한 이전에 Recordset과 연결된 향상된 일괄 처리 업데이트 기능을 제공합니다.

그림 1은 완전한 DataSet 개체 모델을 보여 줍니다.

그림 1.1 DataSet 개체 모델

.NET 데이터 공급자

ADO.NET은 .NET 데이터 공급자의 서비스에 의존합니다. 이러한 서비스는 기본 데이터 원본에 대한 액세스를 제공하며 네 개의 핵심 개체(Connection, Command, DataReaderDataAdapter)를 구성합니다.

현재까지 ADO.NET에는 브리지 공급자와 네이티브 공급자의 두 가지 종류의 공급자가 제공됩니다. OLE DB 및 ODBC에 대해 제공되는 것과 같이 브리지 공급자를 사용하면 이전의 데이터 액세스 기술을 위해 디자인된 데이터 라이브러리를 사용할 수 있습니다. 네이티브 공급자는 SQL Server 및 Oracle 공급자와 같이 부분적으로 추상 계층이 하나 적다는 이유로 인해 일반적으로 향상된 성능을 제공합니다.

  • SQL Server .NET Data Provider. 이 공급자는 Microsoft SQL Server 7.0 및 이후 데이터베이스를 위한 공급자입니다. 이 공급자는 SQL Server 액세스를 위해 최적화되어 있으며 SQL Server의 네이티브 데이터 전송 프로토콜을 사용하여 SQL Server와 직접 통신합니다.

SQL Server 7.0 또는 SQL Server 2000에 연결할 때는 항상 이 공급자를 사용하십시오.

  • Oracle .NET Data Provider. Oracle용 .NET Framework 데이터 공급자는 Oracle 클라이언트 연결 소프트웨어를 통해 Oracle 데이터 원본에 대한 데이터 액세스를 지원합니다. 이 데이터 공급자는 Oracle 클라이언트 소프트웨어 버전 8.1.7 이상을 지원합니다.
  • OLE DB .NET Data Provider. 이 공급자는 OLE DB 데이터 원본을 위한 관리 공급자입니다. 이 공급자는 데이터베이스와 통신할 때 OLE DB 계층을 통해 호출하기 때문에 SQL Server .NET Data Provider보다 효율성이 조금 떨어집니다. 이 공급자는 ODBC(Open Database Connectivity), MSDASQL을 위한 OLE DB 공급자를 지원하지 않습니다. ODBC 데이터 원본의 경우에는 대신 ODBC .NET Data Provider(아래 설명 참조)를 사용하십시오. ADO.NET과 호환 가능한 OLE DB 공급자 목록을 보려면 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconADONETProviders.asp (영문)를 참조하십시오.

현재 베타 테스트에 포함된 다른 .NET 데이터 공급자에는 다음이 포함됩니다.

  • ODBC .NET Data Provider. ODBC용 .NET Framework 데이터 공급자는 네이티브 ODBC DM(드라이버 관리자)을 사용하여 COM 상호 운용성으로 데이터 액세스를 지원합니다.
  • SQL Server 2000에서 XML을 검색하기 위한 관리 공급자. SQL Server 웹 업데이트 2(현재 베타에서 제공됨)용 XML에는 특히 SQL Server 2000에서 XML을 검색하기 위한 관리 공급자가 포함됩니다. 이 업데이트에 대한 자세한 내용은 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/fox7help/html/lngDefault_Property.asp (영문)을 참조하십시오.

다른 데이터 공급자에 대한 자세한 개요를 보려면 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconADONETProviders.asp (영문)의 .NET Framework 개발자 가이드에서 ".NET Framework 데이터 공급자"를 참조하십시오.

이름 공간 구성

각 .NET 데이터 공급자와 연결된 유형(클래스, 구성, 열거 등)은 해당 고유 이름 공간에 있습니다.

  • System.Data.SqlClient. SQL Server .NET Data Provider 유형을 포함합니다.
  • System.Data.OracleClient. Oracle .NET Data Provider 유형을 포함합니다.
  • System.Data.OleDb. OLE DB .NET Data Provider 유형을 포함합니다.
  • System.Data.Odbc. ODBC .NET Data Provider 유형을 포함합니다.
  • System.Data. DataSetDataTable과 같은 공급자 독립 유형을 포함합니다.

해당 관련 이름 공간 내에서 각 공급자는 Connection, Command, DataReaderDataAdapter 개체의 구현을 제공합니다. SqlClient 구현의 접두사는 "Sql"이고 OleDb 구현의 접두사는 "OleDb"입니다. 예를 들어 Connection 개체의 SqlClient 구현 이름은 SqlConnection이고 OleDb에 해당하는 이름은 OleDbConnection입니다. 이와 비슷하게 DataAdapter 개체의 두 가지 구체적인 구현 이름은 각각 SqlDataAdapterOleDbDataAdapter입니다.

이 설명서에서 제공되는 예는 SQL Server 개체 모델로부터 가져온 것입니다. 여기에 설명되지는 않았지만 Oracle/OLEDB 및 ODBC에서도 비슷한 기능이 제공됩니다.

일반 프로그래밍

다른 데이터 원본을 대상화할 가능성이 있고 데이터 원본 간에 코드를 이동하려는 경우에는 System.Data 이름 공간 내에 있는 IDbConnection, IDbCommand, IDataReaderIDbDataAdapter 인터페이스에 대한 프로그래밍을 고려하십시오. Connection, Command, DataReaderDataAdapter 개체의 모든 구현에서는 이러한 인터페이스가 지원되어야 합니다.

.NET 데이터 공급자에 대한 자세한 내용은 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconImplementingNETDataProvider.asp (영문)를 참조하십시오.

또한 응용 프로그램에 여러 데이터베이스에 액세스하기 위해 단일 개체 모델이 사용되는 경우 OLE DB 및 ODBC 연결 공급자가 대신 사용됩니다. 이 경우 응용 프로그램에 필요한 유연성, 데이터베이스 관련 기능이 필요한 정도를 응용 프로그램의 성능 요구 사항에 비추어 고려해야 합니다.

그림 2에서는 데이터 액세스 스택과 ADO.NET이 ADO 및 OLE DB를 비롯한 다른 데이터 액세스 기술과 관련된 방식을 보여 줍니다. 또한 두 개의 관리 공급자와 ADO.NET 모델 내의 주요 개체를 보여 줍니다.

그림 1.2 데이터 액세스 스택

ADO에서 ADO.NET으로 발전된 자세한 내용을 보려면 http://msdn.microsoft.com/msdnmag/issues/1100/adoplus/default.aspx (영문)에서 2000년 11월 MSDN Magazine에 있는 문서"ADO+ 소개: Microsoft .NET Framework를 위한 데이터 액세스 서비스"를 참조하십시오.

저장 프로시저와 직접적인 SQL

이 문서에 표시된 대부분의 코드 단편에서는 SqlCommand 개체를 사용하여 데이터베이스 조작을 수행하기 위한 저장 프로시저를 호출합니다. 일부 경우에는 저장 프로시저 이름이 SqlDataAdapter 개체에 직접 전달되기 때문에 SqlCommand 개체가 표시되지 않습니다. 이 경우에도 내부적으로는 SqlCommand 개체가 만들어집니다.

포함된 SQL 문 대신 저장 프로시저를 사용해야 하는 이유는 다음과 같이 여러 가지입니다.

  • 저장 프로시저를 사용하면 데이터베이스가 프로시저에서 사용되는 데이터 액세스를 최적화하고 이를 이후에 다시 사용할 수 있도록 캐시할 수 있기 때문에 일반적으로 향상된 성능을 제공합니다.
  • 저장 프로시저는 데이터베이스 내에서 개별적으로 보안을 설정할 수 있습니다. 기본 테이블에 대해 어떠한 권한을 부여하지 않더라도 저장 프로시저를 실행할 수 있는 권한을 클라이언트에 부여할 수 있습니다.
  • 배포된 구성 요소에서 하드 코딩된 SQL 문을 변경하는 것보다는 저장 프로시저를 수정하는 것이 일반적으로 더 쉽기 때문에 저장 프록시를 사용하면 보다 쉽게 유지 관리할 수 있습니다.
  • 저장 프로시저는 기본 데이터베이스 스키마로부터 추상 수준을 추가로 제공합니다. 저장 프로시저의 클라이언트는 저장 프로시저의 구현 세부 사항 및 기본 스키마의 영향을 받지 않습니다.
  • 클라이언트로부터 여러 요청을 전송하는 대신 SQL 문을 일괄 처리로 실행할 수 있기 때문에 저장 프로시저를 사용하면 네트워크 트래픽이 줄어들 수 있습니다.

SQL Server 온라인 설명서에서는 생성한 저장 프로시저의 이름 접두사에 "sp_"가 포함되지 않도록 권장합니다. 이 이름 접두사는 시스템 저장 프로시저 이름 접두사로 지정되어 있습니다. SQL Server는 항상 다음과 같은 순서로 sp_로 시작하는 저장 프로시저를 조회합니다.

  1. master 데이터베이스에서 저장 프로시저를 조회합니다.
  2. 제공된 한정자(데이터베이스 이름 또는 소유자)에 따라 저장 프로시저를 조회합니다.
  3. 소유자가 지정되지 않은 경우 dbo를 소유자로 사용하여 저장 프로시저를 조회합니다.

속성과 생성자 인수

생성자 인수를 통하거나 속성을 직접 설정하여 ADO.NET 개체의 특정 속성 값을 설정할 수 있습니다. 예를 들어 다음 코드 단편은 기능적으로 동일합니다.

// Use constructor arguments to configure command objectSqlCommand cmd = new SqlCommand( "SELECT * FROM PRODUCTS", conn );// The above line is functionally equivalent to the following// three lines which set properties explicitlysqlCommand cmd = new SqlCommand();cmd.Connection = conn;cmd.CommandText = "SELECT * FROM PRODUCTS";  

성능적인 관점에서 볼 때 .NET 개체에 대해 속성을 설정하고 가져오는 것이 COM 개체에 대해 비슷한 작업을 수행하는 것보다 효율적이기 때문에 두 접근 방식 간의 차이점은 무시할 수 있습니다.

두 방식은 개인적인 선호나 코딩 스타일에 따라 선택될 수 있습니다. 하지만 속성을 명시적으로 설정하면 특히 사용자가 ADO.NET 개체 모델에 익숙하지 않은 경우 코드를 더욱 쉽게 이해하고 디버깅할 수 있습니다.

참고과거에는 Microsoft Visual Basic 개발 시스템 개발자가 "Dim x As New..." 구성으로 개체를 만들지 않는 것이 일반적이었습니다. COM 기반에서는 이러한 코드를 사용할 경우 COM 개체의 생성 프로세스가 짧게 순환되어 미묘하거나 일부 확실한 버그가 발생할 수 있었습니다. 하지만 .NET 기반에서는 이러한 코드가 더 이상 문제가 되지 않습니다.

데이터베이스 연결 관리

데이터베이스 연결은 특히 다중 계층 웹 응용 프로그램에서 핵심적이고 비용이 높은 제한적 리소스를 나타냅니다. 접근 방식은 응용 프로그램의 전반적인 확장성에 중요한 영향을 줄 수 있기 때문에 연결을 올바르게 관리해야 합니다. 또한 연결 문자열을 저장할 위치에 대해서도 신중히 고려해야 합니다. 이를 위해서는 구성 가능한 보안 위치가 필요합니다.

데이터베이스 연결 및 연결 문자열을 관리할 때는 다음과 같은 문제를 해결해야 합니다.

  • 여러 클라이언트 간의 데이터베이스 연결 풀을 다중화하여 응용 프로그램 확장성을 실현합니다.
  • 구성 가능한 고성능 연결 풀링 전략을 채택합니다.
  • SQL Server에 액세스할 때 Windows 인증을 사용합니다.
  • 중간 계층에서의 가장을 방지합니다.
  • 연결 문자열을 안전하게 저장합니다.
  • 데이터베이스 연결을 늦게 열고 일찍 닫습니다.

이 섹션에서는 연결 풀링에 대해 설명하고 적합한 연결 풀링 전략을 선택할 수 있도록 도와줍니다. 이 섹션에서는 또한 데이터베이스 연결 문자열을 관리 및 저장하는 방법에 대해서도 고려합니다. 마지막으로 이 섹션에서는 연결이 제대로 닫히고 연결 풀로 돌아갈 수 있도록 보장하기 위해 사용할 수 있는 두 가지 코딩 패턴을 제공합니다.

연결 풀링 사용

데이터베이스 연결 풀링을 사용하면 응용 프로그램에서 데이터베이스에 새로운 연결을 반복적으로 설정하는 대신 풀에서 기존 연결을 다시 사용할 수 있습니다. 이 기술은 제한된 개수의 데이터베이스 연결이 매우 많은 수의 클라이언트를 지원하기 때문에 응용 프로그램의 확장성을 향상시켜 줍니다. 이 기술은 또한 새 연결을 설정하는 데 필요한 상당한 시간을 없애주기 때문에 성능을 향상시켜 줍니다.

ODBC와 OLE DB와 같은 데이터 액세스 기술은 여러 수준으로 구성할 수 있는 연결 풀링 형식을 제공합니다. 두 접근 방식 모두 데이터베이스 클라이언트 응용 프로그램에 대부분 투명하게 이뤄집니다. OLE DB 연결 풀링은 세션 또는 리소스 풀링이라고도 자주 부릅니다.

MDAC(Microsoft Data Access Components) 내의 풀링에 대한 일반적인 내용은 http://msdn.microsoft.com/SQL/sqlreldata/ADO.NET/default.aspx?pull=/library/en-us/dnmdac/html/pooling2.asp (영문)에서 "Microsoft Data Access Components 풀링"을 참조하십시오.

ADO.NET 데이터 공급자는 각 공급자에 따라 다른 정확한 방식인 투명한 연결 풀링을 제공합니다. 이 섹션에서는 다음과 관련된 연결 풀링에 대해 설명합니다.

  • SQL Server .NET Data Provider
  • Oracle .NET Data Provider
  • OLE DB .NET Data Provider
  • ODBC .NET Data Provider

SQL Server .NET Data Provider 풀링

SQL Server .NET Data Provider를 사용하는 경우 공급자에서 제공되는 연결 풀링 지원을 사용하십시오. 이러한 지원은 관리 코드 내에서 공급자에 의해 내부적으로 구현되어 트랜잭션을 지원하는 효율적인 메커니즘입니다. 풀은 응용 프로그램 도메인별 기준으로 생성되며 응용 프로그램이 언로드되기 전에는 삭제되지 않습니다.

이러한 형식의 연결 풀링을 투명하게 사용할 수 있지만 사용자는 풀의 관리 방법과 연결 풀링을 세부적으로 조정하는 데 사용할 수 있는 다양한 구성 옵션에 대해 알아야 합니다.

대부분의 경우 SQL Server .NET 데이터 공급자에 대한 기본 연결 풀링 설정 만으로도 응용 프로그램에 충분할 수 있습니다. .NET 기반 응용 프로그램의 개발 및 테스트 중에는 연결 풀 크기를 수정해야 할지 여부를 확인하기 위해 임시 트래픽 패턴을 시뮬레이션하는 것이 좋습니다.

확장 가능한 고성능 응용 프로그램을 개발 중인 개발자는 연결 사용 시간을 최소화하고 데이터를 검색하거나 업데이트하는 데 걸리는 시간 동안만 연결이 열려 있도록 유지해야 합니다. 연결이 닫히면 연결 풀로 돌아가서 다시 사용할 수 있습니다. 이 경우 데이터베이스에 대한 실제 연결은 제공되지 않지만 연결 풀링이 해제된 경우 데이터베이스에 대한 실제 연결이 닫힙니다.

개발자는 참조 범위가 벗어날 때 연결이 반드시 닫힐 필요가 없기 때문에 연결을 해제하기 위해 가비지 수집기에 의존하지 않도록 주의해야 합니다. 이는 연결 누출의 일반적인 원인이며 새로운 연결이 요청될 때 연결 예외를 일으킵니다.

SQL Server .NET Data Provider 연결 풀링 구성

연결 문자열을 통해 제공된 일련의 이름-값 쌍을 사용하여 연결 풀링을 구성할 수 있습니다. 예를 들어 풀링을 설정할지 여부(기본값은 설정), 최대 및 최소 풀 크기, 지연된 연결 열기 요청이 차단할 수 있는 시간 등을 구성할 수 있습니다. 다음은 최대 및 최소 풀 크기를 구성하는 연결 문자열 예입니다.

"Server=(local); Integrated Security=SSPI; Database=Northwind; Max Pool Size=75; Min Pool Size=5"  

연결이 열리고 풀이 생성되면 여러 연결이 풀에 추가되어 연결 개수를 구성된 최소 수준으로 만듭니다. 이후에는 구성된 최대 풀 개수까지 풀에 연결을 추가할 수 있습니다. 최대 개수에 도달하면 새로운 연결 열기 요청이 구성 가능한 기간 동안 큐에 지정됩니다.

풀 크기 선택

수 천의 클라이언트에 대한 동시 연결을 관리하는 대규모 시스템의 경우에는 반드시 최대 임계값을 설정할 수 있어야 합니다. 시스템에 최적의 풀 크기를 확인하려면 연결 풀링과 응용 프로그램의 성능을 모니터링해야 합니다. 최적 크기는 또한 SQL Server를 실행 중인 하드웨어에 따라서도 달라집니다.

개발 중에는 연결 누출을 찾기 위해 기본 최대 풀 크기(현재 100)를 줄이는 것이 좋습니다.

최소 풀 크기를 설정하는 경우 처음 연결하는 몇 개의 클라이언트에는 성능이 높아지지만 이 수준을 만들기 위해 풀을 처음으로 채울 때 약간의 성능 오버헤드가 발생할 수 있습니다. 새 연결을 만드는 프로세스는 직렬화되어 있어서 처음 풀을 채울 때 동시 요청으로 서버가 넘쳐나지 않습니다.

연결 풀링 모니터링에 대한 자세한 내용은 이 문서에서 연결 풀링 모니터링 섹션을 참조하십시오. 연결 풀링 연결 문자열 키워드에 대한 전체 목록을 보려면http://www.microsoft.com/korea/msdn/library/ko-kr/cpguide/html/cpconConnectionPoolingForSQLServerNETDataProvider.aspx의 .NET Framework 개발자 가이드에서 ".NET Framework Data Provider for SQL Server를 위한 연결 풀링"을 참조하십시오.

추가 정보

SQL Server .NET Data Provider 연결 풀링을 사용할 때는 다음에 주의하십시오.

  • 연결은 연결 문자열에 있는 정확히 일치하는 알고리즘을 통해 풀링됩니다. 풀링 메커니즘은 이름-값 쌍 간의 공백에도 영향을 받습니다. 예를 들어 다음 두 연결 문자열은 두 번째 문자열에 추가 공백 문자가 포함되기 때문에 두 개의 서로 다른 풀을 발생시킵니다.
    SqlConnection conn = new SqlConnection(         "Integrated Security=SSPI;Database=Northwind");conn.Open(); // Pool A is createdSqlConmection conn = new SqlConnection(         "Integrated Security=SSPI ; Database=Northwind");conn.Open(); // Pool B is created (extra spaces in string)  
  • 연결 풀은 여러 트랜잭션 관련 풀과 트랜잭션에 현재 등록되지 않은 연결에 대한 한 개의 풀로 구분됩니다. 특정 트랜잭션 컨텍스트와 연결된 스레드의 경우 적합한 풀(해당 트랜잭션에 등록된 연결 포함)의 연결이 반환됩니다. 이렇게 하면 등록된 연결 작업을 투명하게 처리할 수 있습니다.

OLE DB .NET Data Provider 풀링

OLE DB .NET Data Provider는 OLE DB 리소스 풀링의 기본 서비스를 사용하여 연결을 풀링합니다. 리소스 풀링 구성을 위해서는 여러 옵션이 지원됩니다.

  • 연결 문자열을 사용하여 리소스 풀링을 구성, 설정 및 해제할 수 있습니다.
  • 레지스트리를 사용할 수 있습니다.
  • 리소스 풀링을 프로그래밍 방식으로 구성할 수 있습니다.

레지스트리 관련 배포 문제를 방지하려면 레지스트리를 사용하여 OLE DB 리소스 풀링을 구성하지 마십시오.

OLE DB 리소스 풀링에 대한 자세한 내용은 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/oledb/htm/oledbabout_the_ole_db_documentation.asp (영문)에서 OLE DB Programmer's Reference의 19장 "OLE DB Services"에 있는 "Resource Pooling"을 참조하십시오.

풀링된 개체로 연결 풀링 관리

Windows DNA 개발자의 경우 OLE DB 리소스 풀링 및/또는 ODBC 연결 풀링을 해제하고 데이터베이스 연결 풀링을 위한 기술로 COM+ 개체 풀링을 사용하는 것이 좋습니다. 이에 대한 두 가지 주요 이유는 다음과 같습니다.

  • 풀 크기 및 임계값을 명시적으로 구성할 수 있습니다(COM+ 카탈로그).
  • 성능이 향상됩니다. 풀링된 개체 접근 방식은 네이티브 풀링보다 두 배 이상 성능이 뛰어납니다.

하지만 SQL Server .NET Data Provider는 풀링을 내부적으로 사용하기 때문에 이 공급자를 사용할 경우 자체 개체 풀링 메커니즘을 개발할 필요가 없습니다. 따라서 수동 트랜잭션 등록과 관련된 복잡한 문제들을 피할 수 있습니다.

뛰어난 구성 및 향상된 성능을 활용하기 위해 OLE DB .NET Data Provider를 사용할 경우에는 COM+ 개체 풀링을 고려할 수 있습니다. 이러한 목적으로 풀링된 개체를 개발할 경우에는 OLE DB 리소스 풀링 및 자동 트랜잭션 등록을 해제해야 합니다(예: 연결 문자열에 "OLE DB Services=-4" 포함). 풀링된 개체 구현 내에서 트랜잭션 등록을 처리해야 합니다.

연결 풀링 모니터링

응용 프로그램에서 사용되는 연결 풀링을 모니터링할 경우 SQL Server에 제공되는 프로파일러 도구를 사용하거나 Microsoft Windows 2000 운영 체제에 제공되는 성능 모니터 도구를 사용할 수 있습니다.

SQL Server 프로파일러를 사용하여 연결 풀링을 모니터링하려면

  1. 시작을 클릭하고 프로그램, Microsoft SQL Server를 차례로 가리킨 후 프로파일러를 클릭하여 프로파일러를 시작합니다.
  2. 파일 메뉴에서 새로 만들기를 가리킨 다음 추적을 클릭합니다.
  3. 연결 세부 정보를 제공한 다음 확인을 클릭합니다.
  4. 추적 속성 대화 상자에서 이벤트 탭을 클릭합니다.
  5. 선택한 이벤트 클래스 목록에서 Audit LoginAudit Logout 이벤트가 Security Audit 아래에 표시되어 있는지 확인합니다. 추적을 명확하게 만들려면 목록에서 다른 모든 이벤트를 제거합니다.
  6. 실행을 클릭하여 추적을 시작합니다. 연결이 설정되면 Audit Login 이벤트가 표시되고 연결이 닫히면 Audit Logout 이벤트가 표시됩니다.

성능 모니터를 사용하여 연결 풀링을 모니터링하려면

  1. 시작을 클릭하고 프로그램, 관리 도구를 차례로 가리킨 후 성능을 클릭하여 성능 모니터를 시작합니다.
  2. 배경 그래프를 마우스 오른쪽 단추로 클릭한 후 카운터 추가를 클릭합니다.
  3. 성능 개체 드롭다운 목록에서 SQL Server: General Statistics를 클릭합니다.
  4. 표시된 목록에서 User Connections를 클릭합니다.
  5. 추가를 클릭한 다음 닫기를 클릭합니다.

보안 관리

데이터베이스 연결 풀링을 사용하면 응용 프로그램의 전반적 성능이 향상되지만 데이터베이스에서 더 이상 보안을 관리할 수 없습니다. 이러한 이유는 연결 풀링을 지원하기 위해 연결 문자열이 동일해야 하기 때문입니다. 사용자별 기반으로 데이터베이스 작업을 추적해야 하는 경우 사용자 ID를 전달하고 데이터베이스에서 사용자 작업을 수동으로 로그할 수 있는 매개 변수를 추가하도록 고려하십시오. 각 작업에 이 매개 변수를 추가해야 합니다.

Windows 인증 사용

SQL Server에 연결할 때는 다양한 장점을 제공하는 Windows 인증을 사용하는 것이 좋습니다.

  • 별개의 SQL Server 보안 모델 대신 단일 Windows 보안 모델을 사용하기 때문에 쉽게 보안을 관리할 수 있습니다.
  • 연결 문자열에 사용자 이름과 암호를 포함시키지 않아도 됩니다.
  • 사용자 이름과 암호가 네트워크 상에서 일반 텍스트로 전달되지 않습니다.
  • 암호 만료 기간, 최소 길이, 여러 잘못된 로그온 요청 이후 계정 잠금 등을 통해 로그인 보안이 향상됩니다.

Windows 인증을 사용하여 SQL Server에 액세스할 때는 다음 지침을 따르십시오.

  • 성능상의 제약 고려. 성능 테스트에 따르면 SQL Server 인증을 사용할 때와 비교하여 Windows 인증을 사용할 경우 풀링된 데이터베이스 연결을 여는 시간이 더 오래 걸리는 것으로 밝혀졌습니다. .NET 런타임 버전 1.1은 SQL Server 보안과 Windows 인증 간의 성능 차이를 줄여 주지만 SQL Server 인증이 여전히 더 빠릅니다.

    하지만 Windows 인증의 비용이 더 높더라도 명령 또는 저장 프로시저를 실행하는 데 걸리는 시간과 비교할 때 이러한 성능상의 단점은 상대적으로 중요하지 않습니다. 따라서 대부분의 경우 Windows 인증을 사용한 보안 상의 장점이 이러한 약간의 성능 저하를 뛰어 넘습니다. 결정을 내리기 전에 응용 프로그램에 필요한 성능 요구 사항을 확인하십시오.

  • 중간 계층에서의 가장 방지. Windows 인증에는 데이터베이스 액세스를 위한 Windows 계정이 필요합니다. 중간 계층에서 가장을 사용하는 것이 논리적으로 보일 수 있지만 연결 풀링이 제한되고 응용 프로그램의 확장성에 심각한 영향을 줄 수 있으므로 이러한 방식은 피하십시오.

    이 문제를 해결하기 위해서는 인증된 보안 주체 대신 특정 역할을 나타내는 각 계정에서 제한된 개수의 Windows 계정을 가장하십시오.

    예를 들어 다음과 같은 접근 방식을 사용할 수 있습니다.

    1. 두 개의 Windows 계정을 만들고 하나는 읽기 작업용으로 사용하고 다른 하나는 쓰기 작업용으로 사용합니다. (또는 응용 프로그램 관련 역할을 미러링하기 위한 별개의 계정을 사용할 수도 있습니다. 예를 들어 인터넷 사용자를 위해 하나의 계정을 사용하고 내부 작업자 및/또는 관리자를 위한 다른 하나의 계정을 사용할 수 있습니다.)
    2. 각 계정을 SQL Server 데이터베이스 역할에 매핑하고 각 역할에 필요한 데이터베이스 권한을 설정하십시오.
    3. 데이터베이스 작업을 수행하기 전에 가장할 Windows 계정을 확인하기 위해 데이터베이스 액세스 계층에서 응용 프로그램 논리를 사용하십시오.
    참고각 계정은 동일 도메인 또는 트러스트된 도메인에서 IIS(인터넷 정보 서비스) 및 SQL Server에 있는 도메인 계정이어야 합니다. 또는 각 컴퓨터에 동일한 이름 및 암호로 일치하는 계정을 만들 수 있습니다.
  • 네트워크 라이브러리에 TCP/IP 사용. SQL Server 7.0 및 이후 버전은 모든 네트워크 라이브러리에 대해 Windows 인증을 지원합니다. TCP/IP를 사용하여 구성, 성능 및 확장성과 관련된 장점을 얻으십시오. TCP/IP 사용에 대한 자세한 내용은 이 문서에서 방화벽을 통해 연결 섹션을 참조하십시오.

보안 ASP.NET 및 웹 응용 프로그램에 대한 일반 개발 지침을 보려면 다음 Microsoft patterns & practices 지침을 참조하십시오.

연결 문자열 저장

데이터베이스 연결 문자열을 저장하기 위해서는 다양한 유연성 및 보안성을 제공하는 옵션 중에서 선택할 수 있습니다. 원본 코드 내에서 연결 문자열을 하드 코딩하면 최상의 성능을 제공할 수 있지만 파일 시스템 캐싱을 사용하면 파일 시스템에서 외부적으로 문자열을 저장하는 것과 관련된 성능 감소를 무시할 수 있는 수준으로 만들어 줍니다. 관리자 구성을 지원하는 외부 연결 문자열에 의해 제공되는 추가 유연성은 거의 모든 경우에 선호됩니다.

연결 문자열 저장소를 위한 접근 방식을 선택할 경우 가장 중요한 두 가지 고려 사항은 성능과 밀접하게 연관된 보안성 및 구성 용이성입니다.

데이터베이스 연결 문자열 저장을 위해서는 다음 위치 중에서 선택할 수 있습니다.

SQL Server 액세스를 위해 Windows 인증을 사용할 경우 연결 문자열에서 강력한 사용자 이름 및 암호를 피할 수 있습니다. 보안 요구 사항에 따라 더욱 엄격한 방식이 필요한 경우 연결 문자열을 암호화된 형식으로 저장하십시오.

ASP.NET 웹 응용 프로그램의 경우 연결 문자열을 Web.config 파일 내에 암호화된 형식으로 저장하면 보안적이고 구성 가능한 솔루션이 제공됩니다.

참고연결 문자열에서 Persist Security Info라는 명명된 값을 False로 설정하여 암호와 같은 보안상 중요한 세부 정보가 SqlConnection 또는 OleDbConnection 개체의 ConnectionString 속성으로 인해 반환되지 않도록 방지합니다.

다음 하위 섹션에서는 연결 문자열 저장을 위해 여러 옵션을 사용하는 방법에 대해 설명하고 각 접근 방식의 장점과 단점에 대해 설명합니다. 이렇게 하면 특정 응용 프로그램 시나리오에 따라 정확한 결정을 내릴 수 있습니다.

참고구성 응용 프로그램 관리 블록을 사용하면 데이터베이스 연결에서 복잡한 계층적 데이터까지 구성 설정을 관리할 수 있습니다. 자세한 내용은http://msdn.microsoft.com/practices (영문)를 참조하십시오.

XML 응용 프로그램 구성 파일 사용

<appSettings> 요소를 사용하여 응용 프로그램 구성 파일의 사용자 지정 설정 섹션에 데이터베이스 연결 문자열을 저장할 수 있습니다. 이 요소는 다음 조각에 표시된 것과 같이 임의의 키-값 쌍을 지원합니다.

<configuration> <appSettings>  <add key="DBConnStr"     value="server=(local);Integrated Security=SSPI;database=northwind"/> </appSettings></configuration>  
참고<appSettings> 요소는 <configuration> 요소 아래에 표시되며 <system.web> 아래에 직접 표시되지 않습니다.

장점

  • 배포 용이성. 연결 문자열이 일반적인 .NET xcopy 배포를 통해 구성 파일과 함께 배포됩니다.
  • 프로그래밍 방식의 액세스 용이성. ConfigurationSettings 클래스의 AppSettings 속성은 런타임 시 구성된 데이터베이스 연결 문자열을 쉽게 읽을 수 있도록 해줍니다.
  • 동적 업데이트 지원(ASP.NET에만 해당). 관리자가 Web.config 파일에서 연결 문자열을 업데이트하면 연결 문자열을 다음에 액세스할 때 변경 내용이 적용되어 클라이언트가 구성 요소를 다음에 사용할 때 클라이언트가 구성 요소를 사용하여 데이터 액세스 요청을 수행할 수 있습니다.

단점

  • 보안. ASP.NET ISAPI(Internet Server Application Programming Interface) DDL(동적 연결 라이브러리)은 클라이언트가 .config 파일 확장명의 파일을 직접 액세스하지 못하도록 방해하며, NTFS 권한을 사용하여 액세스를 추가로 제한할 수 있지만 프론트 엔드 웹 서버에서 일반 텍스트로 이러한 세부 사항을 저장하지 않아야 할 수 있습니다. 추가 보안을 위해서는 연결 문자열을 구성 파일에 암호화된 형식으로 저장하십시오.

추가 정보

  • System.Configuration.ConfigurationSettings 클래스의 정적 AppSettings 속성을 사용하여 사용자 지정 응용 프로그램 설정을 검색할 수 있습니다. 이러한 내용은 DBConnStr이라는 이전에 설명한 사용자 지정 키를 사용하는 다음 코드 단편에 표시되어 있습니다.
    using System.Configuration;private string GetDBaseConnectionString(){  return ConfigurationSettings.AppSettings["DBConnStr"];}  
  • .NET Framework 응용 프로그램 구성에 대한 자세한 내용은 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconConfiguringNETFrameworkApplications.asp (영문)를 참조하십시오.

UDL 파일 사용

OLE DB .NET 데이터 공급자는 해당 연결 문자열에서 UDL(Universal Data Link) 파일 이름을 지원합니다. OleDbConnection 개체에 대해 생성 인수를 사용하여 연결 문자열을 전달하거나 개체의 ConnectionString 속성을 사용하여 연결 문자열을 설정할 수 있습니다.

참고SQL Server .NET Data Provider는 해당 연결 문자열에서 UDL 파일을 지원하지 않습니다. 따라서 이러한 접근 방식은 OLE DB .NET Data Provider를 사용하는 경우에만 사용할 수 있습니다.

OLE DB 공급자에 대해 연결 문자열에서 UDL 파일을 참조하려면 "File Name=name.udl"을 사용합니다.

장점

  • 표준 접근 방식. 이미 연결 문자열 관리에 UDL 파일을 사용하고 있을 수도 있습니다.

단점

  • 성능. UDL을 포함하는 연결 문자열이 읽혀지고 연결이 열릴 때마다 구문 분석됩니다.
  • 보안. UDL 파일이 일반 텍스트로 저장됩니다. 이러한 파일은 NTFS 파일 권한을 사용하여 보호할 수 있지만 이렇게 하면 .config 파일과 동일한 문제가 발생합니다.
  • SqlClient은 UDL 파일을 지원하지 않음. 이 접근 방식은 SQL Server 7.0 이상에 액세스하는 데 사용하는 SQL Server .NET Data Provider에서 지원되지 않습니다.

추가 정보

  • 관리를 지원하려면 관리자에게 UDL 파일에 대한 읽기/쓰기 액세스 권한이 있는지 확인하고 응용 프로그램을 실행하는 데 사용되는 ID에 읽기 액세스 권한이 있는지 확인하십시오. ASP.NET 웹 응용 프로그램의 경우 사용자는 시스템측 구성 파일(Machine.config)의 <processModel> 요소를 사용하여 대체할 수도 있지만 응용 프로그램 작업자 프로세서는 기본적으로 SYSTEM 계정을 사용하여 실행됩니다. 또한 선택적으로 지정한 계정에서 Web.config 파일의 <identity> 요소를 사용하여 가장할 수도 있습니다.
  • 웹 응용 프로그램의 경우 웹에서 파일을 다운로드할 수 있도록 만들어 주는 UDL 파일을 가상 디렉터리에 배치하지 않도록 확인하십시오.
  • 이러한 기능 및 기타 보안 관련 ASP.NET 기능에 대한 자세한 내용은 http://msdn.microsoft.com/library/en-us/dnbda/html/authaspdotnet.asp (영문)에서 "ASP.NET의 인증: .NET 보안 지침"을 참조하십시오.

Windows 레지스트리 사용

또한 Windows 레지스트리의 사용자 지정 키를 사용하여 연결 문자열을 저장할 수도 있지만 배포 문제로 인해 이 방법은 권장되지 않습니다.

장점

  • 보안. ACL(액세스 제어 목록)을 사용하여 선택한 레지스트리 키에 대한 액세스를 관리할 수 있습니다. 더 높은 수준의 보안을 위해서는 데이터 암호화를 고려하십시오.
  • 프로그래밍 방식의 액세스 용이성. .NET 클래스를 사용하여 레지스트리의 문자열을 읽을 수 있습니다.

단점

  • 배포. xcopy 배포의 장점을 어느 정도 잃더라도 응용 프로그램에 관련 레지스트리 설정을 배포해야 합니다.

사용자 지정 파일 사용

사용자 지정 파일을 사용하여 연결 문자열을 저장할 수 있습니다. 하지만 이 기술은 장점이 없으며 권장되지 않습니다.

장점

  • 없음.

단점

  • 추가 코딩. 이 방법을 사용하려면 추가 코딩이 필요하며 사용자가 동시성 문제를 명시적으로 해결해야 합니다.
  • 배포. 파일을 다른 ASP.NET 응용 프로그램 파일과 함께 복사해야 합니다. 웹에서 다운로드할 수 없도록 파일을 ASP.NET 응용 프로그램 디렉터리 또는 하위 디렉터리에 두지 마십시오.

생성 인수 및 COM+ 카탈로그 사용

데이터베이스 연결 문자열을 COM+ 카탈로그에 저장하고 개체 생성 문자열을 사용하여 개체에 자동으로 전달할 수 있습니다. COM+는 개체를 인스턴스화한 다음 개체의 Construct 메서드를 즉시 호출하여 구성된 생성 문자열을 제공합니다.

참고이 방법은 서비스되는 구성 요소에서만 작동됩니다. 관리 구성 요소에 분산 트랜잭션 지원이나 개체 풀링과 같은 기타 서비스가 사용되는 경우에만 고려하십시오.

장점

  • 관리. 관리자는 구성 요소 서비스 MMC 스냅인을 사용하여 연결 문자열을 쉽게 구성할 수 있습니다.

단점

  • 보안. 사용자가 COM+ 역할로 액세스를 제한할 수 있지만 COM+ 카탈로그는 비-보안 저장소 영역으로 간주되기 때문에 연결 문자열을 일반 텍스트로 유지 관리하도록 사용되어서는 안됩니다.
  • 배포. COM+ 카탈로그의 항목은 .NET 기반 응용 프로그램과 함께 배포되어야 합니다. 분산 트랜잭션 또는 개체 풀링과 같은 다른 엔터프라이즈 서비스를 사용하는 경우 데이터베이스 연결 문자열을 카탈로그에 저장하면 이러한 기타 서비스를 지원하도록 COM+ 카탈로그를 배포해야 하기 때문에 추가 배포 오버헤드가 발생하지 않습니다.
  • 구성 요소를 서비스해야 함. 서비스되는 구성 요소에 대해서만 생성 문자열을 사용할 수 있습니다. 단순히 생성 문자열을 설정하기 위해 구성 요소를 서비스하여 ServicedComponent로부터 구성 요소의 클래스를 파생해서는 안됩니다.
중요연결 문자열의 보안은 반드시 필요합니다. SQL 인증의 경우 연결에는 사용자 이름과 암호가 포함됩니다. 공격자가 웹 서버에서 보안 코드의 취약성을 악용하거나 구성 정보에 대한 액세스를 얻는 경우 데이터베이스가 취약해집니다. 이를 방지하기 위해 연결 문자열을 암호화해야 합니다. 일반 텍스트의 연결 문자열을 암호화하기 위해 사용할 수 있는 여러 방법에 대한 설명을 보려면 http://www.microsoft.com/practices (영문)에서 제공되는 웹 응용 프로그램 보안 향상: 위협과 대처 방안을 참조하십시오.

추가 정보

연결 사용 패턴

사용 중인 .NET 데이터 공급자와는 관계 없이 다음을 항상 수행해야 합니다.

  • 가능한 한 늦게 데이터베이스 연결을 엽니다.
  • 가능한 한 짧은 기간 동안 연결을 사용합니다.
  • 가능한 한 빨리 연결을 닫습니다. Close 또는 Dispose 메서드를 통해 닫지 않으면 연결이 풀로 반환되지 않습니다. 또한 연결이 끊어진 상태인 것으로 발견된 경우에도 연결을 닫아야 합니다. 이렇게 해야 연결이 풀로 반환되어 무효로 표시됩니다. 개체 풀러는 풀을 주기적으로 검색하여 무효로 표시된 개체가 있는지 확인합니다.

메서드 반환 전에 연결이 닫혀 있도록 보장하려면 아래의 두 코드 예제에 설명된 접근 방식 중 하나를 고려하십시오. 첫 번째 예제에서는 finally 블록을 사용합니다. 두 번째 코드에서는 C# using 문을 사용하여 개체의 Dispose 메서드가 호출되도록 보장합니다.

다음 코드는 finally 블록이 연결을 닫도록 보장합니다. Visual Basic .NET에서는 구조화된 예외 처리를 지원하기 때문에 Visual Basic .NET 및 C# 모두에서 이 방식이 작동됩니다.

public void DoSomeWork(){  SqlConnection conn = new SqlConnection(connectionString);  SqlCommand cmd = new SqlCommand("CommandProc", conn );  cmd.CommandType = CommandType.StoredProcedure;  try  {    conn.Open();    cmd.ExecuteNonQuery();  }  catch (Exception e)  {    // Handle and log error  }  finally  {    conn.Close();  }}  

다음 코드는 C# using 문을 사용하는 다른 방식을 보여 줍니다. Visual Basic .NET에서는 using 문이나 기타 비슷한 기능을 제공하지 않습니다.

public void DoSomeWork(){  // using guarantees that Dispose is called on conn, which will  // close the connection.  using (SqlConnection conn = new SqlConnection(connectionString))  {    SqlCommand cmd = new SqlCommand("CommandProc", conn);    fcmd.CommandType = CommandType.StoredProcedure;    conn.Open();    cmd.ExecuteQuery();  }}  

현재 연결에서 다른 작업이 수행되기 전에 닫혀야 하는 SqlDataReader 또는 OleDbDataReader와 같은 다른 개체에 이 방식을 적용할 수도 있습니다.

오류 처리

생성된 ADO.NET 오류는 .NET Framework에서 고유하게 제공되는 기본 구조화된 예외 처리 지원을 통해서 처리됩니다. 따라서 응용 프로그램의 다른 곳에서 오류를 처리하는 것과 동일한 방식으로 데이터 액세스 코드 내에서 오류를 처리합니다. 표준 .NET 예외 처리 구문 및 기술을 통해 예외를 발견하고 처리할 수 있습니다.

이 섹션에서는 강력한 데이터 액세스 코드를 개발하는 방법을 보여 주고 데이터 액세스 오류를 처리하는 방법에 대해 설명합니다. 또한 SQL Server .NET 데이터 공급자와 관련된 특정 예외 처리 지침을 제공합니다.

.NET 예외

.NET 데이터 공급자는 데이터베이스 관련 오류 조건을 데이터 액세스 코드에서 처리해야 하는 표준 예외 유형으로 변환합니다. 데이터베이스 관련 오류 세부 정보는 관련 예외 개체의 속성을 통해 제공됩니다.

모든 .NET 예외 유형은 궁극적으로 System 이름 공간의 기본 Exception 클래스로부터 파생됩니다. .NET 데이터 공급자에서 공급자 특성 예외 유형이 발생합니다. 예를 들어 SQL Server .NET 데이터 공급자에서 SQL Server가 오류 조건을 반환할 때마다 SqlException 개체가 발생하게 됩니다. 이와 비슷하게 OLE DB .NET 데이터 공급자에서는 기본 OLE DB 공급자에 의해 제공된 세부 사항이 포함되는 OleDbException 유형의 예외가 발생합니다.

그림 3은 .NET 데이터 공급자 예외 계층을 보여 줍니다. OleDbException 클래스는 모든 COM Interop 예외의 기본 클래스인 ExternalException으로부터 파생됩니다. 이 개체의 ErrorCode 속성에는 OLE DB에서 보장되는 COM HRESULT가 저장됩니다.

그림 1.3 .NET 데이터 공급자 예외 계층

.NET 예외 발견 및 처리

데이터 액세스 예외 조건을 처리하려면 데이터 액세스 코드를 try 블록 내에 두고 적합한 필터와 함께 catch 블록을 사용하여 생성된 예외를 발견합니다. 예를 들어 SQL Server .NET 데이터 공급자를 사용하여 데이터 액세스 코드를 작성할 경우 다음 코드에 표시된 것과 같이 SqlException 유형의 예외를 발견합니다.

try{  // Data access code}catch (SqlException sqlex) // more specific{}catch (Exception ex) // less specific{}  

다른 필터 조건이 포함된 하나 이상의 catch 문을 제공하는 경우 가장 구체적인 유형부터 가장 추상적인 유형의 순서로 문을 정렬하십시오. 이렇게 하면 모든 특정 예외 유형에 대해 가장 구체적인 유형의 catch 블록이 실행됩니다.

SqlException 클래스는 세부적인 예외 조건이 포함된 속성을 제공합니다. 여기에는 다음이 포함됩니다.

  • 오류를 기술하는 텍스트가 포함된 Message 속성.
  • 오류 유형을 고유하게 식별하는 오류 번호가 포함된 Number 속성.
  • 오류의 호출 상태에 대한 추가 정보가 포함된 State 속성. 이 속성은 일반적으로 특정 오류 조건의 특정 발생을 나타내는 데 사용됩니다. 예를 들어 단일 저장 프로시저로 한 줄 이상에서 동일한 오류를 생성할 수 있는 경우 이 상태를 사용하여 특정 발생을 식별해야 합니다.
  • SQL Server가 발생시키는 오류에 대한 자세한 오류 정보가 포함된 Errors 컬렉션. Errors 컬렉션에는 항상 적어도 하나 이상의 SqlError 개체 유형이 포함됩니다.

다음 코드 단편에서는 SQL Server .NET 데이터 공급자를 사용하여 SQL server 오류 조건을 처리하는 방법을 보여 줍니다.

using System.Data;using System.Data.SqlClient;using System.Diagnostics;// Method exposed by a Data Access Layer (DAL) Componentpublic string GetProductName( int ProductID ){  SqlConnection conn = null;  // Enclose all data access code within a try block  try  {    conn = new SqlConnection(        "server=(local);Integrated Security=SSPI;database=northwind");    conn.Open();    SqlCommand cmd = new SqlCommand("LookupProductName", conn );    cmd.CommandType = CommandType.StoredProcedure;    cmd.Parameters.Add("@ProductID", ProductID );    SqlParameter paramPN =          cmd.Parameters.Add("@ProductName", SqlDbType.VarChar, 40 );    paramPN.Direction = ParameterDirection.Output;    cmd.ExecuteNonQuery();    // The finally code is executed before the method returns    return paramPN.Value.ToString();    }  catch (SqlException sqlex)  {    // Handle data access exception condition    // Log specific exception details    LogException(sqlex);    // Wrap the current exception in a more relevant    // outer exception and re-throw the new exception    throw new DALException(                  "Unknown ProductID: " + ProductID.ToString(), sqlex );  }  catch (Exception ex)  {    // Handle generic exception condition . . .    throw ex;  }  finally  {    if(conn != null) conn.Close(); // Ensures connection is closed  }}// Helper routine that logs SqlException details to the // Application event logprivate void LogException( SqlException sqlex ){  EventLog el = new EventLog();  el.Source = "CustomAppLog";  string strMessage;  strMessage = "Exception Number : " + sqlex.Number +                "(" + sqlex.Message + ") has occurred";  el.WriteEntry( strMessage );  foreach (SqlError sqle in sqlex.Errors)  {    strMessage = "Message: " + sqle.Message +                 " Number: " + sqle.Number +                 " Procedure: " + sqle.Procedure +                 " Server: " + sqle.Server +                 " Source: " + sqle.Source +                 " State: " + sqle.State +                 " Severity: " + sqle.Class +                 " LineNumber: " + sqle.LineNumber;    el.WriteEntry( strMessage );  }}  

SqlException catch 블록 내에서 코드는 LogException 지원 함수를 사용하여 예외 세부 정보를 처음부터 기록합니다. 이 함수는 foreach 문을 사용하여 Errors 컬렉션 내의 공급자 관련 세부 정보를 열거하고 오류 세부 정보를 오류 로그에 기록합니다. 그런 다음 catch 블록 내의 코드는 GetProductName 호출자에게 더욱 유용한 DALException 유형의 예외 내에서 SQL Server 관련 예외를 래핑합니다. 예외 처리기는 throw 키워드를 사용하여 이 예외를 호출자에게 전파합니다.

추가 정보

저장 프로시저로부터 오류 생성

Transact-SQL(T-SQL)은 사용자 지정 오류를 발생시키고 이를 클라이언트로 반환하는 데 사용할 수 있는 RAISERROR(스펠링 주의) 함수를 제공합니다. ADO.NET 클라이언트의 경우 SQL Server .NET Data Provider는 이러한 데이터베이스 오류를 가로채어 SqlError 개체로 변환합니다.

RAISERROR 함수를 사용하는 가장 간단한 방법은 메시지 텍스트를 첫 번째 매개 변수로 포함시킨 다음 아래 코드 단편에 표시된 것과 같이 심각도 및 상태 매개 변수를 지정하는 것입니다.

RAISERROR( 'Unknown Product ID: %s', 16, 1, @ProductID )  

이 예에서 대체 매개 변수는 현재 제품 ID를 오류 메시지 텍스트의 일부로 반환하기 위해 사용됩니다. 매개 변수 two는 메시지 심각도를 나타내며 매개 변수 three는 메시지 상태입니다.

추가 정보

  • 메시지 텍스트 하드 코딩을 피하기 위해서는 sp_addmessage 시스템 저장 프로시저나 SQL Server Enterprise Manager를 사용하여 sysmessages 테이블에 고유 메시지를 추가할 수 있습니다. 그런 다음 RAISERROR 함수에 전달된 ID를 사용하여 메시지를 참조할 수 있습니다. 정의한 메시지 ID는 다음 코드 단편에서와 같이 50,000보다 커야 합니다.
    RAISERROR( 50001, 16, 1, @ProductID )  
  • RAISERROR 함수와 관련된 전체 세부 정보를 보려면 SQL Server 온라인 도움말 색인에서 RAISERROR를 조회하십시오.

적합한 심각도 수준 사용

오류 심각도 수준을 신중하게 선택하고 각 수준의 영향을 확인하십시오. SQL Server 2000에서 발생하는 문제 유형을 식별하는 데에는 0부터 25 사이의 오류 심각도 수준 범위가 사용됩니다. 클라이언트 코드에서 사용자는 SqlException 클래스의 Errors 컬렉션 내에서 SqlError 개체의 Class 속성을 검사하여 오류의 심각도를 가져올 수 있습니다. 표 1은 여러 심각도 수준의 영향 및 의미를 나타냅니다.

표 1. 오류 심각도 수준 - 영향 및 의미

심각도 수준연결이 닫힘
SqlException 생성Meaning
10 이하아니오아니오오류 조건이 아닐 수도 있는 정보 메시지.
11–16아니오수정한 입력 데이터로 작업을 다시 시도하는 등의 사용자 작업을 통해 수정할 수 있는 오류.
17–19아니오리소스 또는 시스템 오류.
20–25치명적인 시스템 오류(하드웨어 오류 포함). 클라이언트 연결이 종료됩니다.

자동 트랜잭션 제어

SQL Server .NET 데이터 공급자에서는 심각도 수준이 10 이상인 오류에 대해 SqlException이 발생합니다. 자동(COM+) 트랜잭션의 일부인 구성 요소에서 SqlException이 발견되면 구성 요소에서 트랜잭션을 취소해야 합니다. 이러한 과정은 자동으로 진행되거나 수동으로 진행될 수도 있으며 메서드가 AutoComplete 특성으로 표시되어 있는지 여부에 따라 달라집니다.

자동 트랜잭션 컨텍스트에서 SqlException을 처리하는 방법에 대한 자세한 내용은 이 문서의 트랜잭션 결과 확인 섹션을 참조하십시오.

정보 메시지 검색

10 이하의 심각도 수준은 정보 메시지를 나타내는 데 사용되며 SqlException을 발생시키지 않습니다.

정보 메시지를 검색하려면

  • 이벤트 처리기를 만들고 SqlConnection 개체에 의해 제공되는 InfoMessage 이벤트를 구독합니다. 이 이벤트의 위임은 다음 코드 단편에 표시되어 있습니다.
    public delegate void SqlInfoMessageEventHandler( object sender,                                                      SqlInfoMessageEventArgs e );  

메시지 데이터는 이벤트 처리기에 전달된 SqlInfoMessageEventArgs 개체를 통해 제공됩니다. 이 개체는 정보 메시지당 하나의 SqlError 개체 집합을 포함하는 Errors 속성을 제공합니다. 다음 코드 단편에서는 정보 메시지를 로그하는 데 사용되는 이벤트 처리기를 등록하는 방법을 보여 줍니다.

public string GetProductName( int ProductID ){  SqlConnection conn = null;  try  {    conn = new SqlConnection(        "server=(local);Integrated Security=SSPI;database=northwind");    // Register a message event handler    conn.InfoMessage += new SqlInfoMessageEventHandler( MessageEventHandler );    conn.Open();    // Setup command object and execute it    . . .  }  catch (SqlException sqlex)  {    // log and handle exception    . . .  }  finally  {    if(conn != null) conn.Close();  }}// message event handlervoid MessageEventHandler( object sender, SqlInfoMessageEventArgs e ){  foreach( SqlError sqle in e.Errors )  {    // Log SqlError properties    . . .  }}  

성능

이 섹션에서는 다양한 일반 데이터 액세스 시나리오를 소개하고 각 시나리오에 대해 ADO.NET 데이터 액세스 코드 측면에서 최고의 성능과 확장성을 제공하는 솔루션에 대한 세부 정보를 제공합니다. 필요에 따라 성능, 기능 및 개발 효율 등이 비교됩니다. 이 섹션에서는 다음과 같은 기능적 시나리오를 고려합니다.

  • . 결과 집합을 검색하고 검색된 행을 반복 처리.
  • . 지정된 기본 키로 단일 행을 검색.
  • . 지정된 행에서 단일 항목을 검색.
  • . 특정 기본 키의 행이 존재하는지 여부 확인. 이 시나리오는 간단한 부울 반환으로도 충분한 단일 항목 조회 시나리오의 변형 시나리오입니다.

여러 행 검색

이 시나리오에서는 테이블 형식의 데이터 집합을 검색하고 검색된 행을 반복해서 작업을 수행할 수 있습니다. 예를 들어 일련의 데이터 집합을 검색하고 연결이 끊어진 상태로 이를 작업하고 웹 서비스 등을 통해 클라이언트 응용 프로그램에 XML 문서로 전달할 수 있습니다. 또는 데이터를 HTML 테이블 형식으로 표시할 수 있습니다.

가장 적합한 데이터 액세스 방식을 결정하기 위해서는 (연결이 끊어진) DataSet 개체에 대해 추가 유연성이 필요하거나 B2C(Business-to consumer) 웹 응용 프로그램에서 데이터를 표현하는 데 적합한 SqlDataReader 개체로 제공되는 기본 성능이 필요한지 여부를 고려하십시오. 그림 4는 두 가지 기본 시나리오를 보여 줍니다.

참고DataSet을 채우는 데 사용되는 SqlDataAdapter는 내부적으로 SqlDataReader를 사용하여 데이터에 액세스합니다.

그림 1.4. 여러 행 데이터 액세스 시나리오

옵션 비교

데이터 원본으로부터 여러 행을 검색할 때는 다음 옵션을 사용할 수 있습니다.

  • DataSet 또는 DataTable을 생성하기 위해 SqlDataAdapter 개체 사용.
  • 읽기 전용, 전진 전용 데이터 스트림을 제공하기 위해 SqlDataReader 사용.
  • XML 데이터의 읽기 전용, 전진 전용 데이터 스트림을 제공하기 위해 XmlReader 사용.

SqlDataReaderDataSet/DataTable의 선택은 기본적으로 기능 및 성능 중 어떤 것이 중요한지 여부에 따라 달라집니다. SqlDataReader는 최적의 성능을 제공하며 DataSet은 추가 기능 및 유연성을 제공합니다.

데이터 바인딩

이러한 세 가지 개체는 모두 데이터 바인딩 컨트롤의 데이터 원본 역할을 수행할 수 있지만 DataSetDataTableSqlDataReader보다 더 다양한 컨트롤에 대한 데이터 원본으로 사용될 수 있습니다. 이러한 이유는 DataSetDataTableIListSource(IList 생성)를 구현하는 데 비해 SqlDataReaderIEnumerable을 구현하기 때문입니다. 데이터 바인딩이 가능한 여러 WinForm 컨트롤에는 IList를 구현하는 데이터 원본이 필요합니다.

이러한 차이는 각 개체 유형이 디자인된 시나리오 유형이 다르기 때문입니다. DataSet(DataTable 포함)은 웹 및 데스크톱(WinForm) 시나리오 모두에 적합한 연결이 끊긴 리치 구조입니다. 반면에 데이터 판독기는 최적화된 전진 전용 데이터 액세스가 필요한 웹 응용 프로그램에 최적화되어 있습니다.

바인딩하려는 특정 컨트롤 유형에 대한 데이터 원본 요구 사항을 확인하십시오.

응용 프로그램 계층 간 데이터 전달

DataSet은 선택적으로 XML로 조작될 수 있는 데이터의 관계형 뷰를 제공하며 응용 프로그램 계층과 구성 요소 간에 연결이 끊긴 상태로 캐시된 데이터 복사본을 전달하도록 허용합니다. 하지만 SqlDataReaderDataSet 생성과 관련된 성능 및 메모리 오버헤드를 방지하기 때문에 최적의 성능을 제공합니다. DataSet 개체를 만들면 DataTable, DataRowDataColumn 개체를 포함한 여러 하위 개체와 이러한 하위 개체의 컨테이너로 사용되는 컬렉션 개체가 만들어진다는 점에 유의하십시오.

DataSet 사용

다음 경우에 SqlDataAdapter 개체로 채워지는 DataSet을 사용하십시오.

  • 응용 프로그램 내의 다른 구성 요소 또는 계층에 전달할 수 있도록 연결 해제된 메모리 상주 데이터 캐시가 필요합니다.
  • XML 또는 비-XML 조작을 위해 데이터에 대한 인-메모리 관계형 뷰가 필요합니다.
  • 여러 데이터베이스, 테이블 또는 파일과 같은 여러 데이터 원본에서 검색된 데이터를 사용합니다.
  • 검색된 행 일부 또는 전체를 업데이트하고 SqlDataAdapter의 일괄 업데이트 기능을 사용합니다.
  • IList를 지원하는 데이터 원본이 필요한 컨트롤에 대해 데이터 바인딩을 수행합니다.
참고자세한 내용은 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnbda/html/BOAGag.asp (영문)의 MSDN 웹 사이트에서 "데이터 계층 구성 요소 디자인 및 계층 간 데이터 전달"을 참조하십시오.

추가 정보

DataSet 또는 DataTable을 생성하기 위해 SqlDataAdapter를 사용하는 경우 다음에 유의하십시오.

  • 데이터베이스 연결을 명시적으로 열거나 닫을 필요가 없습니다. SqlDataAdapter Fill 메서드는 데이터베이스 연결을 연 다음 반환 전에 연결을 닫습니다. 연결이 이미 열려 있는 경우 Fill은 연결을 열린 상태로 둡니다.
  • 다른 목적으로 연결이 필요한 경우 Fill 메서드를 호출하기 전에 연결을 여십시오. 그러면 불필요한 열기/닫기 작업을 방지하고 성능상의 장점을 얻을 수 있습니다.
  • 동일 SqlCommand 개체를 반복적으로 사용하여 같은 명령을 여러 번 실행할 수 있지만 다른 명령을 실행할 경우에는 동일 SqlCommand 개체를 다시 사용하지 마십시오.
  • SqlDataAdapter를 사용하여 DataSet 또는 DataTable을 채우는 방법을 보여 주는 코드 예제를 보려면 부록에 있는 SqlDataAdapter를 사용하여 여러 행을 검색하는 방법을 참조하십시오.

SqlDataReader 사용

다음 경우에 SqlCommand 개체의 ExecuteReader 메서드를 호출하여 SqlDataReader를 사용합니다.

  • 단일 캐시로는 너무 많아서 유지 관리가 어려운 대량의 데이터 볼륨을 처리합니다.
  • 응용 프로그램의 메모리 사용량을 줄입니다.
  • DataSet과 관련된 개체 생성 오버헤드를 방지합니다.
  • IEnumerable을 구현하는 데이터 원본을 지원하는 컨트롤에서 데이터 바인딩을 수행합니다.
  • 데이터 액세스의 효율을 높이고 최적화합니다.
  • BLOB(Binary Large Object) 열을 포함하는 열을 읽습니다. 데이터를 한 번에 모두 풀링하는 대신 SqlDataReader를 사용하여 데이터베이스에서 관리 가능한 청크의 BLOB 데이터를 풀링할 수 있습니다. BLOB 데이터 처리에 대한 자세한 내용은 이 문서의 BLOB 처리 섹션을 참조하십시오.

추가 정보

SqlDataReader를 사용하는 경우 다음에 유의하십시오.

  • 데이터베이스에 대한 기본 연결이 열린 상태로 유지되고 데이터 판독기가 활성화된 경우 다른 목적에 이러한 연결을 사용할 수 없습니다. 가능한 한 빨리 SqlDataReader에서 Close를 호출합니다.
  • 연결당 하나의 데이터 판독기만 가능합니다.
  • 데이터 판독기를 종료할 때 연결을 명시적으로 닫거나 CommandBehavior.CloseConnection 열거 값을 ExecuteReader 메서드에 전달하여 SqlDataReader 개체에 연결 수명을 연결할 수 있습니다. 이렇게 하면 SqlDataReader가 닫힐 때 연결이 닫히도록 표시합니다.
  • 판독기를 사용하여 데이터에 액세스할 때는 열 데이터를 읽을 때 필요한 유형 변환 양을 줄일 수 있기 때문에 열의 기본 데이터 유형을 아는 경우 형식있는 접근자 메서드(예: GetInt32GetString)를 사용합니다.
  • 데이터가 불필요하게 서버에서 클라이언트로 풀링되지 않도록 하려면 판독기를 닫고 남은 결과를 삭제할 때 판독기에서 Close를 호출하기 전에 명령 개체의 Cancel 메서드를 호출합니다. Cancel을 호출하면 결과가 서버에서 삭제되어 불필요하게 클라이언트로 풀링되지 않도록 보장합니다. 반대로 데이터 판독기에서 Close를 호출하면 판독기가 남은 결과를 불필요하게 풀링하여 데이터 스트림을 비게 만듭니다.
  • 출력을 가져오거나 저장 프로시저로부터 반환된 값을 반환하고 SqlCommand 개체의 ExecuteReader 메서드를 사용 중인 경우 출력 및 반환 값을 사용하기 전에 판독기에서 Close 메서드를 호출해야 합니다.
  • SqlDataAdapter를 사용하는 방법을 보여 주는 코드 예제를 보려면 부록에 있는 SqlDataReader를 사용하여 여러 행을 검색하는 방법을 참조하십시오.

XmlReader 사용

다음 경우에 SqlCommand 개체의 ExecuteXmlReader 메서드를 호출하여 XmlReader를 사용합니다.

  • 검색된 데이터를 XML로 처리하지만 DataSet을 만드는 성능 오버헤드를 발생시키지 않고 연결 해제된 데이터 캐시가 필요합니다.
  • XML 조각(즉, 루트 요소가 없는 XML 문서)을 유연한 방식으로 데이터베이스에서 검색하도록 허용하는 SQL Server 2000 FOR XML 절의 기능을 사용합니다. 예를 들어 이 방식을 사용하면 정확한 요소 이름, 요소 또는 특성 중심 스키마의 사용 여부, XML 데이터로 스키마를 반환할지 여부 등을 지정할 수 있습니다.

추가 정보

XmlReader를 사용하는 경우 다음에 유의하십시오.

  • XmlReader에서 데이터를 읽는 동안 연결이 열린 상태로 유지되어야 합니다. SqlCommand 개체의 ExecuteXmlReader 메서드는 현재 CommandBehavior.CloseConnection 열거 값을 지원하지 않으므로 판독기를 종료할 때 연결을 명시적으로 닫아야 합니다.
  • XmlReader를 사용하는 방법을 보여 주는 코드 예제를 보려면 부록에 있는 XmlReader를 사용하여 여러 행을 검색하는 방법을 참조하십시오.

단일 행 검색

이 시나리오에서는 데이터 원본에서 지정된 열 집합이 포함된 단일 데이터 행을 검색할 수 있습니다. 예를 들어 고객 ID를 사용하여 관련된 고객 세부 정보를 조회하거나 제품 ID를 사용하여 제품 정보를 검색할 수 있습니다.

옵션 비교

데이터 원본에서 검색된 단일 행으로 데이터 바인딩을 수행하려는 경우 앞에서 설명한 여러 행 검색 및 반복 시나리오에서 설명된 것과 동일한 방법으로 SqlDataAdapter를 사용하여 DataSet 또는 DataTable을 채울 수 있습니다. 하지만 특별히 DataSet/DataTable 기능이 필요하지 않으면 이러한 개체를 만들지 않아야 합니다.

단일 행을 검색하려는 경우 다음 옵션 중 하나를 사용하십시오.

두 옵션 모두 서버에 결과 집합을 만들고 클라이언트에 DataSet을 만드는 불필요한 오버헤드를 방지합니다. 각 방식의 성능 차이는 스트레스 수준과 데이터베이스 연결 풀링의 설정 여부에 따라 달라집니다. 데이터베이스 연결 풀링이 설정된 경우 성능 테스트에서는 스트레스 조건이 높은 상태에서(동시 연결 200 이상) 저장 프로시저 방식이 SqlDataReader 방식보다 거의 30퍼센트 정도 뛰어난 성능을 보여 줍니다.

저장 프로시저 출력 매개 변수 사용

연결 풀링을 설정한 다중 계층 웹 응용 프로그램에서 단일 행을 검색하려는 경우 저장 프로시저 출력 매개 변수를 사용하십시오.

추가 정보

저장 프로시저 출력 매개 변수를 사용하는 방법을 보여 주는 코드 예제를 보려면 부록에 있는 저장 프로시저 출력 매개 변수를 사용하여 단일 행을 검색하는 방법을 참조하십시오.

SqlDataReader 사용

다음 경우에 SqlDataReader를 사용합니다.

  • 데이터 값 외에도 메타데이터가 필요합니다. 데이터 판독기의 GetSchemaTable 메서드를 사용하여 열 메타데이터를 가져올 수 있습니다.
  • 연결 풀링을 사용하지 않습니다. 연결 풀링이 해제된 경우 모든 스트레스 조건에서는 SqlDataReader를 사용하는 것이 좋습니다. 성능 테스트에서는 200개의 브라우저 연결 시 저장 프로시저 방식보다 20퍼센트 정도 성능이 뛰어난 것으로 밝혀졌습니다.

추가 정보

SqlDataReader를 사용하는 경우 다음에 유의하십시오.

  • 쿼리에서 단일 행만 반환하는 것으로 알려진 경우 SqlCommand 개체의 ExecuteReader 메서드를 호출할 때 CommandBehavior.SingleRow 열거 값을 사용합니다. OLE DB .NET Data Provider와 같은 일부 공급자는 이 힌트를 사용하여 성능을 최적화합니다. 예를 들어 이 공급자는 비용이 보다 높은 IRowset 대신 가능하면 IRow 인터페이스를 사용하여 바인딩을 수행합니다. 이 인수는 SQL Server .NET 데이터 공급자에 영향을 주지 않습니다.
  • SQL Server 명령에 출력 매개 변수 또는 반환 값이 포함된 경우 DataReader를 닫기 전에는 이를 사용할 수 없습니다.
  • SqlDataReader 개체를 사용할 때는 GetStringGetDecimal과 같이 항상 SqlDataReader 메서드의 형식있는 접근자 메서드를 통해 출력 매개 변수를 검색하십시오. 이렇게 하면 불필요한 형식 변환을 방지할 수 있습니다.
  • .NET Framework 버전 1.1에는 DataReader가 읽기 전에 결과를 반환하는지 여부를 확인할 수 있는 HasRows라는 추가 DataReader 속성이 포함되어 있습니다.
  • SqlDataReader 개체를 사용하여 단일 행을 검색하는 방법을 보여 주는 코드 예제를 보려면 부록에 있는 SqlDataReader를 사용하여 단일 행을 검색하는 방법을 참조하십시오.

단일 항목 검색

이 시나리오에서는 데이터의 단일 항목을 검색할 수 있습니다. 예를 들어 해당 ID에 따른 단일 제품 이름 또는 해당 고객 이름에 따른 단일 고객 신용 등급을 조회할 수 있습니다. 이러한 시나리오에서는 일반적으로 단일 항목을 검색할 때 DataSet 또는 심지어 DataTable의 오버헤드도 발생시키지 않아야 합니다.

또한 단순히 특정 행이 데이터베이스에 존재하는지 여부를 확인할 수도 있습니다. 예를 들어 새로운 사용자가 웹 사이트에 등록할 때 선택한 사용자 이름이 이미 존재하는지 여부를 확인해야 합니다. 이 경우는 단일 항목 조회의 특수한 경우이지만, 이 경우 간단한 부울 반환으로도 충분합니다.

옵션 비교

데이터 원본에서 데이터의 단일 항목을 검색할 때는 다음 옵션을 고려하십시오.

  • 저장 프로시저와 함께 SqlCommand 개체의 ExecuteScalar 메서드를 사용합니다.
  • 저장 프로시저 출력을 사용하거나 매개 변수를 반환합니다.
  • SqlDataReader 개체를 사용합니다.

ExecuteScalar 메서드는 단일 값만 반환하는 쿼리에 맞게 디자인되어 있으므로 데이터 항목을 직접 반환합니다. 이 메서드는 저장 프로시저 출력 매개 변수나 SqlDataReader 방식에 필요한 것보다 필요한 코드가 적습니다.

성능적인 관점에서 볼 때 테스트에서 저장 프로시저 방식이 높고 낮은 스트레스 조건(동시 브라우저 연결 100개 미만에서 200개 이상까지)에서 일관적인 성능을 제공하는 것으로 밝혀졌기 때문에 저장 프로시저 출력을 사용하거나 매개 변수를 반환해야 합니다.

추가 정보

단일 항목을 검색할 때 다음을 고려 하십시오.

방화벽을 통해 연결

방화벽을 통해 SQL Server에 연결하도록 인터넷 응용 프로그램을 구성할 수 있습니다. 예를 들어 여러 웹 응용 프로그램 및 해당 방화벽의 핵심 아키텍처 구성 요소는 내부 네트워크로부터 프런트 엔드 웹 서버를 격리시키는 데 사용되는 주변 네트워크(DMZ 또는 비무장지대)입니다.

방화벽을 통해 SQL Server에 연결하려면 방화벽, 클라이언트 및 서버의 특정 구성이 필요합니다. SQL Server는 구성을 지원하는 클라이언트 네트워크 유틸리티 및 서버 네트워크 유틸리티 프로그램을 제공합니다.

네트워크 라이브러리 선택

방화벽을 통해 연결할 때 구성을 단순화하려면 SQL Server TCP/IP 네트워크 라이브러리를 사용하십시오. 이 라이브러리는 SQL Server 2000 설치 시 기본으로 설치됩니다. 이전 버전의 SQL Server를 사용하는 경우에는 각각 클라이언트 네트워크 유틸리티 및 서버 네트워크 유틸리티를 사용하여 클라이언트 및 서버 모두에서 기본 네트워크 라이브러리로 TCP/IP를 구성했는지 확인합니다.

구성 장점 외에도 TCP/IP 라이브러리 결과를 사용하면 다음과 같은 장점이 있습니다.

  • 많은 데이터 볼륨에 대해 향상된 성능과 확장성을 활용할 수 있습니다.
  • 명명된 파이프와 관련된 추가 보안 문제를 방지할 수 있습니다.

TCP/IP로 클라이언트 및 서버 컴퓨터를 구성해야 합니다. 대부분의 방화벽은 트래픽 흐름을 허용하는 일련의 포트를 제한하기 때문에 SQL Server에서 사용되는 포트 번호에 주의해야 합니다.

서버 구성

SQL Server의 기본 인스턴스는 포트 1433으로 수신합니다. 또한 SQL 클라이언트가 네트워크의 다른 SQL Server를 찾을 수 있도록 허용하는 데에는 UDP 포트 1434가 사용됩니다. 하지만 SQL Server 2000의 명명된 인스턴스는 서버가 처음 시작될 때 포트 번호를 동적으로 할당합니다. 네트워크 관리자는 방화벽에서 포트 번호 범위를 열지 않기를 바라기 때문에 방화벽에서 SQL Server의 명명된 인스턴스를 사용할 때는 서버 네트워크 유틸리티를 사용하여 특정 포트 번호에서 수신하도록 인스턴스를 구성하십시오. 그런 다음 관리자는 서버 인스턴스가 수신하는 특정 IP 주소 및 포트 번호에 대한 트래픽을 허용하도록 방화벽을 구성할 수 있습니다.

참고클라이언트 네트워크 라이브러리가 사용하는 원본 포트는 1024-5000 범위 내에 동적으로 할당됩니다. 이러한 동작은 TCP/IP 클라이언트 응용 프로그램에 있어서 표준 방식이지만 이렇게 하려면 이 범위 내의 모든 포트에 대한 트래픽을 허용하도록 방화벽을 구성해야 합니다. SQL Server에서 사용되는 포트에 대한 자세한 내용은 Microsoft 기술 자료 문서 287932, "INF: 방화벽을 통한 SQL Server 연결에 필요한 TCP 포트"를 참조하십시오.

명명된 인스턴스의 동적 검색

SQL Server가 수신하는 기본 포트 번호를 변경하려면 클라이언트가 이 포트에 연결하도록 구성하십시오. 자세한 내용은 이 문서의 클라이언트 구성 섹션을 참조하십시오.

SQL Server 2000 기본 인스턴스에 대한 포트 번호를 변경할 경우 클라이언트를 수정하지 않으면 연결 오류가 발생합니다. 여러 SQL Server 인스턴스가 있는 경우 최신 버전의 MDAC 데이터 액세스 스택(2.6)은 동적 검색을 사용하고 UDP(사용자 데이터그램 프로토콜) 협상(UDP 포트 1434 사용)를 사용하여 명명된 인스턴스를 찾습니다. 개발 환경에서 이를 사용할 수도 있지만 방화벽은 일반적으로 UDP 협상 트래픽을 차단하기 때문에 실제 환경에서는 작동하지 않을 수 있습니다.

이러한 문제를 방지하려면 항상 구성된 대상 포트 번호로 연결하도록 클라이언트를 구성하십시오.

클라이언트 구성

TCP/IP 네트워크 라이브러리를 사용하여 SQL Server에 연결하도록 클라이언트를 구성하고 클라이언트 라이브러리에 올바른 대상 포트 번호가 사용되는지 확인해야 합니다.

TCP/IP 네트워크 라이브러리 사용

SQL Server 클라이언트 네트워크 유틸리티를 사용하여 클라이언트를 구성할 수 있습니다. 일부 설치(예: 웹 서버)의 경우 클라이언트에 이 유틸리티가 설치되지 않았을 수도 있습니다. 이 경우에는 다음 중 하나를 수행합니다.

  • 연결 문자열을 통해 제공된 "Network Library=dbmssocn" 이름-값 쌍을 사용하여 네트워크 라이브러리를 지정합니다. "dbmssocn" 문자열은 TCP/IP(소켓) 라이브러리를 식별하는 데 사용됩니다.
    참고SQL Server .NET Data Provider를 사용하는 경우 네트워크 라이브러리 설정은 기본적으로 "dbmssocn"을 사용합니다.
  • 클라이언트 시스템에서 레지스트리를 수정하여 TCP/IP를 기본 라이브러리로 설정합니다. SQL Server 네트워크 라이브러리 구성에 대한 자세한 내용은 방법: 클라이언트 네트워크 유틸리티를 사용하지 않고 SQL Server 기본 네트워크 라이브러리 변경(Q250550)을 참조하십시오.

포트 지정

SQL Server 인스턴스가 기본 1433 포트 이외의 다른 포트에서 수신하도록 구성된 경우 다음과 같은 방법으로 연결할 포트 번호를 지정할 수 있습니다.

  • 클라이언트 네트워크 유틸리티 사용.
  • 연결 문자열에 제공된 "Server" 또는 "Data Source" 이름-값 쌍으로 포트 번호 지정. 다음 형식의 문자열을 사용합니다.
    "Data Source=ServerName, PortNumber"  
    참고ServerName은 IP 주소 또는 DNS(Domain Name System) 이름일 수 있습니다. 최적의 성능을 위해서는 DNS 조회를 피하도록 IP 주소를 사용하십시오.

분산 트랜잭션

COM+ 분산 트랜잭션 및 Microsoft DTC(분산 트랜잭션 코디네이터) 서비스를 사용하는 서비스 구성 요소를 개발한 경우 개별 DTC 인스턴스 및 DTC 및 SQL Server와 같은 리소스 관리자 사이의 DTC 트래픽 흐름을 허용하도록 방화벽을 구성해야 할 수도 있습니다.

DTC용 포트 열기에 대한 자세한 내용은 정보: 방화벽을 통해 작동하도록 Microsoft DTC(분산 트랜잭션 코디네이터) 구성을 참조하십시오.

BLOB 처리

현재 여러 응용 프로그램에서는 기존의 문자 및 숫자 데이터 외에도 그래픽 및 사운드와 같은 데이터 형식이나 비디오와 같은 보다 정교한 데이터 형식을 다루어야 합니다. 그래픽, 사운드 및 비디오 형식은 매우 다양한 종류가 있습니다. 하지만 저장소의 관점에서 볼 때 이러한 데이터는 모두 BLOB(Binary Large Object)라고도 부르는 대량의 이진 데이터로 간주될 수 있습니다.

SQL Server는 BLOB를 저장할 수 있는 binary, varbinaryimage 데이터 형식을 제공합니다. 이름과는 달리 BLOB 데이터는 텍스트 기반 데이터도 참조할 수 있습니다. 예를 들어 특정 행과 연결할 수 있는 임의 길이의 메모 필드를 저장할 수 있습니다. SQL Server는 이러한 목적을 위해 ntexttext 데이터 형식을 제공합니다.

일반적으로 8KB 미만의 이진 데이터의 경우에는 varbinary 데이터 형식을 사용하십시오. 이 크기를 초과하는 이진 데이터의 경우에는 image를 사용하십시오. 표 2에서는 각 데이터 형식의 기본 특징에 대해 설명합니다.

표 2. 데이터 형식 특징

데이터 형식크기설명
binary1에서 8,000바이트 사이. 저장소 크기는 4바이트를 추가한 길이로 지정됩니다.고정 길이 이진 데이터
varbinary1에서 8,000바이트 사이. 저장소 크기는 제공된 데이터와 4바이트를 추가한 실제 길이입니다.가변 길이 이진 데이터
image0에서 2GB 사이의 가변 길이 이진 데이터.크기가 큰 가변 길이 이진 데이터
text0에서 2GB 사이의 가변 길이 데이터.문자 데이터
ntext0에서 2GB 사이의 가변 길이 데이터.유니코드 문자 데이터
참고Microsoft SQL Server 2000은 데이터 행에서 중간 크기의 text, ntext 및 image 값을 저장하는 기능을 지원합니다. 이 기능은 text, ntextimage 열의 데이터가 일반적으로 하나의 단위로 읽혀지거나 쓰여지는 테이블과 text, ntextimage 데이터를 사용하는 테이블을 참조하는 대부분의 문에 가장 적합합니다. 자세한 내용은 SQL Server 온라인 설명서의 "행 텍스트" 부분을 참조하십시오.

BLOB 데이터 저장 위치

SQL Server 7.0 이상에서는 데이터베이스에 저장된 BLOB 데이터 작업 성능이 향상되었습니다. 이러한 이유 중 하나는 데이터베이스 페이지 크기가 8KB로 증가했기 때문입니다. 그 결과 8KB 미만의 텍스트 또는 이미지 데이터를 별개의 이진 트리 페이지 구조로 저장할 필요 없이 단일 행에 저장할 수 있습니다. 즉, text, ntext 또는 image 데이터를 읽고 쓰는 작업을 문자 및 이진 문자열을 읽거나 쓰는 속도만큼 빠르게 수행할 수 있습니다. 8KB를 초과하는 경우에는 포인터가 행 내부에 유지되고 데이터 자체는 별개의 데이터 페이지의 이진 트리 구조에 저장되어 성능상의 단점을 피할 수 없습니다.

text, ntextimage 데이터를 단일 행에 강제로 저장하는 방법은 SQL Server 온라인 설명서에서 "텍스트 및 이미지 데이터 사용" 항목을 참조하십시오.

BLOB 처리를 위해 일반적으로 사용되는 다른 방법은 BLOB 데이터를 파일 시스템에 저장하고 포인터(URL(Uniform Resource Locater) 링크)를 데이터베이스 열에 저장하여 해당 파일을 참조합니다. SQL Server 7.0 이전 버전의 경우에는 파일 시스템에 있는 데이터베이스에 BLOB 데이터를 외부적으로 저장하여 성능을 향상시킬 수 있습니다.

하지만 BLOB 데이터 읽기 및 쓰기를 위한 ADO.NET 지원과 함께 SQL Server 2000에서 제공되는 향상된 BLOB 지원을 통해 BLOB 데이터를 데이터베이스에 매우 쉬운 방식으로 저장할 수 있습니다.

데이터베이스에 BLOB 데이터를 저장하는 방법의 장점

데이터베이스에 BLOB 데이터를 저장하면 여러 장점을 제공합니다.

  • 행의 남은 항목과 BLOB 데이터를 쉽게 동기화할 수 있습니다.
  • BLOB 데이터를 데이터베이스와 함께 백업합니다. 단일 저장소 시스템을 통해 관리가 용이해집니다.
  • XML 스트림에서 데이터를 Base 64 인코딩 표현으로 반환할 수 있는 SQL Server 2000의 XML 지원을 통해 BLOB 데이터를 액세스할 수 있습니다.
  • SQL Server FTS(전체 텍스트 검색) 작업을 고정 또는 가변 길이 문자(유니코드 포함) 데이터가 들어 있는 열에 대해 수행할 수 있습니다. 또한 Microsoft Word나 Microsoft Excel 문서와 같이 image 필드 내에 포함된 형식있는 텍스트 기반 데이터에 대해서도 FTS 작업을 수행할 수 있습니다.

데이터베이스에 BLOB 데이터를 저장하는 방법의 단점

데이터베이스와 파일 시스템에 특정 리소스를 저장할 경우의 장단점을 신중하게 고려하십시오. 이에 대한 예로는 HTTP HREF로 참조되는 이미지를 들 수 있습니다. 이러한 이유는 다음과 같습니다.

  • 데이터베이스에서 이미지를 검색하면 파일 시스템을 사용할 때보다 상당한 오버헤드를 발생시킵니다.
  • 데이터베이스 SAN의 디스크 저장소는 일반적으로 웹 서버 팜에 사용되는 디스크의 저장소보다 비용이 높습니다.
참고메타데이터 전략을 효과적으로 구성할 경우 이미지, 동영상 및 심지어 Microsoft Office 문서와 같은 리소스를 데이터베이스에 저장할 필요를 줄여 줄 수 있습니다. 메타데이터는 인덱싱될 수 있으며 파일 시스템에 저장된 리소스에 대한 포인터를 포함합니다.

데이터베이스에 BLOB 데이터 쓰기

다음 코드는 ADO.NET을 사용하여 파일에서 가져온 이진 데이터를 SQL Server의 image 필드에 기록하는 방법을 보여 줍니다.

public void StorePicture( string filename ){  // Read the file into a byte array  using(FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read))  {    byte[] imageData = new Byte[fs.Length];    fs.Read( imageData, 0, (int)fs.Length );  }  using( SqlConnection conn = new SqlConnection(connectionString) )  {    SqlCommand cmd = new SqlCommand("StorePicture", conn);    cmd.CommandType = CommandType.StoredProcedure;    cmd.Parameters.Add("@filename", filename );    cmd.Parameters["@filename"].Direction = ParameterDirection.Input;    cmd.Parameters.Add("@blobdata", SqlDbType.Image);    cmd.Parameters["@blobdata"].Direction = ParameterDirection.Input;    // Store the byte array within the image field    cmd.Parameters["@blobdata"].Value = imageData;    conn.Open();    cmd.ExecuteNonQuery();  }}  

데이터베이스에 BLOB 데이터 읽기

BLOB 데이터 포함된 행을 읽기 위해 ExecuteReader 메서드를 통해 SqlDataReader 개체를 만드는 경우 CommandBehavior.SequentialAccess 열거 값을 사용하십시오. 이 열거 값이 없으면 판독기가 서버에서 클라이언트로 한 번에 한 행씩 데이터를 끌어옵니다. 행에 BLOB 열이 포함된 경우에는 매우 큰 용량의 메모리가 필요합니다. 열거 값을 사용하면 GetBytes 메서드와 같이 읽혀진 바이트 수를 제어하는 데 사용할 수 있는 방법을 사용하여 참조될 경우에만 BLOB 데이터를 끌어오기 때문에 더욱 세부적으로 제어할 수 있습니다. 이에 대해서는 다음 코드 단편에서 설명합니다.

// Assume previously established command and connection// The command SELECTs the IMAGE column from the tableconn.Open();using(SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess)){  reader.Read();  // Get size of image data–pass null as the byte array parameter  long bytesize = reader.GetBytes(0, 0, null, 0, 0);  // Allocate byte array to hold image data  byte[] imageData = new byte[bytesize];  long bytesread = 0;  int curpos = 0;  while (bytesread < bytesize)  {    // chunkSize is an arbitrary application defined value     bytesread += reader.GetBytes(0, curpos, imageData, curpos, chunkSize);    curpos += chunkSize;  }}// byte array 'imageData' now contains BLOB from database  
참고CommandBehavior.SequentialAccess를 사용하려면 엄격한 순차적 순서에 따라 열 데이터를 액세스해야 합니다. 예를 들어 BLOB 데이터가 열 3에 있는 경우 열 1과 열 2의 데이터도 필요하며 3을 읽기 전에 열 1과 2도 읽어야 합니다.

DataSet에서 데이터베이스 업데이트 수행

데이터베이스 업데이트 수행을 위한 아키텍처는 ADO.NET의 도입으로 크게 바뀌었습니다. ADO.NET은 대형 데이터베이스 크기와 대규모의 클라이언트 수로 확장되는 다중 계층 응용 프로그램을 더욱 쉽게 개발할 수 있도록 디자인되었습니다. 그 결과 특히 다음과 같은 일부 중요한 결과를 가져왔습니다.

  • ADO.NET 응용 프로그램은 일반적으로 중간 및 데이터베이스 계층에서 비즈니스 및 데이터 통합 계산으로부터 클라이언트의 응용 프로그램 논리를 분리합니다. 실제 환경에서 이는 일반적인 응용 프로그램이 클라이언트 응용 프로그램과 데이터베이스 간의 숫자가 적은(하지만 용량은 더 큰) 상호 작용에서 일괄 처리 또는 트랜잭션 특성보다 뛰어나다는 것을 의미합니다.
  • ADO.NET 응용 프로그램은 업데이트의 정확한 처리 방법에 대해 ADO 및 ADO의 이전 버전들보다 더욱 세부적인 제어 성능을 제공합니다.
  • ADO.NET을 사용하면 응용 프로그램에서 데이터베이스 테이블의 행을 직접 조작하는 대신 백엔드 데이터베이스에 저장된 저장 프로시저를 사용하여 변경 내용을 전파할 수 있습니다. 이러한 방법이 권장됩니다.

업데이트 사용 패턴

DataSet에서 데이터를 업데이트하기 위해 ADO.NET을 사용하는 과정은 다음과 같이 요약할 수 있습니다.

  1. DataAdapter 개체를 만들고 데이터베이스 쿼리 결과로 DataSet 개체를 채웁니다. 데이터가 로컬로 캐시됩니다.
  2. 로컬 DataSet 개체를 변경합니다. 이러한 변경 사항에는 로컬로 캐시된 DataSet에 있는 하나 이상의 테이블에 대한 업데이트, 삭제 및 삽입이 포함될 수 있습니다.
  3. DataAdapter 업데이트 관련 속성을 초기화합니다. 이 단계에서는 업데이트, 삭제 또는 삽입이 처리되는 방법을 정확하게 구성합니다. 이를 처리하는 방법은 여러 가지이기 때문에 권장되는 방법이 아래의 "DataAdapters 업데이트 초기화"에 설명되어 있습니다.
  4. DataAdapter Update 메서드를 호출하여 보류 중인 변경 사항을 전송합니다. 로컬로 캐시된 DataSet의 변경된 각 레코드가 처리됩니다. 변경 사항이 없는 레코드는 Update 메서드에서 자동으로 무시됩니다.
  5. DataAdapter Update 메서드에서 발생한 예외를 처리합니다. 데이터베이스에서 요청한 사항을 변경할 수 없으면 예외가 발생합니다.

업데이트 수행을 위한 다른 한 가지 방법이 있습니다. ExecuteNonQuery 메서드를 사용하여 SQL 업데이트 쿼리를 직접 수행할 수 있습니다. 이 기술은 DataSet 개체를 사용하지 않고 특정 행을 프로그래밍 방식으로 업데이트하려는 경우에 적합합니다.

DataAdapters 업데이트 초기화

ADO.NET에서는 데이터베이스 업데이트 사항을 전송하기 위한 고유 코드를 DataAdapter 개체에 추가해야 합니다.. 이를 위한 세 가지 방법은 다음과 같습니다.

  • 고유 업데이트 논리를 제공할 수 있습니다.
  • 데이터 어댑터 구성 마법사를 사용하여 업데이트 논리를 생성할 수 있습니다.
  • CommandBuilder 개체를 사용하여 업데이트 논리를 생성할 수 있습니다.

고유 업데이트 논리를 제공하는 것이 좋습니다. 시간 절약을 위해서는 데이터 어댑터 구성 마법사를 사용할 수 있지만, 그럴 경우에는 논리를 런타임에 생성하지 않도록 하십시오. 성능상의 문제가 있고 개체에서 생성하는 업데이트 논리를 제어할 수 없으므로 필요한 경우가 아니면 CommandBuilder 개체를 사용하지 마십시오. 또한 CommandBuilder는 저장 프로시저를 사용한 업데이트 전송을 지원하지 않습니다.

CommandBuilder는 보고 또는 데이터 추출 도구와 같이 데이터 액세스 논리를 동적으로 생성하는 응용 프로그램에서 사용할 수 있습니다. CommandBuilder를 사용하면 이러한 도구에서 고유 코드 생성 모듈을 작성할 필요가 없습니다.

저장 프로시저 사용

업데이트에 대해 저장 프로시저를 사용하면 데이터베이스 관리자가 동적 SQL에서 제공되는 보안보다 세부적인 수준의 보안을 구현할 수 있으며 보다 정밀한 데이터 무결성 검사가 가능합니다. 예를 들어 저장 프로시저는 항목을 감사 로그에 삽입할 수 있을 뿐만 아니라 요청된 업데이트를 수행할 수도 있습니다. 저장 프로시저는 또한 저장 프로시저로 데이터베이스 내에 수행된 오프라인 쿼리 최적화로 인해 최상의 성능을 제공할 수 있습니다. 마지막으로 데이터베이스 구조 및 저장 프로시저가 제공하는 응용 프로그램간의 통합으로 인해 더욱 쉽게 유지 관리할 수 있습니다.

저장 프로시저를 사용하는 ADO.NET 응용 프로그램은 여러 장점을 제공하고 데이터베이스를 직접 수정하는 방법보다 구현하기에 어렵지 않기 때문에 거의 모든 경우에 이 방법이 권장됩니다. 이에 대한 예외는 이를 지원하지 않는 Microsoft Access와 같이 여러 백엔드 또는 데이터베이스를 사용해야 하는 경우입니다. 이러한 경우에는 쿼리 기반 업데이트를 사용하십시오.

동시성 관리

DataSet 개체는 사용자가 데이터를 원격으로 처리하고 데이터를 사용하는 경우와 같이 장기 실행 활동에 대한 최적의 동시성 사용을 권장하도록 디자인되었습니다. DataSet의 업데이트를 데이터베이스 서버로 전송하는 경우 최적의 동시성을 관리하기 위한 주요 방법은 다음 네 가지입니다.

  • 기본 키 열만 포함
  • WHERE 절에 모든 열 포함
  • 고유 키 열 및 타임스탬프 열 포함
  • 고유 키 열 및 수정된 열 포함

마지막 세 가지 접근 방식은 데이터 무결성을 유지하며 처음 방식은 유지하지 않습니다.

기본 키 열만 포함

이 옵션은 마지막 업데이트가 모든 이전 변경 사항을 무효화하는 경우를 만듭니다. CommandBuilder는 이 옵션을 지원하지 않지만 데이터 어댑터 구성 마법사는 이 옵션을 지원합니다. 이 옵션을 사용하려면 고급 옵션 탭으로 이동하고 동시성 사용 확인란의 선택을 취소합니다.

이 방식은 사용자가 모르는 사이에 다른 사용자의 변경 사항을 덮어쓸 수 있기 때문에 권장되지 않습니다. 다른 사용자의 업데이트에 대한 무결성은 절대로 손상시켜서는 안됩니다. 이 기술은 단일 사용자 데이터베이스에서만 적합합니다.

WHERE 절에 모든 열 포함

이 옵션은 사용자의 코드가 행을 패치하는 시간과 사용자의 코드가 행의 보류 중인 변경 사항을 전송하는 시간 사이에 다른 사용자가 변경한 사항을 덮어쓰지 않도록 방지합니다. 이 옵션은 데이터 어댑터 구성 마법사 및 SqlCommandBuilder에서 생성한 SQL 코드에 대한 기본 동작입니다.

이 방식은 다음과 같은 이유로 인해 권장되지 않습니다.

  • 테이블에 열을 추가할 경우 쿼리를 수정해야 합니다.
  • 일반적으로 데이터베이스에서 두 개의 BLOB 값은 크기가 너무 커서 비교 작업이 비효율적으로 되므로 이러한 두 값의 비교가 허용되지 않습니다. CommandBuilder 및 데이터 어댑터 구성 마법사와 같은 도구에는 WHERE 절에 BLOB 열이 포함되지 않아야 합니다.
  • 테이블 내의 모든 열을 업데이트된 행의 모든 열과 비교하면 과도한 오버헤드를 발생시킬 수 있습니다.

고유 키 열 및 타임스탬프 열 포함

이 옵션을 사용하면 데이터베이스가 각 행의 업데이트 후에 타임스탬프 열을 고유 값으로 업데이트합니다. 테이블에 타임스탬프 열을 제공해야 합니다. 현재까지는 CommandBuilder 또는 데이터 어댑터 구성 마법사에서 이 옵션이 지원되지 않습니다.

고유 키 열 및 수정된 열 포함

일반적으로 응용 프로그램 논리가 오래된 데이터 필드나 업데이트하지 않는 필드에 의존하는 경우 오류가 발생할 수 있기 때문에 이 옵션은 권장되지 않습니다. 예를 들어 사용자 A가 주문 수량을 변경하고 사용자 B가 단가를 변경하는 경우 주문 합계(가격 곱하기 수량)가 잘못 계산될 수 있습니다.

Null 필드의 올바른 업데이트

데이터베이스의 필드에 데이터 값이 포함되지 않은 경우 이러한 빈 필드에는 특수한 Null 값이 포함된 것으로 생각하는 것이 편리한 경우가 많습니다. 하지만 데이터베이스 표준에 Null 값에 대한 특수 처리가 필요한 경우에는 이러한 추상적 관점으로 인해 프로그래밍 오류가 발생할 가능성이 있습니다.

Null 필드와 관련된 핵심 문제는 일반적인 SQL = operator의 경우 두 피연산자 중 하나 또는 모두가 Null 값인 경우 항상 false를 반환한다는 점입니다. 연산자 IS NULL은 SQL 쿼리에서 Null 필드의 존재를 확인하기 위한 유일한 올바른 방법입니다.

응용 프로그램에서 이에 설명된 기술을 사용하여 WHERE 절 지정을 통한 동시성을 관리하는 경우 필드가 Null일 가능성이 있는 모든 경우에 명시적인 IS NULL 식을 포함시켜야 합니다. 예를 들어 다음 쿼리는 OldLastName이 Null인 경우 항상 실패합니다.

SET LastName = @NewLastName WHERE StudentID = @StudentID AND                                   LastName = @OldLastName   

이 쿼리는 다음과 같이 다시 작성되어야 합니다.

SET LastName = @NewLastName WHERE (StudentID = @StudentID) AND                                  ((LastName = @OldLastName) OR                                   (OldLastName IS NULL AND LastName IS NULL))  

위에 표시된 업데이트 논리를 작성하는 방법을 올바르게 이해하기 위해서는 CommandBuilder 도구에서 생성된 출력을 읽어봐야 합니다.

추가 정보

데이터베이스 업데이트에 대한 완벽한 취급 설명은 David Sceppa의 Microsoft ADO.NET(Microsoft Press, 2002), 11장 및 12장을 참조하십시오.

강력하게 형식있는 DataSet 개체 사용

강력하게 형식있는 DataSet 개체는 데이터베이스 테이블 및 열을 개체 및 속성으로 제공합니다. 액세스는 컬렉션으로의 인덱싱이 아닌 이름별로 수행됩니다. 즉, 강력하게 형식있는 DataSet 개체와 형식없는 DataSet 개체 사이의 차이점은 필드를 액세스하는 방식으로 인식할 수 있습니다.

string n1 = myDataSet.Tables["Students"].Rows[0]["StudentName"];  // untypedstring n2 = myDataSet.Students[0].StudentName;           // strongly typed  

강력하게 형식있는 DataSet 개체 사용에 따른 장점은 다음과 같습니다.

  • 필드 액세스에 필요한 코드를 보다 쉽게 이해할 수 있으며 간결하게 만들 수 있습니다.
  • Visual Studio .NET 코드 편집기 내의 Intellisense 기능으로 사용자가 코드를 입력할 때 자동으로 완성할 수 있습니다.
  • 컴파일러는 강력하게 형식있는 DataSet 유형의 불일치 오류를 알릴 수 있습니다. 런타임 보다는 컴파일 타임에 형식 오류를 검색하는 것이 더 좋습니다.

강력하게 형식있는 DataSet을 사용하는 경우

강력하게 형식있는 DataSet은 응용 프로그램을 더욱 쉽게 개발하고 오류를 줄일 수 있도록 해주기 때문에 유용합니다. 포커스가 그래픽 사용자 인터페이스 상에 있는 다중 계층 응용 프로그램의 클라이언트측과 여러 필드 액세스 작업이 필요한 데이터 유효성 검사의 경우에 특히 유용합니다.

하지만 강력하게 형식있는 DataSet은 필드 및 테이블 이름이 수정되는 경우와 같이 데이터베이스 구조가 변경되는 경우에는 작업이 번거로워질 수 있습니다. 이 경우 형식있는 DataSet 클래스를 다시 생성하고 모든 종속 클래스를 수정해야 합니다.

강력하게 형식있는 방식과 형식없는 방식을 모두 같은 응용 프로그램에서 사용할 수 있습니다. 예를 들어 일부 개발자는 클라이언트 측에 강력하게 형식있는 DataSet을 사용하고 서버에서는 형식없는 레코드를 사용합니다. 강력하게 형식있는 DataSet의 .Merge 메서드를 사용하면 형식없는 DataSet으로부터 데이터를 가져올 수 있습니다.

DataSet 클래스 생성

.NET Framework SDK 및 Visual Studio.NET은 모두 필요한 DataSet 하위 클래스 생성에 도움이 되는 유틸리티를 제공합니다. .NET Framework SDK에는 명령줄 도구 사용 및 코드 작성 작업이 포함됩니다. Visual Studio .NET 메서드는 명백하게 Visual Studio .NET 개발 환경에 의존하며 명령 창을 열 필요가 없습니다.

DataSet 클래스 생성 방법과는 관계없이 해당 형식있는 DataSet을 참조하는 모든 계층에 새로운 클래스를 배포해야 합니다. 이 경우는 일반적인 시나리오가 아니지만 원격을 사용하여 계층 간에 형식있는 DataSet을 전달할 경우에는 반드시 고려해야 합니다.

.NET Framework 유틸리티 사용

.NET Framework SDK에는 XML 스키마(.xsd) 파일을 기준으로 클래스 파일을 생성하는 데 도움이 되는 XML 스키마 정의 도구라는 명령줄 유틸리티가 포함됩니다. DataSet 개체의 WriteXmlSchema 메서드와 함께 이 유틸리티를 사용하여 형식없는 DataSet을 강력하게 형식있는 DataSet으로 변환합니다.

다음 명령은 XML 스키마 파일로부터 클래스 파일을 생성하는 방법을 보여 줍니다. 명령 창을 열고 다음을 입력합니다.

C:\>xsd MyNewClass.xsd /d  

명령에서 첫 번째 매개 변수는 XML 스키마 파일에 대한 경로입니다. 두 번째 매개 변수는 만들려는 클래스가 DataSet 클래스로부터 파생됨을 나타냅니다. 기본적으로 이 도구는 Visual C# .NET 클래스 파일을 생성하지만 적합한 옵션을 추가할 경우 Visual Basic .NET 클래스 파일을 생성할 수도 있습니다. 도구의 사용 가능한 옵션을 확인하려면 다음을 입력합니다.

xsd /?  

새로운 클래스 파일을 만든 다음에는 이를 프로젝트에 추가합니다. 이제 다음 Visual C# .NET 코드 조각에 표시된 것과 같이 강력하게 형식있는 DataSet 클래스의 인스턴스를 만들 수 있습니다.

MyNewClass ds = new MyNewClass();  

Visual Studio .NET 사용

Visual Studio .NET에서 강력하게 형식있는 DataSet을 생성하려면 폼 디자이너 창에서 마우스 오른쪽 단추를 클릭한 다음 데이터 집합 생성을 클릭합니다. 그러면 클래스 파일과 함께 .xsd(XML 스키마 정의) 파일이 만들어진 다음 프로젝트에 추가됩니다. 이를 수행하기 전에 Windows 폼에 하나 이상의 DataAdapters가 추가되었는지 확인하십시오. 클래스 파일은 숨겨져 있습니다. 이 파일을 보려면 솔루션 탐색기 창의 도구 모음에 있는 모든 파일 표시 단추를 클릭합니다. 클래스 파일은 .xsd 파일과 연결되어 있습니다.

강력하게 형식있는 DataSet에 관계를 추가하려면 솔루션 탐색기 창에서 스키마 파일을 두 번 클릭하여 XML 스키마 디자이너를 연 다음 제약 조건을 추가하려는 테이블을 마우스 오른쪽 단추로 클릭합니다. 바로가기 메뉴에서 새 관계 추가를 클릭합니다.

Visual Studio .NET에서 강력하게 형식있는 DataSet을 생성하는 다른 방법은 프로젝트 탐색기에서 프로젝트를 마우스 오른쪽 단추로 클릭하고 파일 추가를 선택한 다음 데이터집합을 선택하는 것입니다. 그러면 새로운 .xsd 파일이 생성됩니다. 여기에서 서버 탐색기를 사용하여 데이터베이스에 연결하고 테이블을 xsd 파일로 끌어다 놓을 수 있습니다.

Null 데이터 필드 작업

다음은 .NET 데이터 아키텍처에서 Null 필드 값을 올바르게 사용하기 위한 일부 팁입니다.

  • 항상 System.DBNull 클래스를 사용하여 Null 필드의 값을 설정합니다. C# 또는 Visual Basic .NET에서 제공된 Null 값을 사용하지 않습니다. 예를 들면 다음과 같습니다.
    rowStudents["Nickname"] = DBNull.Value   // correct!  
  • 강력하게 형식있는 DataSet에는 DataRow마다 두 개의 추가 메서드가 포함되며 이중 하나의 메서드는 열에 Null 값이 포함되어 있는지 확인하기 위한 것이고 다른 하나는 열 값을 Null로 설정하기 위한 것입니다. 이러한 메서드는 다음 코드 단편에 표시되어 있습니다.
    If (tds.rowStudent[0].IsPhoneNoNull()) {….}tds.rowStudent[0].SetPhoneNoNull()  
  • 데이터베이스에서 Null 값을 테스트하려면 항상 DataRow 클래스의 IsNull 메서드(또는 이전 단락에서 설명된 강력하게 형식있는 개체)를 사용합니다. 이 메서드는 Null 데이터베이스 값을 테스트하기 위해 지원되는 유일한 방법입니다.
  • 데이터 필드에 Null 값이 포함될 가능성이 있는 경우 비-Null 값을 사용하는 컨텍스트에서 값을 사용하기 전에 IsNull 메서드를 사용하여 Null 값 여부를 테스트하십시오. 이에 대한 일반적인 예는 Null일 수 있는 Integer 값 데이터 필드입니다. .NET 런타임 Integer 데이터 형식에는 Null 값이 포함되지 않습니다. 다음은 구문 예입니다.
    int i = rowStudent["ZipCode"];         // throws exception if null!  
  • 데이터베이스에서 Null 값이 매핑되는 방법을 구성하려면 강력하게 형식있는 DataSet .xsd 파일의 nullValue 주석을 사용합니다. 기본적으로 예외가 발생합니다. 하지만 보다 세부적인 제어를 위해서는 String.Empty와 같은 지정된 값으로 Null 값을 교체하도록 클래스를 구성할 수 있습니다.

트랜잭션

데이터 원본을 업데이트하는 거의 모든 상업용 응용 프로그램에는 트랜잭션 지원이 필요합니다. 트랜잭션은 잘 알려진 ACID(원자성, 일관성, 격리성 및 영속성) 두문자어의 네 가지 기본 보장을 제공하여 하나 이상의 데이터 원본 내에 포함된 시스템 상태의 무결성을 보장하기 위해 사용됩니다.

예를 들어 구매 주문을 처리하는 웹 기반 판매 응용 프로그램을 고려해 보십시오. 각 주문에는 세 가지 데이터베이스 업데이트를 포함하는 세 개의 개별 작업이 필요합니다.

  • 주문된 수량에 따라 재고 수준을 줄여야 합니다.
  • 고객의 대변 수준을 구매 수량에 따라 차변해야 합니다.
  • 새로운 주문을 주문 데이터베이스에 추가해야 합니다.

이러한 세 가지 고유 작업은 하나의 단위로 원자성 방식으로 수행되어야 합니다. 이 세 작업은 모두 성공해야 하며 아니면 모두 실패해야 합니다. 그렇지 않으면 데이터 무결성이 손상될 수 있습니다. 트랜잭션은 이러한 보장 및 기타 보장을 제공합니다.

트랜잭션 처리의 기본 개념에 대한 추가 배경 지식을 보려면 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconTransactionProcessingFundamentals.asp (영문)를 참조하십시오.

데이터 액세스 코드에 트랜잭션 관리를 포함시키기 위해 채택할 수 있는 방법은 매우 다양합니다. 각 방식은 두 가지 기본 프로그래밍 모델 중 하나에 속합니다.

  • 수동 트랜잭션. 구성 요소 코드 또는 저장 프로시저에서 각각 ADO.NET 또는 Transact-SQL의 트랜잭션 지원 기능을 직접 사용하는 코드를 작성합니다.
  • 자동(COM+) 트랜잭션. 런타임시 개체의 트랜잭션 요구 사항을 지정하는 선언적 특성을 .NET 클래스에 추가합니다. 이 모델을 사용하면 동일 트랜잭션 내에서 작업을 수행하도록 여러 구성 요소를 쉽게 구성할 수 있습니다.

두 기술 모두 로컬 트랜잭션(즉, SQL Server 2000과 같은 단일 리소스 관리자에 대해 수행되는 트랜잭션) 또는 분산 트랜잭션(즉, 원격 컴퓨터에 있는 여러 리소스 관리자에 대해 수행되는 트랜잭션)을 수행할 수 있지만 자동 트랜잭션 모델은 분산 트랜잭션 처리를 크게 단순화합니다.

보다 쉬운 프로그래밍 모델을 활용할 수 있도록 자동(COM+) 트랜잭션을 사용할 수도 있습니다. 이러한 장점은 특히 데이터베이스 업데이트를 수행하는 여러 구성 요소가 있는 경우에 두드러집니다. 하지만 대부분의 시나리오의 경우에는 이러한 트랜잭션 모델로 인해 비롯되는 추가 오버헤드 및 성능 감소를 방지해야 합니다.

이 섹션에서는 특정 응용 프로그램 시나리오에 따라 가장 적합한 모델을 선택하는 데 도움이 되는 지침을 제공합니다.

트랜잭션 모델 선택

트랜잭션 모델을 선택하기 전에 트랜잭션이 필요한지 여부를 고려해야 합니다. 트랜잭션은 서버 응용 프로그램에서 소비되는 가장 비용이 높은 단일 리소스이며 불필요하게 사용할 경우 확장성을 감소시킵니다. 트랜잭션 사용과 관련된 다음 지침을 고려하십시오.

  • 일련의 작업에 대해 잠금을 획득하고 ACID 규칙을 강제로 적용해야 하는 경우에만 트랜잭션을 수행합니다.
  • 가능한 한 트랜잭션을 짧게 유지하여 데이터베이스 잠금을 유지하는 시간을 최소화합니다.
  • 트랜잭션 수명을 클라이언트가 제어할 수 없도록 합니다.
  • 개별 SQL 문에 대해서는 트랜잭션을 사용하지 않습니다. SQL Server는 각 문을 개별 트랜잭션으로 자동으로 실행합니다.

자동 트랜잭션과 수동 트랜잭션

자동 트랜잭션을 사용하면 특히 여러 구성 요소가 데이터베이스 업데이트를 수행하는 경우 프로그래밍 모델이 다소 간소화됩니다. 수동 로컬 트랜잭션을 사용하면 Microsoft DTC와의 상호 작용이 필요하지 않기 때문에 항상 속도가 더 빠릅니다. 성능 감소가 줄어들긴 하지만 단일 로컬 리소스 관리자(예: SQL Server)에 대해 자동 트랜잭션을 사용하는 경우, 수동의 로컬 트랜잭션은 DTC와의 불필요한 상호 프로세스 통신(IPC)을 방지하기 때문에 자동 트랜잭션이 수동 트랜잭션보다 느립니다.

다음 경우에 수동 트랜잭션을 사용하십시오.

  • 단일 데이터베이스에 대해 트랜잭션을 수행합니다.

다음 경우에 자동 트랜잭션을 사용하십시오.

  • 단일 트랜잭션을 여러 원격 데이터베이스로 확장할 수 있어야 합니다.
  • 단일 트랜잭션에서 여러 리소스 관리자(예: 데이터베이스 및 Windows 2000 Message Queuing(이전의 MSMQ))를 포함해야 합니다.
참고트랜잭션 모델은 혼합하지 마십시오. 하나만 선택해서 사용하십시오.

성능이 충분하다고 여겨지는 응용 프로그램 시나리오의 경우에는 프로그래밍 모델을 단순화하기 위해 단일 데이터베이스의 경우라도 자동 트랜잭션을 사용하는 것이 바람직합니다. 자동 트랜잭션을 사용하면 여러 구성 요소에서 동일 트랜잭션의 일부인 작업을 쉽게 수행할 수 있습니다.

수동 트랜잭션 사용

수동 트랜잭션의 경우 구성 요소 코드 또는 저장 프로시저에서 각각 ADO.NET 또는 Transact-SQL의 트랜잭션 지원 기능을 직접 사용하는 코드를 작성합니다. 대부분의 경우 이 방식은 뛰어난 캡슐화 성능을 제공하고 성능적인 관점에서 ADO.NET 코드로 트랜잭션을 수행하는 것과 비슷하기 때문에 저장 프로시저에서 트랜잭션을 제어할 수 있어야 합니다.

ADO.NET에서 수동 트랜잭션 수행

ADO.NET은 새 트랜잭션을 시작하고 커밋 또는 롤백 여부를 명시적으로 제어할 수 있는 트랜잭션을 지원합니다. 트랜잭션 개체는 단일 데이터베이스 연결과 연결되어 있으며 연결 개체의 BeginTransaction 메서드로 얻어집니다. 이 메서드를 호출해도 이후의 명령이 이 트랜잭션의 컨텍스트로 실행되지는 않습니다. 그렇게 하려면 명령의 Transaction 속성을 설정하여 각 명령을 트랜잭션과 명시적으로 연결해야 합니다. 트랜잭션 개체와 여러 명령 개체를 연결하면 단일 트랜잭션의 단일 데이터베이스에 대해 여러 작업을 그룹화합니다.

ADO.NET 트랜잭션 코드 사용 예를 보려면 부록에서 ADO.NET 수동 트랜잭션 코딩 방법을 참조하십시오.

추가 정보

  • ADO.NET 수동 트랜잭션의 기본 격리 수준은 Read Committed이므로 데이터가 읽혀지는 동안 데이터베이스가 공유 잠금을 유지하지만 트랜잭션이 끝나기 전에 데이터를 변경할 수 있습니다. 따라서 반복되지 않은 읽기 또는 가상 데이터가 발생할 수 있습니다. 트랜잭션 개체의 IsolationLevel 속성을 IsolationLevel 열거 유형에서 정의된 열거 값 중 하나로 설정하여 격리 수준을 변경할 수 있습니다.
  • 트랜잭션에 적합한 격리 수준을 선택할 때는 주의를 기울여야 합니다. 이 때 사용자는 데이터 일관성과 성능 중에서 하나를 선택해야 합니다. 최상의 격리 수준(직렬화)은 절대적인 데이터 일관성을 제공하지만 전반적인 시스템 처리량이 저하됩니다. 격리 수준이 낮으면 응용 프로그램의 확장성이 향상되지만 동시에 데이터 불일치로 인한 오류 발생 가능성이 높아집니다. 데이터 읽기 작업을 수행하고 쓰기 작업은 거의 수행하지 않는 시스템의 경우 낮은 격리 수준이 적합합니다.
  • 적합한 트랜잭션 격리 수준 선택에 대한 자세한 내용을 보려면 Microsoft Press 책, Inside SQL Server 2000(저자: Kalen Delaney)을 참조하십시오.

저장 프로시저로 수동 트랜잭션 수행

또한 저장 프로시저에서 Transact-SQL 문을 사용하여 수동 트랜잭션을 직접 제어할 수 있습니다. 예를 들어 BEGIN TRANSACTION, END TRANSACTION 및 ROLLBACK TRANSACTION과 같은 Transact-SQL 트랜잭션 문을 사용하는 단일 저장 프로시저를 사용하여 트랜잭션 작업을 수행할 수 있습니다.

추가 정보

  • 필요한 경우 저장 프로시저에서 SET TRANSACTION ISOLATION LEVEL 문을 사용하여 트랜잭션 격리 수준을 제어할 수 있습니다. Read Committed는 SQL Server 기본값입니다. SQL Server 격리 수준에 대한 자세한 내용은 SQL Server 온라인 설명서의 "관계 데이터 액세스 및 변경" 섹션의 격리 수준을 참조하십시오.
  • Transact-SQL 트랜잭션 문을 사용하여 트랜잭션 업데이트 수행 방법을 보여 주는 코드 예제를 보려면 부록에 있는 Transact-SQL로 트랜잭션을 수행하는 방법을 참조하십시오.

자동 트랜잭션 사용

자동 트랜잭션은 새로운 트랜잭션을 명시적으로 시작하거나 트랜잭션을 명시적으로 커밋 또는 취소할 필요가 없기 때문에 프로그래밍 모델을 단순화합니다. 하지만 자동 트랜잭션의 가장 중요한 장점은 DTC와 함께 작동할 수 있다는 점입니다. 따라서 단일 트랜잭션을 여러 분산 데이터 원본으로 확장할 수 있습니다. 대규모 분산 응용 프로그램의 경우 이러한 장점은 매우 유용할 수 있습니다. DTC를 직접 프로그래밍하여 분산 트랜잭션을 수동으로 제어할 수 있는 경우라도 자동 트랜잭션은 작업을 크게 간소화할 수 있으며 구성 요소 기반 시스템에 맞게 디자인되었습니다. 예를 들어 단일 트랜잭션으로 구성된 작업을 수행하도록 여러 구성 요소를 선언적으로 쉽게 구성할 수 있습니다.

자동 트랜잭션은 COM+에서 제공되는 분산 트랜잭션 지원 기능에 의존하며, 그 결과 서비스 구성 요소(즉, ServicedComponent 클래스에서 파생된 구성 요소)에서 자동 트랜잭션을 사용할 수 있습니다.

자동 트랜잭션에 대한 클래스를 구성하려면

  • System.EnterpriseServices 이름 공간 내에 있는 ServicedComponent 클래스로부터 클래스를 파생합니다.
  • Transaction 특성을 사용하여 클래스의 트랜잭션 요구 사항을 정의합니다. TransactionOption 열거 유형으로부터 제공된 값은 클래스가 COM+ 카탈로그에서 구성되는 방법을 결정합니다. 이 특성으로 설정할 수 있는 다른 속성에는 트랜잭션 격리 수준과 제한 시간이 포함됩니다.
  • 트랜잭션 결과를 명시적으로 선택해야 하는 경우를 방지하기 위해 AutoComplete 특성으로 메서드를 주석 처리할 수 있습니다. 이러한 메서드가 예외를 발생시키면 트랜잭션이 자동으로 취소됩니다. 필요한 경우 트랜잭션 결과를 직접 선택할 수도 있습니다. 자세한 내용은 이 문서의 후반에 있는 트랜잭션 결과 확인을 참조하십시오.

추가 정보

  • COM+ 자동 트랜잭션에 대한 자세한 내용을 보려면 플랫폼 SDK 설명서에서 "COM+를 통한 자동 트랜잭션"을 찾아 보십시오.
  • 트랜잭션 .NET 클래스 예를 보려면 부록에 있는 트랜잭션 .NET 클래스를 코딩하는 방법을 참조하십시오.

트랜잭션 격리 수준 구성

COM+ 버전1.0(Windows 2000에서 실행 중인 COM+)에 대한 트랜잭션 격리 수준은 직렬화됩니다. 이 수준은 최고의 격리 수준을 제공하지만 성능 감소를 수반합니다. 관련된 리소스 관리자(일반적으로 데이터베이스)가 트랜잭션 기간 동안 읽기 및 쓰기 잠금을 모두 유지해야 하기 때문에 시스템의 전반적인 처리량이 감소됩니다. 이 기간 동안 다른 모든 트랜잭션이 차단되어 응용 프로그램의 확장 성능에 심각한 영향을 줄 수 있습니다.

Microsoft Windows .NET과 함께 제공되는 COM+ 버전 1.5를 사용하면 트랜잭션 격리 수준을 구성 요소별 기반에 따라 COM+ 카탈로그에 구성할 수 있습니다. 트랜잭션의 루트 구성 요소와 연결된 설정은 트랜잭션의 격리 수준을 결정합니다. 또한 동일 트랜잭션 스트림의 일부인 내부 하위 구성 요소의 트랜잭션 수준은 루트 구성 요소에서 정의된 것보다 높지 않아야 합니다. 격리 수준이 높으면 하위 구성 요소가 인스턴스화될 때 오류가 발생합니다.

.NET 관리 클래스의 경우 Transaction 특성은 공용 Isolation 속성을 지원합니다. 이 속성을 사용하여 다음 코드에 표시된 것과 같이 특정 격리 수준을 선언적으로 지정할 수 있습니다.

 [Transaction(TransactionOption.Supported, Isolation=TransactionIsolationLevel.ReadCommitted)]public class Account : ServicedComponent{  . . .}  

추가 정보

트랜잭션 결과 확인

자동 트랜잭션의 결과는 단일 트랜잭션 스트림의 모든 트랜잭션 구성 요소 컨텍스트에서 일관성 플래그와 함께 트랜잭션 취소 플래그의 상태로 제어됩니다. 트랜잭션 결과는 트랜잭션 스트림의 루트 구성 요소가 비활성화되고 컨트롤이 호출자에게 반환되는 시점에 확인됩니다. 이러한 내용은 기존의 은행 예금 전송 트랜잭션을 보여 주는 그림 5에 설명되어 있습니다.

그림 1.5. 트랜잭션 스트림 및 컨텍스트

트랜잭션의 결과는 루트 개체(이 예에서는 Transfer 개체)가 비활성화되고 클라이언트 메서드 호출이 반환될 때 확인됩니다. 임의의 컨텍스트 내의 일관성 플래그가 False로 설정되었거나 트랜잭션 취소 플래그가 True로 설정된 경우 기본 물리적 DTC 트랜잭션이 취소됩니다.

다음 두 방법 중 하나로 .NET 개체로부터 트랜잭션 결과를 제어할 수 있습니다.

  • AutoComplete 특성으로 메서드에 주석을 달고 .NET에서 트랜잭션 결과에 대한 선택 제어를 자동으로 배치합니다. 이 특성을 사용하면 메서드가 예외를 시킬때 일관성 플래그가 자동으로 False로 설정되어 결과적으로 트랜잭션이 취소됩니다. 예외를 발생시지 않고 메서드가 반환되면 일관성 플래그가 True로 설정되어 구성 요소에서 트랜잭션을 커밋해도 된다는 것을 나타냅니다. 이러한 과정은 자동 트랜잭션이 동일 트랜잭션 스트림에서 다른 개체의 선택 사항에 의존하기 때문에 보장되지 않습니다.
  • 일관성 플래그를 각각 True 또는 False로 설정하는 ContextUtil 클래스의 정적 SetComplete 또는 SetAbort 메서드를 호출할 수 있습니다.

심각도가 11 이상인 SQL Server 오류가 발생하면 관리 데이터 공급자가 SqlException 유형의 예외가 발생할 수 있습니다. 메서드가 예외를 알리고 처리하는 경우 트랜잭션을 취소하도록 수동으로 선택하거나 [AutoComplete]로 플래깅된 메서드의 경우 예외가 호출자에게 전파되도록 하십시오.

[AutoComplete] 메서드

AutoComplete 특성으로 표시된 메서드의 경우 다음 중 하나를 수행하십시오.

  • SqlException을 다시 호출 스택으로 전파합니다.
  • 외부 예외에서 SqlException을 래핑하고 이를 호출자에게 전파합니다. 호출자에게 보다 유용한 예외 유형으로 예외를 래핑합니다.

예외를 전파하지 못하면 데이터베이스 오류가 있더라도 개체가 트랜잭션을 취소하도록 선택하지 못할 수 있습니다. 즉, 동일 트랜잭션 스트림을 공유하는 다른 개체에서 수행한 성공적인 작업이 커밋될 수 있습니다.

다음 코드에서는 SqlException을 알리고 이를 호출자에게 직접 전파합니다. 이 개체의 일관성 플래그는 비활성화시에 자동으로 False로 설정되기 때문에 트랜잭션이 결과적으로 취소됩니다.

 [AutoComplete]void SomeMethod(){  try  {    // Open the connection, and perform database operation    . . .  }  catch (SqlException sqlex )  {    LogException( sqlex ); // Log the exception details    throw;                 // Rethrow the exception, causing the consistent                            // flag to be set to false.  }  finally  {    // Close the database connection    . . .  }}  

비-[AutoComplete] 메서드

AutoComplete 특성으로 표시되지 않은 메서드의 경우 다음을 수행해야 합니다.

  • catch 블록 내에서 ContextUtil.SetAbort를 호출하여 트랜잭션을 취소하도록 선택합니다. 이렇게 하면 일관성 플래그가 False로 설정됩니다.
  • 예외가 발생하지 않는 경우 ContextUtil.SetComplete를 호출하여 트랜잭션을 커밋하도록 선택합니다. 이렇게 하면 일관성 플래그가 True(기본 상태)로 설정됩니다.

다음 코드에서는 이러한 방식에 대해 설명합니다.

void SomeOtherMethod(){  try  {    // Open the connection, and perform database operation    . . .    ContextUtil.SetComplete(); // Manually vote to commit the transaction  }  catch (SqlException sqlex)  {    LogException( sqlex );   // Log the exception details    ContextUtil.SetAbort();  // Manually vote to abort the transaction    // Exception is handled at this point and is not propagated to the caller  }  finally  {    // Close the database connection    . . .  }}  
참고여러 catch 블록이 있는 경우 메서드 시작 시에 ContextUtil.SetAbort를 한 번 호출하고 try 블록의 끝에서 ContextUtil.SetComplete를 호출하는 것이 더 쉽습니다. 이러한 방식에서는 모든 catch 블록 내에서 ContextUtil.SetAbort를 반복해서 호출할 필요가 없습니다. 이러한 메서드로 결정된 일관성 플래그의 설정은 메서드가 반환하는 경우에만 필요합니다

이 방법은 호출 중인 코드에서 트랜잭션이 실패할 것이라는 사실을 인식하도록 만들기 때문에 예외(또는 래핑된 예외)를 호출 스택으로 항상 전파해야 합니다. 이렇게 하면 호출 중인 코드에서 최적화를 수행할 수 있습니다. 예를 들어 은행 예금 전송 시나리오의 경우 전송 구성 요소는 차변 작업이 이미 실패한 경우 대변 작업을 수행하지 않도록 결정할 수 있습니다.

일관성 플래그를 False로 설정한 다음 예외가 발생하지 않고 반환된 경우 호출 중인 코드는 트랜잭션이 실패할 것이라는 사실을 알 수 있는 방법이 없습니다. 부울 값을 반환하거나 부울 출력 매개 변수를 설정할 수 있는 경우에도 일관적으로 예외를 발생시켜 오류 조건을 나타내야 합니다. 이렇게 하면 오류 처리에 대한 표준 방식으로 보다 명확하고 일관적인 코드를 만들 수 있습니다.

데이터 페이징

데이터를 통한 페이징은 분산 응용 프로그램에서 일반적인 요구 사항입니다. 예를 들어 전체 목록을 한 번에 표시하는 것이 금지된 경우 책의 목록을 사용자에게 제공할 수 있습니다. 사용자는 데이터에서 데이터의 다음 또는 이전 페이지를 보거나 목록의 처음이나 마지막으로 이동하는 등의 익숙한 동작을 수행하길 바랄 것입니다.

이 섹션에서는 이 기능의 구현 옵션에 대해 설명하고 각 옵션이 확장성 및 성능에 주는 영향에 대해 설명합니다.

옵션 비교

데이터 페이징 옵션은 다음과 같습니다.

  • SqlDataAdapterFill 메서드를 사용하여 쿼리의 결과 범위로 DataSet을 채웁니다.
  • COM 상호 운용성을 통해 ADO를 사용하고 서버측 커서를 사용합니다.
  • 저장 프로시저를 사용하여 데이터 페이징을 수동으로 구현합니다.

데이터 페이징을 위한 최상의 옵션은 아래 나열된 요소에 따라 달라집니다.

  • 확장성 요구 사항
  • 성능 요구 사항
  • 네트워크 대역폭
  • 데이터베이스 서버 메모리 및 성능
  • 중간 계층 서버 메모리 및 성능
  • 쿼리에서 반환된 페이징하려는 행 개수
  • 데이터 페이지 크기

성능 테스트에서는 저장 프로시저를 사용한 수동 방식이 여러 스트레스 수준 범위에서 최상의 성능을 제공하는 것으로 나타났습니다. 하지만 수동 방식은 해당 작업을 서버에서 수행하기 때문에 사이트 기능 중 대부분이 데이터 페이징 기능에 의존하는 경우 서버 스트레스 수준이 중요한 문제가 될 수 있습니다. 이 방식이 사용자의 특정 환경에 적합한지 보장하기 위해서는 특정 요구 사항에 대해 모든 옵션을 테스트해야 합니다.

이에 대한 여러 옵션은 아래에 설명됩니다.

SqlDataAdapter의 Fill 메서드 사용

앞에서 설명한 것과 같이 SqlDataAdapter는 데이터베이스의 데이터로 DataSet을 채우는 데 사용됩니다. 오버로드된 Fill 메서드(다음 코드 참조) 중 하나는 두 개의 정수 인덱스 값을 사용합니다.

public int Fill(   DataSet dataSet,   int startRecord,   int maxRecords,   string srcTable);  

startRecord 값은 시작 레코드의 0 기반 인덱스를 나타냅니다. maxRecords 값은 startRecord부터 시작하여 새로운 DataSet에 복사할 레코드 수를 나타냅니다.

내부적으로 SqlDataAdapterSqlDataReader를 사용하여 쿼리를 실행하고 결과를 반환합니다. SqlDataAdapter는 결과를 읽고 SqlDataReader로부터 데이터 읽기에 따라 DataSet을 만듭니다. SqlDataAdapterstartRecordmaxRecords를 통해 모든 결과를 새로 생성된 DataSet에 복사하고 필요하지 않은 결과는 삭제합니다. 즉, 불필요한 여러 데이터를 네트워크에서 데이터 액세스 클라이언트로 가져올 가능성이 있으며, 이러한 점이 바로 이 방식의 주요 단점이 됩니다.

예를 들어 1,000개의 레코드가 있고 900에서 950까지의 레코드만 필요한 경우에도 앞 부분의 899개의 레코드를 네트워크에서 가져와서 클라이언트 측에서 삭제합니다. 이러한 오버헤드는 결과 집합이 적을 경우 크게 문제가 되지 않을 수 있지만 대규모 데이터 집합에서 페이징을 수행할 경우 심각해질 수 있습니다.

ADO 사용

페이징 구현을 위한 또 다른 옵션은 COM 기반 ADO를 사용하여 페이징을 수행하는 것입니다. 이러한 옵션의 주요 목적은 ADO Recordset 개체를 통해 제공되는 서버 측 커서에 대한 액세스를 얻기 위한 것입니다. Recordset 커서 위치를 adUseServer로 설정할 수 있습니다. OLE DB 공급자가 이를 지원하는 경우(예: SQLOLEDB), 서버측 커서를 사용할 수 있게 됩니다. 그런 다음 이 커서를 사용하여 네트워크에서 모든 레코드를 데이터 액세스 클라이언트 코드로 끌어오지 않아도 시작 레코드로 직접 이동할 수 있습니다.

이 방식은 다음과 같은 두 가지 주요 단점이 있습니다.

  • 대부분의 경우 클라이언트 관리 코드에서 사용할 수 있도록 Recordset 개체에 반환된 레코드를 DataSet으로 변환해야 합니다. OleDbDataAdapterFill 메서드를 오버로드하여 ADO Recordset 개체를 사용하고 이를 DataSet으로 변환하지만 특정 레코드를 시작하고 끝내는 기능이 없습니다. 가능한 유일한 옵션은 Recordset 개체에서 시작 레코드로 이동하고, 각 레코드를 루프 처리한 다음, 데이터를 새로 수동으로 생성한 DataSet으로 직접 복사하는 것입니다. 특히 COM Interop 호출의 오버헤드를 통한 이러한 작업 수행은 특히 작은 DataSet의 경우 추가 데이터를 네트워크에서 끌어오지 않는다는 장점을 더욱 줄여 줍니다.
  • 서버에서 원하는 데이터를 끌어오는 동안 연결과 서버측 커서를 열린 상태로 유지합니다. 커서는 일반적으로 데이터베이스 서버에서 열고 유지 관리하는 데 비용이 많이 드는 리소스입니다. 이 옵션은 성능을 향상시킬 수 있지만 오랜 시간 동안 서버에서 귀중한 리소스를 소비하므로 확장성을 감소시킬 가능성이 있습니다.

수동 구현 사용

이 섹션에서 다뤄지는 데이터 페이징에 대한 마지막 옵션은 저장 프로시저를 사용하여 응용 프로그램에 페이징 기능을 수동으로 구현하는 것입니다. 고유 키가 포함된 테이블의 경우 저장 프로시저를 비교적 쉽게 구현할 수 있습니다. 고유 키가 없는(고유키가 많지 않아야 하는) 테이블의 경우 이 프로세스는 복잡해질 수 있습니다.

고유 키가 있는 테이블에 대한 페이징

테이블에 고유 키가 있는 경우 WHERE 절에서 키를 사용하여 특정 행으로부터 시작하는 결과 집합을 만들 수 있습니다. SET ROWCOUNT 문 또는 결과 집합의 크기를 제한하는 데 사용되는 SQL Server TOP 문과 함께 사용되는 이 방식은 효율적인 페이징 메커니즘을 제공합니다. 이 방식은 다음 저장 프로시저 코드에 설명되어 있습니다.

CREATE PROCEDURE GetProductsPaged@lastProductID int,@pageSize intASSET ROWCOUNT @pageSizeSELECT *FROM ProductsWHERE [standard search criteria]AND ProductID > @lastProductIDORDER BY [Criteria that leaves ProductID monotonically increasing]GO  

이 저장 프로시저의 호출자는 단순히 lastProductID 값을 유지하고 연속적인 호출 사이에 선택된 페이지 크기에 따라 이를 늘리거나 줄입니다.

고유 키가 없는 테이블에 대한 페이징

페이징하려는 테이블에 고유 키가 없는 경우 ID 열 사용 등의 방법으로 고유 키를 추가하는 방법을 고려하십시오. 이렇게 하면 앞에서 설명한 페이징 솔루션을 구현할 수 있습니다.

고유 키가 없는 테이블의 경우에도 결과 집합의 일부인 두 개 이상의 필드를 조합하여 고유성을 생성할 수만 있다면 효과적인 페이징 솔루션을 구현할 수 있습니다.

예를 들어 다음 테이블을 고려하십시오.

Col1Col2Col3기타열ç
A1Wç
A1Xç
A1Yç
A1Zç
A2Wç
A2Xç
B1Wç
B1Xç

이 테이블에서는 Col1, Col2Col3을 조합하여 고유성을 생성할 수 있습니다. 따라서 다음 저장 프로시저에 설명된 방식을 사용하여 페이징 메커니즘을 구현할 수 있습니다.

CREATE PROCEDURE RetrieveDataPaged@lastKey char(40),@pageSize intASSET ROWCOUNT @pageSizeSELECTCol1, Col2, Col3, Col4, Col1+Col2+Col3 As KeyFieldFROM SampleTableWHERE [Standard search criteria]AND Col1+Col2+Col3 > @lastKeyORDER BY Col1 ASC, Col2 ASC, Col3 ASCGO  

클라이언트는 저장 프로시저로 반환된 KeyField 열의 마지막 값을 유지하고 이를 다시 저장 프로시저에 삽입하여 테이블의 페이징을 제어합니다.

수동 구현이 데이터베이스 서버의 오버헤드를 증가시키지만 네트워크에서 불필요한 데이터 전송은 방지합니다. 성능 테스트에서는 여러 스트레스 수준 범위에서는 이 방식이 적합한 것으로 나타났습니다. 하지만 사이트 작업 중 데이터 페이징 기능이 차지하는 비중에 따라 서버의 수동 페이징 수행이 응용 프로그램의 확장성에 미치는 영향이 달라질 수 있습니다. 특정 응용 프로그램 시나리오에 맞는 최적의 방식을 찾기 위해서는 자체 환경에서 성능 테스트를 실행해야 합니다.

부록

.NET 클래스에 개체 생성을 설정하는 방법

엔터프라이즈(COM+) 서비스를 사용하여 개체 생성에 .NET 관리 클래스를 설정할 수 있습니다.

.NET 관리되는 클래스를 설정하려면

  1. System.EnterpriseServices 이름 공간에 있는 ServicedComponent 클래스로부터 클래스를 파생합니다.
    using System.EnterpriseServices;public class DataAccessComponent : ServicedComponent  
  2. ConstructionEnabled 특성을 클래스에 설정하고 선택적으로 기본 생성 문자열을 지정합니다. 이 기본값은 COM+ 카탈로그에 보유됩니다. 관리자는 구성 요소 서비스 MMCMicrosoft Management Console) 스냅인을 사용하여 값을 유지할 수 있습니다.
     [ConstructionEnabled(Default="default DSN")]public class DataAccessComponent : ServicedComponent  
  3. 가상 Construct 메서드의 무시된 구현을 제공합니다. 이 메서드는 개체의 언어 관련 생성자 이후에 호출됩니다. COM+ 카탈로그에 포함된 생성 문자열은 이 메서드에 단일 인수로 제공됩니다.
    public override void Construct( string constructString ){  // Construct method is called next after constructor.  // The configured DSN is supplied as the single argument}  
  4. AssemblyKey 파일 또는 AssemblyKeyName 특성을 통해 서명하여 어셈블리에 대한 강력한 이름을 제공합니다. COM+ 서비스로 등록된 모든 어셈블리에는 강력한 이름이 있어야 합니다. 강력하게 명명된 어셈블리에 대한 자세한 내용은 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconWorkingWithStrongly-NamedAssemblies.asp (영문)를 참조하십시오.
     [assembly: AssemblyKeyFile("DataServices.snk")]  
  5. 동적(느린) 등록을 지원하려면 어셈블리 수준 특성인 ApplicationNameApplicationActivation을 각각 사용하여 어셈블리 구성 요소 및 응용 프로그램의 활성화 유형을 유지하는 데 사용되는 COM+ 응용 프로그램의 이름을 지정합니다. 어셈블리 등록에 대한 자세한 내용은 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconRegisteringServicedComponents.asp (영문)를 참조하십시오.
    // the ApplicationName attribute specifies the name of the// COM+ Application which will hold assembly components[assembly : ApplicationName("DataServices")]// the ApplicationActivation.ActivationOption attribute specifies // where assembly components are loaded on activation// Library : components run in the creator's process// Server : components run in a system process, dllhost.exe[assembly: ApplicationActivation(ActivationOption.Library)]  

다음 코드 단편은 COM+ 생성 문자열을 사용하여 데이터베이스 연결 문자열을 가져오는 DataAccessComponent라는 서비스 구성 요소를 보여 줍니다.

using System;using System.EnterpriseServices;// the ApplicationName attribute specifies the name of the// COM+ Application which will hold assembly components[assembly : ApplicationName("DataServices")]// the ApplicationActivation.ActivationOption attribute specifies // where assembly components are loaded on activation// Library : components run in the creator's process// Server : components run in a system process, dllhost.exe[assembly: ApplicationActivation(ActivationOption.Library)]// Sign the assembly. The snk key file is created using the // sn.exe utility[assembly: AssemblyKeyFile("DataServices.snk")][ConstructionEnabled(Default="Default DSN")]public class DataAccessComponent : ServicedComponent{    private string connectionString;    public DataAccessComponent()    {      // constructor is called on instance creation    }    public override void Construct( string constructString )    {      // Construct method is called next after constructor.      // The configured DSN is supplied as the single argument      this.connectionString = constructString;    }}  

SqlDataAdapter를 사용하여 여러 행을 검색하는 방법

다음 코드에서는 SqlDataAdapter 개체를 사용하여 DataSet 또는 DataTable을 생성하는 명령을 실행하는 방법을 보여 줍니다. 이 코드는 SQL Server Northwind 데이터베이스에서 제품 범주 집합을 검색합니다.

using System.Data;using System.Data.SqlClient;public DataTable RetrieveRowsWithDataTable(){  using ( SqlConnection conn = new SqlConnection(connectionString) )  {    conn.Open();    SqlCommand cmd = new SqlCommand("DATRetrieveProducts", conn);    cmd.CommandType = CommandType.StoredProcedure;    SqlDataAdapter adapter = new SqlDataAdapter( cmd );    DataTable dataTable = new DataTable("Products");    adapter .Fill(dataTable);    return dataTable;  }}  

SqlAdapter를 사용하여 DataSet 또는 DataTable을 생성하려면

  1. SqlCommand 개체를 만들어서 저장 프로시저를 호출하고 이를 SqlConnection 개체(표시됨) 또는 연결 문자열(표시되지 않음)과 연결합니다.
  2. 새로운 SqlDataAdapter 개체를 만들고 이를 SqlCommand 개체와 연결합니다.
  3. DataTable(또는 선택적으로 DataSet) 개체를 만듭니다. 생성자 인수를 사용하여 DataTable을 명명합니다.
  4. SqlDataAdapter 개체의 Fill 메서드를 호출하여 DataSet 또는 DataTable을 검색된 행으로 채웁니다.

SqlDataReader를 사용하여 여러 행을 검색하는 방법

SqlDataReader를 사용하여 여러 행을 검색하는 방법은 다음 코드 단편에 설명되어 있습니다.

using System.IO;using System.Data;using System.Data.SqlClient;public SqlDataReader RetrieveRowsWithDataReader(){  SqlConnection conn = new SqlConnection(         "server=(local);Integrated Security=SSPI;database=northwind");  SqlCommand cmd = new SqlCommand("DATRetrieveProducts", conn );  cmd.CommandType = CommandType.StoredProcedure;  try  {    conn.Open();    // Generate the reader. CommandBehavior.CloseConnection causes the    // the connection to be closed when the reader object is closed    return( cmd.ExecuteReader( CommandBehavior.CloseConnection ) );  }  catch  {    conn.Close();    throw;  }}// Display the product list using the consoleprivate void DisplayProducts(){  SqlDataReader reader = RetrieveRowsWithDataReader();  try  {    while (reader.Read())    {      Console.WriteLine("{0} {1} {2}",                         reader.GetInt32(0).ToString(),                         reader.GetString(1) );    }  }  finally  {    reader.Close(); // Also closes the connection due to the                    // CommandBehavior enum used when generating the reader  }}  

SqlDataReader를 사용하여 행을 검색하려면

  1. 저장 프로시저를 실행하는 데 사용되는 SqlCommand 개체를 만들고 이를 SqlConnection 개체와 연결합니다.
  2. 연결을 엽니다.
  3. SqlCommand 개체의 ExecuteReader 메서드를 호출하여 SqlDataReader 개체를 생성합니다.
  4. 스트림에서 데이터를 읽으려면 SqlDataReader 개체의 Read 메서드를 호출하여 행을 검색하고 형식있는 메서드(예: GetInt32GetString 메서드)를 사용하여 열 값을 검색합니다.
  5. 판독기 작업이 끝나면 해당 Close 메서드를 호출합니다.

XmlReader를 사용하여 여러 행을 검색하는 방법

SqlCommand 개체를 사용하여 XML 데이터에 대한 전진 전용의 스트림 기반 액세스를 제공하는 XmlReader 개체를 생성합니다. 이 명령(일반적으로 저장 프로시저)은 SQL Server 2000의 경우 일반적으로 유효한 FOR XML 절이 포함된 SELECT 문으로 구성되는 XML 기반 결과 집합을 생성해야 합니다. 다음 코드 단편에서는 이러한 방식에 대해 설명합니다.

public void RetrieveAndDisplayRowsWithXmlReader(){  using( SqlConnection conn = new SqlConnection(connectionString) )  {;    SqlCommand cmd = new SqlCommand("DATRetrieveProductsXML", conn );    cmd.CommandType = CommandType.StoredProcedure;try  {      conn.Open();    XmlTextReader xreader = (XmlTextReader)cmd.ExecuteXmlReader();    while ( xreader.Read() )    {      if ( xreader.Name == "PRODUCTS" )       {        string strOutput = xreader.GetAttribute("ProductID");        strOutput += " ";        strOutput += xreader.GetAttribute("ProductName");        Console.WriteLine( strOutput );      }    }    xreader.Close();  // XmlTextReader does not support IDisposable so it can't be                      // used within a using keyword   }}  

앞의 코드에는 다음 저장 프로시저가 사용됩니다.

CREATE PROCEDURE DATRetrieveProductsXMLASSELECT * FROM PRODUCTS FOR XML AUTOGO  

XmlReader를 사용하여 XML 데이터를 검색하려면

  1. SqlCommand 개체를 만들어서 XML 결과 집합을 생성하는 저장 프로시저를 호출합니다(예: SELECT 문에서 FOR XML 절 사용). SqlCommand 개체를 연결과 연결합니다.
  2. SqlCommand 개체의 ExecuteXmlReader 메서드를 호출하고 결과를 전진 전용 XmlTextReader 개체에 할당합니다. 이 유형은 반환된 데이터의 XML 기반 유효성 검사가 필요하지 않은 경우 사용해야 하는 가장 빠른 XmlReader 개체 유형입니다.
  3. XmlTextReader 개체의 Read 메서드를 사용하여 데이터를 읽습니다.

저장 프로시저 출력 매개 변수를 사용하여 단일 행을 검색하는 방법

명명된 출력 매개 변수를 사용하여 단일 행 내의 검색된 데이터 항목을 반환하는 저장 프로시저를 호출할 수 있습니다. 다음 코드 단편에서는 저장 프로시저를 사용하여 Northwind 데이터베이스에 있는 Products 테이블에 포함된 특정 제품에 대한 제품 이름 및 단가를 검색합니다.

void GetProductDetails( int ProductID,                         out string ProductName, out decimal UnitPrice ){  using( SqlConnection conn = new SqlConnection(        "server=(local);Integrated Security=SSPI;database=Northwind") )  {    // Set up the command object used to execute the stored proc    SqlCommand cmd = new SqlCommand( "DATGetProductDetailsSPOutput", conn )    cmd.CommandType = CommandType.StoredProcedure;    // Establish stored proc parameters.    //  @ProductID int INPUT    //  @ProductName nvarchar(40) OUTPUT    //  @UnitPrice money OUTPUT    // Must explicitly set the direction of output parameters    SqlParameter paramProdID =              cmd.Parameters.Add( "@ProductID", ProductID );    paramProdID.Direction = ParameterDirection.Input;    SqlParameter paramProdName =              cmd.Parameters.Add( "@ProductName", SqlDbType.VarChar, 40 );    paramProdName.Direction = ParameterDirection.Output;    SqlParameter paramUnitPrice =              cmd.Parameters.Add( "@UnitPrice", SqlDbType.Money );    paramUnitPrice.Direction = ParameterDirection.Output;    conn.Open();    // Use ExecuteNonQuery to run the command.     // Although no rows are returned any mapped output parameters     // (and potentially return values) are populated     cmd.ExecuteNonQuery( );    // Return output parameters from stored proc    ProductName = paramProdName.Value.ToString();    UnitPrice = (decimal)paramUnitPrice.Value;   }}  

저장 프로시저 출력 매개 변수를 사용하여 단일 행을 검색하려면

  1. SqlCommand 개체를 만들고 이를 SqlConnection 개체와 연결합니다.
  2. SqlCommand's Parameters 컬렉션의 Add 메서드를 호출하여 저장 프로시저 매개 변수를 설정합니다. 기본적으로 매개 변수는 입력 매개 변수로 간주되기 때문에 출력 매개 변수의 방향을 명시적으로 설정해야 합니다.
    참고입력 매개 변수를 포함하여 모든 매개 변수의 방향을 명시적으로 설정하는 것이 좋습니다.
  3. 연결을 엽니다.
  4. SqlCommand 개체의 ExecuteNonQuery 메서드를 호출합니다. 이렇게 하면 출력 매개 변수(및 반환 값)를 채웁니다.
  5. Value 속성을 사용하여 적합한 SqlParameter 개체로부터 출력 매개 변수를 검색합니다.
  6. 연결을 닫습니다.

앞의 코드 단편에서는 다음 저장 프로시저를 호출합니다.

CREATE PROCEDURE DATGetProductDetailsSPOutput@ProductID int,@ProductName nvarchar(40) OUTPUT,@UnitPrice money OUTPUTASSELECT @ProductName = ProductName,        @UnitPrice = UnitPrice FROM Products WHERE ProductID = @ProductIDGO  

SqlDataReader를 사용하여 단일 행을 검색하는 방법

SqlDataReader 개체를 사용하여 단일 행을 검색하고 특히 반환된 데이터 스트림에서 원하는 열을 검색할 수 있습니다. 이에 대해서는 다음 코드 단편에서 설명합니다.

void GetProductDetailsUsingReader( int ProductID,                         out string ProductName, out decimal UnitPrice ){  using( SqlConnection conn = new SqlConnection(         "server=(local);Integrated Security=SSPI;database=Northwind") )  {    // Set up the command object used to execute the stored proc    SqlCommand cmd = new SqlCommand( "DATGetProductDetailsReader", conn );    cmd.CommandType = CommandType.StoredProcedure;    // Establish stored proc parameters.    //  @ProductID int INPUT    SqlParameter paramProdID = cmd.Parameters.Add( "@ProductID", ProductID );    paramProdID.Direction = ParameterDirection.Input;    conn.Open();    using( SqlDataReader reader = cmd.ExecuteReader() )    {      if( reader.Read() ) // Advance to the one and only row      {        // Return output parameters from returned data stream        ProductName = reader.GetString(0);        UnitPrice = reader.GetDecimal(1);       }    }  }}  

SqlDataReader 개체를 사용하여 단일 행을 반환하려면

  1. SqlCommand 개체를 설정합니다.
  2. 연결을 엽니다.
  3. SqlDataReader 개체의 ExecuteReader 메서드를 호출합니다.
  4. SqlDataReader 개체의 형식있는 접근자 메서드(이 경우에는 GetStringGetDecimal)를 사용하여 출력 매개 변수를 검색합니다.

앞의 코드 단편에서는 다음 저장 프로시저를 호출합니다.

CREATE PROCEDURE DATGetProductDetailsReader@ProductID intASSELECT ProductName, UnitPrice FROM ProductsWHERE ProductID = @ProductIDGO  

ExecuteScalar를 사용하여 단일 항목을 검색하는 방법

ExecuteScalar 메서드는 단일 값만 반환하는 쿼리를 위해 디자인되었습니다. 여러 열 및/또는 행을 반환하는 쿼리의 경우 ExecuteScalar는 첫 번째 행의 첫 번째 열만 반환합니다.

다음 코드에서는 특정 제품 ID에 대한 제품 이름을 조회하는 방법을 보여 줍니다.

void GetProductNameExecuteScalar( int ProductID, out string ProductName ){  using( SqlConnection conn = new SqlConnection(         "server=(local);Integrated Security=SSPI;database=northwind") )  {    SqlCommand cmd = new SqlCommand("LookupProductNameScalar", conn );    cmd.CommandType = CommandType.StoredProcedure;    cmd.Parameters.Add("@ProductID", ProductID );    conn.Open();    ProductName = (string)cmd.ExecuteScalar();  }}  

ExecuteScalar를 사용하여 단일 항목을 검색하려면

  1. 저장 프로시저를 호출하기 위한 SqlCommand 개체를 설정합니다.
  2. 연결을 엽니다.
  3. ExecuteScalar 메서드를 호출합니다. 이 메서드는 개체 유형을 반환합니다. 여기에는 검색된 첫 번째 열의 값이 포함되며 적합한 유형으로 형변환되어야 합니다.
  4. 연결을 닫습니다.

앞의 코드에는 다음 저장 프로시저가 사용됩니다.

CREATE PROCEDURE LookupProductNameScalar@ProductID intASSELECT TOP 1 ProductNameFROM ProductsWHERE ProductID = @ProductIDGO  

저장 프로시저 출력 또는 반환 매개 변수를 사용하여 단일 항목을 검색하는 방법

저장 프로시저 출력 또는 반환 매개 변수를 사용하여 단일 값을 조회할 수 있습니다. 다음 코드에는 출력 매개 변수를 사용하는 방법이 설명되어 있습니다.

void GetProductNameUsingSPOutput( int ProductID, out string ProductName ){  using( SqlConnection conn = new SqlConnection(        "server=(local);Integrated Security=SSPI;database=northwind") )  {    SqlCommand cmd = new SqlCommand("LookupProductNameSPOutput", conn );    cmd.CommandType = CommandType.StoredProcedure;    SqlParameter paramProdID = cmd.Parameters.Add("@ProductID", ProductID );    ParamProdID.Direction = ParameterDirection.Input;    SqlParameter paramPN =            cmd.Parameters.Add("@ProductName", SqlDbType.VarChar, 40 );    paramPN.Direction = ParameterDirection.Output;    conn.Open();    cmd.ExecuteNonQuery();    ProductName = paramPN.Value.ToString();    }}  

저장 프로시저 출력 매개 변수를 사용하여 단일 값을 검색하려면

  1. 저장 프로시저를 호출하기 위한 SqlCommand 개체를 설정합니다.
  2. SqlCommandParameters 컬렉션에 SqlParameters를 추가하여 입력 매개 변수 및 단일 출력 매개 변수를 설정합니다.
  3. 연결을 엽니다.
  4. SqlCommand 개체의 ExecuteNonQuery 메서드를 호출합니다.
  5. 연결을 닫습니다.
  6. 출력 SqlParameterValue 속성을 사용하여 출력 값을 검색합니다.

앞의 코드에는 다음 저장 프로시저가 사용됩니다.

CREATE PROCEDURE LookupProductNameSPOutput @ProductID int,@ProductName nvarchar(40) OUTPUTASSELECT @ProductName = ProductNameFROM ProductsWHERE ProductID = @ProductIDGO  

다음 코드에서는 특정 행이 존재하는지 여부를 나타내기 위해 반환 값을 사용하는 방법을 보여 줍니다. 코딩 관점에서 볼 때 이 방법은 SqlParameter 방향을 ParameterDirection.ReturnValue로 명시적으로 설정해야 한다는 점을 제외하면 저장 프로시저 출력 매개 변수를 사용하는 방법과 비슷합니다.

bool CheckProduct( int ProductID ){  using( SqlConnection conn = new SqlConnection(       "server=(local);Integrated Security=SSPI;database=northwind") )  {    SqlCommand cmd = new SqlCommand("CheckProductSP", conn );    cmd.CommandType = CommandType.StoredProcedure;    cmd.Parameters.Add("@ProductID", ProductID );    SqlParameter paramRet =             cmd.Parameters.Add("@ProductExists", SqlDbType.Int );    paramRet.Direction = ParameterDirection.ReturnValue;    conn.Open();    cmd.ExecuteNonQuery();  }  return (int)paramRet.Value == 1;}  

저장 프로시저 반환 값을 사용하여 특정 행이 존재하는지 여부를 확인하려면

  1. 저장 프로시저를 호출하기 위한 SqlCommand 개체를 설정합니다.
  2. 액세스할 행의 기본 키 값이 포함된 입력 매개 변수를 설정합니다.
  3. 단일 반환 값 매개 변수를 설정합니다. SqlCommandParameters 컬렉션에 SqlParameter 개체를 추가하고 해당 방향을 ParameterDirection.ReturnValue로 설정합니다.
  4. 연결을 엽니다.
  5. SqlCommand 개체의 ExecuteNonQuery 메서드를 호출합니다.
  6. 연결을 닫습니다.
  7. 반환 값 SqlParameterValue 속성을 사용하여 반환 값을 검색합니다.

앞의 코드에는 다음 저장 프로시저가 사용됩니다.

CREATE PROCEDURE CheckProductSP @ProductID intASIF EXISTS( SELECT ProductID           FROM Products           WHERE ProductID = @ProductID )  return 1ELSE  return 0GO  

SqlDataReader를 사용하여 단일 항목을 검색하는 방법

SqlDataReader 개체를 사용하면 명령 개체의 ExecuteReader 메서드를 호출하여 단일 출력 값을 가져올 수 있습니다. 이를 위해서는 SqlDataReader Read 메서드를 호출해야 하고 원하는 값이 판독기의 접근자 메서드 중 하나를 통해 검색되기 때문에 조금 더 많은 코드가 필요합니다. 다음 코드에는 SqlDataReader 개체를 사용하는 방법이 설명되어 있습니다.

bool CheckProductWithReader( int ProductID ){  using( SqlConnection conn = new SqlConnection(         "server=(local);Integrated Security=SSPI;database=northwind") )  {    SqlCommand cmd = new SqlCommand("CheckProductExistsWithCount", conn );    cmd.CommandType = CommandType.StoredProcedure;    cmd.Parameters.Add("@ProductID", ProductID );    cmd.Parameters["@ProductID"].Direction = ParameterDirection.Input;    conn.Open();    using( SqlDataReader reader = cmd.ExecuteReader(                                CommandBehavior.SingleResult ) )    {    if( reader.Read() )    {      return (reader.GetInt32(0) > 0);    }    return false;  }}  

앞의 코드에는 다음 저장 프로시저가 사용되는 것으로 가정됩니다.

CREATE PROCEDURE CheckProductExistsWithCount @ProductID intASSELECT COUNT(*) FROM ProductsWHERE ProductID = @ProductIDGO  

ADO.NET 수동 트랜잭션을 코딩하는 방법

다음 코드에서는 트랜잭션으로 예금 전송 작업을 보호하기 위해 SQL Server .NET Data Provider에서 제공되는 트랜잭션 지원의 장점을 활용하는 방법을 보여 줍니다. 이 작업은 동일 데이터베이스에 있는 두 계정 간에 예금을 전송합니다.

public void TransferMoney( string toAccount, string fromAccount, decimal amount ){  using ( SqlConnection conn = new SqlConnection(            "server=(local);Integrated Security=SSPI;database=SimpleBank" ) )  {    SqlCommand cmdCredit = new SqlCommand("Credit", conn );    cmdCredit.CommandType = CommandType.StoredProcedure;    cmdCredit.Parameters.Add( new SqlParameter("@AccountNo", toAccount) );    cmdCredit.Parameters.Add( new SqlParameter("@Amount", amount ));    SqlCommand cmdDebit = new SqlCommand("Debit", conn );    cmdDebit.CommandType = CommandType.StoredProcedure;    cmdDebit.Parameters.Add( new SqlParameter("@AccountNo", fromAccount) );    cmdDebit.Parameters.Add( new SqlParameter("@Amount", amount ));    conn.Open();    // Start a new transaction    using ( SqlTransaction trans = conn.BeginTransaction() )    {      // Associate the two command objects with the same transaction      cmdCredit.Transaction = trans;      cmdDebit.Transaction = trans;      try      {        cmdCredit.ExecuteNonQuery();        cmdDebit.ExecuteNonQuery();        // Both commands (credit and debit) were successful        trans.Commit();      }      catch( Exception ex )      {        // transaction failed        trans.Rollback();        // log exception details . . .        throw ex;      }    }  }}  

Transact-SQL을 사용하여 트랜잭션을 수행하는 방법

다음 저장 프로시저에서는 Transact-SQL 저장 프로시저 내에서 트랜잭션 예금 전송 작업을 수행하는 방법을 보여 줍니다.

CREATE PROCEDURE MoneyTransfer@FromAccount char(20),@ToAccount char(20),@Amount moneyASBEGIN TRANSACTION-- PERFORM DEBIT OPERATIONUPDATE AccountsSET Balance = Balance - @AmountWHERE AccountNumber = @FromAccountIF @@RowCount = 0BEGIN  RAISERROR('Invalid From Account Number', 11, 1)  GOTO ABORTENDDECLARE @Balance moneySELECT @Balance = Balance FROM ACCOUNTSWHERE AccountNumber = @FromAccountIF @BALANCE < 0BEGIN  RAISERROR('Insufficient funds', 11, 1)  GOTO ABORTEND-- PERFORM CREDIT OPERATIONUPDATE Accounts SET Balance = Balance + @Amount WHERE AccountNumber = @ToAccountIF @@RowCount = 0BEGIN  RAISERROR('Invalid To Account Number', 11, 1)  GOTO ABORTENDCOMMIT TRANSACTIONRETURN 0ABORT:  ROLLBACK TRANSACTIONGO  

이 저장 프로시저는 BEGIN TRANSACTION, COMMIT TRANSACTIONROLLBACK TRANSACTION 문을 사용하여 트랜잭션을 수동으로 제어합니다.

트랜잭션 .NET 클래스를 코딩하는 방법

다음 코드 예에서는 자동 트랜잭션으로 구성된 세 가지 서비스 .NET 관리 클래스를 보여 줍니다. 각 클래스는 Transaction 특성으로 주석이 달려 있으며 이 특성의 값은 새 트랜잭션 스트림이 시작되어야 하는지 여부 또는 개체가 해당 직속 호출자의 스트림을 공유해야 하는지 여부를 결정합니다. 이러한 구성 요소는 함께 작동하여 은행 예금 전송을 수행합니다. Transfer 클래스는 RequiresNew 트랜잭션 특성으로 구성되어 있으며 DebitCredit 클래스는 Required로 구성되어 있습니다. 따라서 런타임 시 세 가지 개체 모두 동일한 트랜잭션을 공유합니다.

using System;using System.EnterpriseServices;[Transaction(TransactionOption.RequiresNew)]public class Transfer : ServicedComponent{  [AutoComplete]  public void Transfer( string toAccount,                         string fromAccount, decimal amount )  {    try    {      // Perform the debit operation      Debit debit = new Debit();      debit.DebitAccount( fromAccount, amount );      // Perform the credit operation      Credit credit = new Credit();      credit.CreditAccount( toAccount, amount );    }    catch( SqlException sqlex )    {      // Handle and log exception details      // Wrap and propagate the exception      throw new TransferException( "Transfer Failure", sqlex );        }  }}[Transaction(TransactionOption.Required)]public class Credit : ServicedComponent{  [AutoComplete]  public void CreditAccount( string account, decimal amount )  {    try    {      using( SqlConnection conn = new SqlConnection(              "Server=(local); Integrated Security=SSPI"; database="SimpleBank") )      {        SqlCommand cmd = new SqlCommand("Credit", conn );        cmd.CommandType = CommandType.StoredProcedure;        cmd.Parameters.Add( new SqlParameter("@AccountNo", account) );        cmd.Parameters.Add( new SqlParameter("@Amount", amount ));        conn.Open();        cmd.ExecuteNonQuery();      }    }  }catch( SqlException sqlex ){     // Log exception details here     throw; // Propagate exception  }}[Transaction(TransactionOption.Required)]public class Debit : ServicedComponent{  public void DebitAccount( string account, decimal amount )  {    try    {      using( SqlConnection conn = new SqlConnection(              "Server=(local); Integrated Security=SSPI"; database="SimpleBank") )      {        SqlCommand cmd = new SqlCommand("Debit", conn );        cmd.CommandType = CommandType.StoredProcedure;        cmd.Parameters.Add( new SqlParameter("@AccountNo", account) );        cmd.Parameters.Add( new SqlParameter("@Amount", amount ));        conn.Open();        cmd.ExecuteNonQuery();      }     }    catch (SqlException sqlex)    {      // Log exception details here      throw; // Propagate exception back to caller    }  }}  

도와주신 분들

많은 도움을 주신 아래 분들에게 감사드립니다.

Bill Vaughn, Mike Pizzo, Pablo Castro, Doug Rothaus, Kevin White, Blaine Dokter, David Schleifer, Graeme Malcolm (Content Master), Bernard Chen (Sapient), Matt Drucker (Turner Broadcasting), Steve Kirk, David Sceppa, Scott Densmore, Diego González (Lagash Systems)

.NET의 최선의 구현 방법에 대한 자세한 내용을 보려면 patterns & practices (영문) 웹 페이지를 방문해 주십시오.

이 항목과 관련된 온라인 협력 개발 환경에 참여하려면 GotDotNet 작업 공간: Microsoft Patterns & Practices Data Access for .NET Workspace (영문)에 참여하십시오. 이 작업 공간의 커뮤니티와 귀하의 데이터 액세스 차단 질문, 제안 및 특정 정보를 공유하시기 바랍니다.

질문, 정보 또는 제안할 사항이 있거나 이 문서에 대한 의견을 보내시려면 devfdbck@microsoft.com으로 전자 메일을 보내 주시기 바랍니다.

http://www.microsoft.com/korea/msdn/SQL/sqlreldata/ADO.NET/default.aspx?pull=/korea/msdn/library/ko-kr/dnbda/html/daag.aspx

Posted by 퓨전마법사
,