달력

02

« 2012/02 »

  •  
  •  
  •  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  •  
  •  
  •  

'Ado.net'에 해당되는 글 1

  1. 2008/01/09 ADO.NET 표준사례
2008/01/09 14:40

ADO.NET 표준사례 General Tech.2008/01/09 14:40

Dennis Lu
Doug Rothaus
Microsoft Corporation

2002년 7월

적용대상:
Microsoft ADO.NET,Microsoft .NET Framework을 사용한 경험이 있는 개발자

요약: 개발자가 Microsoft ADO.NET을 사용하여 코드를 작성할 때 고려해야 하는 표준사례와 ADO.NET 안에 포함된 개체를 사용하는 방법에 대한 지침을 소개합니다.

알림 만약, ADO.NET과 .NET Framework에 친숙하지 않다면, .NET Framework SDK에 포함되어 있는 ADO.NET을 사용한 데이터 액세스 (영문) 문서를 참조하십시오. 응용프로그램을 ADO.NET으로 마이그레이션하는 분야에 관심이 있다면, ADO 프로그래머를 위한 ADO.NET (영문) 문서를 참조하십시오.

목차

소개
.NET Framework Data Provider
DataReader, DataSet, DataAdapter, DataView 개체 사용
Command 개체 사용
Connection 개체 사용
XML과 통합
유용한 팁

소개

Microsoft ADO.NET 기반 응용프로그램을 구현할 때 최적의 성능, 확장성, 기능성을 보장하기 위하여 사용할 수 있는 표준 솔루션을 소개합니다. ADO.NET에 포함되어 있는 각 개체를 사용하는 방법에 대한 표준사례를 소개하고, ADO.NET의 설계를 최적화하기 위한 제안과 ADO.NET 기반 응용프로그램의 설계상 고려해야 하는 제안사항에 대해서 소개합니다.

본문 기사에는 다음과 같은 내용이 포함되어 있습니다.:

  • .NET Framework에 포함되어 있는 .NET Framework Data Provider에 대한 정보.
  • DataSet 개체와 DataReader개체의 차이점과 각 개체를 사용하는 표준사례.
  • DataSet 개체와 Command 개체,Connections개체를 사용하는 방법.
  • XML 통합관련 정보.
  • 일반적인 팁과 이슈사항.

ADO.NET 표준사례에 대한 좀 더 자세한 정보는 MSDN 라이브러리에 소개된 .NET 데이터 액세스 아키텍처 가이드 (영문) 를 참조하십시오. .NET 데이터 액세스 아키텍처 가이드에서는 주로 Microsoft SQL Server 7.0 이후 버전의 기술구조를 중점으로 소개합니다.

다음은 ADO.NET에 대한 추가적인 정보를 제공하는 기사 목록입니다.

.NET Framework Data Provider

.NET Framework Data Provider는 데이터 원본과 응용프로그램을 연결시켜 주는 교량역할을 합니다. .NET Framework Data Provider는 데이터 원본으로부터 질의 결과를 반환하여 주며, 데이터 원본에 대한 명령을 실행하고, DataSet 개체에서 발생한 변경내역을 데이터 원본에 반영하는 역할을 담당합니다. 본문에서는 각 상황에 가장 적합한 .NET Framework를 선택하기 위해 고려해야 하는 팁을 소개합니다.

어떤 .NET Framework Data Provider를 사용해야 할까요?

응용프로그램이 최상의 성능을 발휘할 수 있도록 하기 위해서는, 각 데이터 원본에 최적화되어 있는 .NET Framework Data Provider를 사용해야 합니다. 응용프로그램에서 사용할 수 있는 데이터 공급자는 여러 종류가 있습니다. 다음 표는 각 데이터 원본에 최적화되어 있는 데이터 공급자에 대해 설명하고 있습니다.

Data Provider 상세설명
SQL Server .NET Data Provider System.Data.SqlClient 네임스페이스를 사용합니다.

Microsoft SQL Server 7.0 또는 그 이후 버전을 사용하는 미들티어 응용프로그램에서 사용할 것을 권장합니다.

Microsoft 데이터엔진(MSDE)이나 Microsoft SQL Server 7.0 또는 그 이후 버전을 기반으로 한 단일계층 응용프로그램에서 사용할 것을 권장합니다.

Microsoft SQL Server 6.5 버전이전을 사용하는 경우에는 OLE DB .NET Data Provider 에 포함되어 있는 OLE DB Provider for SQL Server (영문)를 사용해야 합니다.

OLE DB .NET Data Provider System.Data.OleDb 네임스페이스를 사용합니다.

Microsoft SQL Server 6.5 버전이전이나 .NET Framework SDK의 OLE DB .NET Data Provider에 의해 사용되는 OLE DB 인터페이스 (영문) 목록에 포함된 OLE DB 인터페이스를 지원하는 OLE DB 공급자를 사용하는 미들티어 응용프로그램에서 사용할 것을 권장합니다. (OLE DB 2.5 인터페이스는 필수가 아닙니다.)

Microsoft SQL Server 7.0 이후 버전에서는, SQL Server .NET Data Provider를 사용할 것을 권장합니다.

Microsoft Access 데이터베이스를 사용하는 단일 계층 응용프로그램인 경우, OLE DB .NET Data Provider를 사용할 것을 권장합니다. 미들티어 응용프로그램에서는 Access 데이터베이스를 사용하지 않을 것을 권장합니다.

OLE DB Provider for ODBC (MSDASQL)에 대한 지원은 비활성화되었습니다. ODBC 데이터원본에 접근하는 경우에 사용할 수 있는 ODBC .NET Data Provider(다운로드 (영문))도 현재 다운로드 받을 수 있으며, .NET Framework 1.1버전에 포함되게 될 것입니다.

ODBC .NET Data Provider ODBC .NET Data Provider 는 현재 다운로드 가능합니다.(다운로드 (영문))

Microsoft.Data.Odbc 네임스페이스를 사용합니다.

ODBC 드라이버를 사용하여 데이터 원본에 접근할 수 있도록 하는 기능을 제공합니다.

알림 ODBC .NET Data Provider 는 .NET Framework의 1.1버전에 포함될 예정입니다. ODBC .NET Data Provider 는 System.Data.Odbc 네임스페이스를 사용합니다.

.NET Data Provider for Oracle .NET Data Provider for Oracle 은 현재 다운로드 가능합니다.(다운로드 (영문))

System.Data.OracleClient 네임스페이스를 사용합니다.

Oracle 데이터 원본(8.1.7 이후 버전)에 접근할 수 있도록 하는 기능을 제공합니다.

알림 .NET Data Provider for Oracle 은 .NET Framework의 1.1버전에 포함될 예정입니다.

사용자정의 .NET Data Provider ADO.NET 은 필요에 따라 직접 .NET Framework Data Provider를 구현할 수 있도록, 필요한 최소한의 인터페이스를 제공합니다. 사용자정의 Data Provider를 생성하기 위한 자세한 정보는 .NET Framework SDK에 포함되어 있는 .NET Framework Data Provider 구현 (영문) 문서를 참조하십시오.
SQLXML관리 클래스 XML for Microsoft SQL Server 2000 (SQLXML 3.0)에는 Microsoft SQL Server 2000 이후 버전의 XML 지원기능에 접근할 수 있도록 하기 위한 SQLXML 관리 클래스가 포함되어 있습니다. 예를 들어, SQLXML 관리 클래스를 사용하면 XML 템플릿을 실행하거나, XPath 질의를 사용하여 서버 데이터를 질의할 수 있으며, Updategram이나 Diffgram을 사용하여 데이터를 변경할 수 있습니다.

SQLXML 1.0이나 SQLXML 2.0의 기능에 추가하여, SQLXML3.0에서는 SQL Server 2000에 대한 웹 서비스 기능을 제공합니다. SQLXML 3.0을 사용하면, 저장프로시저나 XML 템플릿을 SOAP 프로토콜을 사용하여 웹 서비스로 생성할 수 있습니다.

SQLXML 3.0 은 현재 다운로드 가능합니다.(다운로드 (영문))

SQL Server 7.0 이후 버전에 연결하기

Microsoft SQL Server 7.0 및 이후 버전에 연결할 때 최적의 성능을 발휘하도록 하기 위해서는 SQL Server .NET Data Provider를 사용해야 합니다. SQL Server .NET Data Provider는 다른 기술 계층 없이 곧바로 SQL Server에 접근할 수 있도록 설계되었습니다. 그림 1에는 SQL Server 7.0 및 이후 버전에 연결할 때 사용할 수 있는 다양한 기술을 비교한 도표가 나타나 있습니다.

그림 1. SQL Server 7.0 이후 버전에 접근하기 위한 연결방법

ODBC 데이터원본 연결

ODBC .NET Data Provider는 Microsoft.Data.Odbc ODBC .NET Data Provider는 Microsoft.Data.Odbc 네임스페이스를 사용하며, .NET data providers for SQL Server 나 OLE DB와 동일한 기술구조를 가지고 있습니다. ODBC .NET Data Provider (다운로드 (영문))는 "ODBC" 접두사를 명명규칙(예를 들어, OdbcConnection)으로 하고, 표준 ODBC 연결문자열을 사용합니다.

알림 ODBC .NET Data Provider 는 .NET Framework 1.1 버전에 포함될 예정입니다. ODBC .NET Data Provider 는 System.Data.Odbc 네임스페이스를 사용합니다.

DataReader, DataSet, DataAdapter, DataView 사용

ADO.NET은 관계형 데이터를 조회하고 메모리상에 저장하기 위해 DataSet 개체와 DataReader개체를 제공합니다.DataSet개체는 메모리 상에서 테이블간의 관계뿐만 아니라 데이터에 대한 제약조건, 정렬을 지원하는 완전한 관계형 데이터 저장소 기능을 제공합니다. DataReader 개체는 데이터베이스로부터 데이터를 빠르고, 앞으로만 이동가능한, 읽기전용 데이터 스트림으로 조회하는 기능을 제공합니다.

DataSet 체를 사용할 때는, 데이터 원본과 상호작용하기 위해 주로 DataAdapter 개체(CommandBuilder 개체 포함)를 사용합니다. 또한, DataSet 개체내부에서 데이터를 정렬하거나 필터링하기 위해서 DataView 개체를 사용합니다. DataSet 개체는 강력하게 형식화된(strongly typed) DataSet을 생성하기 위해 상속될 수 있으며, 강력하게 형식화된 DataSet 에는 테이블, 행, 열 등이 강력하게 형식화된 개체 속성으로 표현됩니다.

이번 기사에서는 언제 DataSet 개체와 DataReader 개체를 사용해야 하는지에 대한 정보와 DataSet 개체와 DataReader 개체에 포함되어 있는 데이터에 대한 접근방법을 최적화하는 방법, DataAdapter(CommandBuilder 개체 포함) 개체와 DataView개체를 최적화하여 사용하는 방법에 대한 팁을 제공합니다.

DataSet 개체 vs. DataReader 개체

응용프로그램을 설계할 때 DataSet 개체를 사용할 것인지, DataReader 개체를 사용할 것인지를 결정하기 위해서는, 반드시 응용프로그램에서 요구하는 기능을 검토해야 합니다.

다음과 같은 응용프로그램 요구사항이 있는 경우에는, DataSet 개체를 사용해야 합니다.

  • 여러 개의 결과 테이블간의 검색이 필요한 경우
  • 여러 개의 데이터 원본으로부터 데이터를 조작해야 하는 경우 (예를 들어, 하나 이상의 데이터베이스로부터 데이터를 조회해야 하거나, XML 파일, 스프레드 시트에서 데이터를 통합하여 조작해야 하는 경우)
  • XML Web Service나 응용프로그램 계층간에 데이터를 교환해야 하는 경우 DataReader 개체와는 달리, DataSet 개체는 원격 클라이언트로 전달이 가능합니다.
  • 데이터에 대한 정렬, 검색, 필터링 작업의 성능향상을 위해, 행 집합을 캐싱한 다음, 재사용해야 하는 경우
  • 대량의 행 단위 처리가 필요한 경우. DataReader 개체를 사용하여 각 행 단위로 결과집합을 반환하게 되면, DataReader 개체에서 필요이상으로 연결을 유지해야 하기 때문에, 성능상에 문제가 될 수 있습니다.
  • XSLT 변환이나 XPath 질의와 같은 XML 작업을 사용하여 데이터를 조작해야 하는 경우

다음과 같은 응용프로그램 요구사항이 있는 경우에는, DataReader 개체를 사용해야 합니다.

  • 데이터를 캐시에 저장할 필요가 없는 경우
  • 결과집합이 너무 커서 메모리상에 저장할 수 없는 경우
  • 데이터에 앞으로만 이동가능하고, 읽기전용인 상태로 빠르게 접근해야 하는 경우
알림 DataAdapter 개체는 DataSet 개체에 데이터를 입력하기 위해 DataReader 개체를 사용합니다. 그러므로, DataSet 개체 대신에 DataReader 개체를 사용하게 되면, 성능상으로 DataSet 개체에서 사용하는 메모리 공간을 절약할 수 있으며, DataSet 개체에 데이터를 입력하는 과정에서 소요되는 자원을 절약할 수 있습니다. 응용프로그램에 요구사항에 따라 반드시 사용해야 하는 경우에만 DataSet 개체를 사용하고, 그 외의 경우, DataReader 개체를 사용하게 되면, 그만큼 성능상에 효과를 얻을 수 있습니다.

강력하게 형식화된 DataSet 개체를 사용하는 효과

DataSet 개체를 사용함으로써 얻을 수 있는 효과 중 하나는 DataSet 개체를 상속하여 강력하게 형식화된 DataSet 개체를 생성할 수 있다는 것입니다. 강력하게 형식화된 DataSet 개체를 사용하면, 설계 시점의 데이터형 점검기능과 Microsoft Visual Studio .NET 의 문장 자동완성 기능을 활용할 수 있습니다. DataSet 개체를 고정된 스키마 또는 관계형 구조로 사용하는 경우, 컬렉션 개체의 항목이 아니라, 행과 열 속성을 제공하는 강력하게 형식화된 DataSet 개체를 생성할 수 있습니다. 예를 들어,Customer 테이블에 포함된 특정 행의 Name 열에 접근하는 대신, Customer 개체의 Name 속성으로 접근할 수 있습니다. 형식화된 DataSet 개체는 DataSet 클래스에서 파생되었기 때문에, DataSet 클래스의 기능을 모두 사용할 수 있습니다. 즉, 형식화된 DataSet 개체는 원격에서 접근이 가능하며, DataGrid와 같은 데이터 바인딩 컨트롤의 데이터 원본으로 사용할 수 있습니다. 스키마가 형식화되어 있지 않은 경우에도, 순수 DataSet 개체의 기능을 모두 사용할 수 있지만, 강력하게 형식화된 DataSet 개체에서 제공해 주는 추가기능은 사용할 수 없습니다.

강력하게 형식화된 DataSet 내에서 Null값 처리

강력하게 형식화된 DataSet을 사용하면, null 값을 처리하기 위해 XML 스키마 정의 언어(XSD) 구문을 사용할 수 있습니다. nullValue 주석(annotation)을 사용하여 DBNull을 특정한 값이나 String.Empty으로 변환할 수도 있고, null값에 대한 참조로 유지하거나 예외를 발생시킬 수도 있습니다. 어떤 옵션을 선택할 것인지는 응용프로그램 컨텍스트에 따라 결정됩니다. 기본값으로는, null 값에 대한 참조가 발생하게 되면, 예외를 발생시키게 됩니다.

좀 더 자세한 정보는, 형식화된 DataSet사용 (영문) 기사를 참조하십시오.

DataSet 개체내의 데이터 갱신

서버로부터 변경된 값을 DataSet 개체에 반영하기 위해서는, DataAdapter.Fill 메서드를 사용합니다. DataTable 개체에 기본 키가 설정된 경우, DataAdapter.Fill 메서드는 해당 기본 키를 기준으로 변경된 행을 매핑하고, 기존 행에 변경된 서버 값을 적용시키게 됩니다. 새로운 데이터로 갱신된 행의 RowState 속성은 데이터 갱신작업이전에 변경된 행이라고 할지라도, Unchanged로 설정됩니다. DataTable 개체에 기본 키가 설정되어 있지 않은 경우에는, DataAdapter.Fill 메서드는 잠재적으로 중복된 기본 키 값을 사용하여 새로운 행을 추가합니다.

현재 테이블 내에 존재하는 행의 변경된 값을 유지한 상태로, 해당 테이블의 데이터를 갱신하고자 한다면, DataAdapter.Fill 메서드를 사용하여 새로운 DataTable 개체에 데이터를 입력한 다음, Merge 메서드의 perserveChanges 인수를 true로 설정한 상태에서, 해당 DataTable 개체를 DataSet 개체에 병합하면 됩니다.

DataSet 개체 내에서 데이터 검색

DataSet 개체에서 특정한 검색조건에 일치하는 행을 조회하고자 질의하는 경우, 인덱스 기반 검색을 사용하면, 검색 성능을 개선할 수 있습니다. DataTable 개체의 PrimaryKey 속성값을 설정하게 되면, 인덱스를 생성할 수 있습니다. DataTable 개체에 대해서 DataView를 생성하는 경우에도, 인덱스가 생성됩니다. 인덱스 기반 검색을 사용할 수 있는 몇 가지 팁이 아래에 소개되어 있습니다.

  • DataTable 개체의 PrimaryKey 속성으로 지정된 열에 대한 질의를 수행하는 경우, DataTable.Select 메서드 대신 DataTable.Rows.Find 메서드를 사용합니다.
  • 기본 키 열이 아닌 열에 대한 질의를 수행하는 경우, DataView 개체를 사용하면 데이터에 대한 다양한 질의의 성능을 향상시킬 수 있습니다. DataView를 정렬하게 되면, 검색을 할 때 사용할 수 있는 인덱스가 생성됩니다. 기존 DataTable 개체에 대해 질의를 수행하기 위해 사용하는, Find 메서드와 FindRows 메서드를 DataView 개체도 동일하게 제공합니다.
  • 테이블을 정렬할 필요가 없는 경우라도, DataTable 개체에 대해 DataView를 생성하게 되면, 인덱스 기반 검색의 장점을 활용할 수 있습니다. 데이터에 대해 다수의 질의를 수행하는 경우에는, 인덱스 기반 검색의 장점을 활용할 수 있습니다. 만약, 단일 질의만 수행해야 하는 경우라면, 오히려 인덱스를 생성하기 위해 필요한 처리과정이 인덱스를 사용함으로써 얻을 수 있는 성능상의 장점을 상쇄시키게 됩니다.

DataView 구성

DataView가 생성되거나, Sort, RowFilter, RowStateFilter 속성이 수정된 경우에는, 기존 DataTable의 데이터에 대한 인덱스를 생성합니다. DataView 개체를 생성하는 경우, 기존 DataTable에 설정된 Sort, RowFilter, RowStateFilter 값을 생성자 인수로 전달받게 됩니다. 그러므로, 설정된 각 속성값에 따라 한번에 인덱스가 생성될 수 있습니다. 비어있는 DataView를 생성하거나, Sort, RowFilter, RowStateFilter 속성을 사후에 설정하게 되면 적어도 두 번 이상 인덱스 생성 작업이 수행됩니다.

페이징

ADO.NET은 데이터 원본으로부터 어떤 데이터를 가져올 것인지를 명시적으로 통제할 수 있으며, DataSet 개체 내에 캐시할 데이터의 양을 지정할 수 있는 기능을 제공합니다. 질의의 결과에 대한 페이징을 처리하기 위한 명시적인 해답은 없지만, 응용프로그램의 설계단계에서 페이징을 처리하기 위해 고려할 수 있는 몇 가지 팁을 소개합니다.

  • startRecord 인수와, maxRecord 인수를 입력받는, 오버로드된 DataAdapter.Fill 메서드를 사용하지 않아야 합니다. 해당 오버로드된 DataAdapter.Fill 메서드를 사용하게 되면, DataSet에는 startRecord 인수로 지정된 레코드로부터 시작하여 maxRecords 인수로 지정된 레코드의 수만큼 데이터를 입력하게 되지만, 불필요한 전체 질의 결과가 모두 반환되게 됩니다. 결국, 요청되지 않는 레코드를 읽기 위해 불필요한 처리과정이 필요하게 되며, 추가적인 레코드를 반환하기 위해 서버 자원이 필요이상으로 사용됩니다.
  • 한 번에 한 페이지에 필요한 데이터만 가져오기 위해서, TOP 지시자와 함께 WHERE 절과 ORDER BY 절을 조합하여 SQL 문장을 생성하게 됩니다. 이러한 기술을 사용하기 위해서는 우선 각 행에 대한 식별자가 존재하느냐가 매우 중요한 요소가 됩니다. 다음 페이지를 위한 레코드를 검색하기 위해, WHERE 절을 현재 페이지의 맨 마지막 레코드보다 큰 레코드만을 조회할 수 있도록 수정해야 합니다. 각 질의를 수행할 때는 TOP 구문을 사용하여 다음 페이지에 필요한 레코드만을 가져오게 됩니다. 이전 페이지를 조회하기 위해서는, 결과집합을 역순으로 정렬하여 데이터를 반환해야 합니다. 역순으로 정렬한 결과의 TOP을 반환하는 방법을 사용하면, 질의의 맨 마지막 페이지를 반환할 수 있으며, 필요한 경우, 역순으로 정렬된 데이터를 표시하기 전에 다시 정렬을 해서 표시되도록 해야 합니다. 좀 더 자세한 정보는 질의 결과를 통한 페이징 구현 (영문) 기사를 참조하십시오.
  • 또 다른 방법으로 한 페이지에 해당하는 레코드만을 반환하기 위해 하위 SELECT 구문과 TOP 지시자를 조합하여 SQL 문장을 만드는 방법을 사용할 수 있습니다. 하위 SELECT 구문과 TOP 지시자를 조합하여 사용하는 방법은 각 행을 식별하기 위한 식별자가 없는 경우에도 사용할 수 있습니다. 먼저 필요로 하는 페이지 수에 페이지 당 행수를 곱하여 반환해야 하는 레코드 수를 계산해야 합니다. 그 다음, 앞 단계에 계산한 레코드 수를 TOP 지시자로 지정한 SQL 질의를 오름차순으로 정렬된 순서로 생성합니다. 다음 단계로, 앞에서 작성한 질의를 하위 질의로 포함하는 SELECT 질의를 하나 더 생성하여, 한 페이지에 필요한 레코드만 반환할 수 있도록 TOP 지시자를 사용하며, 외부 SELECT 질의의 경우는 역순으로 정렬하여 반환될 수 있도록 합니다. 결과적으로, 하위 SELECT 질의에서 반환하는 맨 마지막 페이지를 반환합니다. 예를 들어, 페이지 당 레코드 수가 10 건이고, 세 번째 페이지를 반환하기 위해서는, 다음과 같은 질의를 사용하면 됩니다. SELECT TOP 10 * FROM (SELECT TOP 30 * FROM Customers ORDER BY Id ASC) AS Table1 ORDER BY Id DESC

    질의의 결과가 역순으로 정렬된 상태로 반환된다는 것에 유념할 필요가 있습니다. 필요하다면, 다시 오름차순으로 정렬될 수 있도록 다시 정렬을 해야 합니다.

  • 데이터가 자주 변경되지 않는다면, DataSet 개체내에 로컬로 레코드를 캐시하면 성능을 향상시킬 수 있습니다. 예를 들어, 사용자가 첫 번째 페이지부터 마지막 페이지까지 모두 캐시된 데이터에서 검색할 수 있도록, 필요한 경우, 데이터 원본에 대한 한 번의 질의를 통해, 로컬 DataSet 개체 내에 10 개의 페이지를 저장할 수도 있습니다. 좀 더 자세한 정보는 .NET 데이터 액세스 아키텍쳐 가이드 (영문) 기사를 참조하십시오.

    스키마가 적용된 DataSet 개체에 데이터 채우기

    DataAdapter.Fill 메서드를 사용하여, DataSet 개체의 설정된 스키마를 기준으로 SelectCommand 명령의 수행결과로 반환된 데이터를 DataSet 개체내에 입력합니다. 데이터 채우기의 대상이 되는 테이블 명칭이 지정되지 않으면, Fill 메서드가 새로운 테이블을 생성하게 됩니다. 기본값으로, Fill 메서드는 열과 열의 데이터형만을 지정하게 됩니다.

    DataAdapter 개체의 MissingSchemaAction 속성을 설정하면, Fill 메서드의 기본 동작을 재정의(override)할 수 있습니다. 예를 들어, 기본 키 정보, unique 제약조건, null 허용여부, 열의 최대 길이, 읽기전용 열, 자동증가 열과 같은 열 속성이 포함된 테이블 스키마를 생성하도록 하기 위해서, DataAdapter.MissingSchemaAction 속성값을 MissingSchemaAction.AddWithKey로 지정할 수 있습니다. 또한, DataAdapter.Fill 메서드를 호출하기 전에 DataAdapter.FillSchema 메서드를 호출하여, DataSet에 데이터를 채우기 작업을 수행하기 전에 제대로 구성된 스키마에 데이터가 매핑될 수 있도록 보증해 줄 수 있습니다.

    FillSchema 메서드를 호출하게 되면, 추가적인 스키마 정보를 조회하기 위해서 서버로 별도의 라운드트립이 발생하게 됩니다. 최대한 성능을 보장해 주기 위해서는, DataSet에 스키마를 정의하거나, Fill 메서드를 호출하기 전에 DataAdapter개체의 MissingSchemaAction 속성을 설정해 주는 것이 바람직합니다.

    CommandBuilder 개체 사용 표준사례

    CommandBuilder 개체는, 단일 테이블에 대한 SELECT 기능을 수행하는, DataAdapter 개체의 SelectCommand 속성을 기반으로 하여, 자동으로 InsertCommand, UpdateCommand, DeleteCommand 속성을 생성해 줍니다. CommandBuilder 개체가 최적의 성능을 발휘할 수 있도록 하기 위한 몇 가지 팁을 소개합니다.

    • CommandBuilder 개체는 디자인 시점이나 임의 시나리오를 위해서만 제한적으로 사용해야 합니다. DataAdapter 개체에 각 Command 속성을 생성하기 위한 처리작업은 성능에 악영향을 미칠 가능성이 있습니다. 사전에 사용할 INSERT/UPDATE/DELETE 문장을 알고 있는 경우라면, 명시적으로 설정하는 것이 바람직합니다. 올바른 설계방법은 INSERT/UPDATE/DELETE 작업을 수행하는 저장 프로시저를 생성한 다음, 각 저장 프로시저를 DataAdapter 개체의 각 Command 속성에 명시적으로 지정하는 것입니다.
    • CommandBuilder 개체는 다른 Command 속성값을 지정하기 위해서 DataAdapter의 SelectCommand 속성을 사용합니다. DataAdapter 개체의 SelectCommand 속성을 변경하고자 하는 경우에는, 모든 관련 속성에 변경내역을 반영하기 위해 RefreshSchema 메서드를 호출해야 합니다.
    • DataAdapter 개체의 Command 속성이 null값(Command 속성은 Null값이 기본값으로 설정)으로 설정되어 있는 경우에는, CommandBuilder 개체가 각 Command 속성을 생성하게 됩니다. Command 속성이 명시적으로 지정되게 되면, CommandBuilder 개체는 그 값을 덮어쓰지 않습니다. CommandBuilder 개체가 이미 값이 지정된 Command 속성에 명령을 생성하도록 하기 위해서는, 먼저 해당 Command 속성을 Null로 설정해 주어야 합니다.

    SQL 배치 문장 처리

    대부분의 데이터베이스에서는 여러 개의 명령을 조합하여 하나의 배치작업으로 실행하는 기능을 제공합니다. 예를 들어, SQL 서버에서는 세미콜론(;)을 사용하여 명령을 구분할 수 있습니다. 여러 개의 명령을 하나의 배치작업으로 조합하여 실행하게 되면, 서버로의 라운드트립을 줄여 줄 수 있기 때문에, 응용프로그램의 성능을 향상시킬 수 있습니다. 예를 들어, 응용프로그램에서 발생하는 삭제요청을 하나로 조합한 다음, 한꺼번에 데이터원본에 해당 행을 삭제하도록 배치작업을 실행할 수 있습니다.

    여러 개의 명령을 하나의 배치로 실행하는 방법을 사용하면, 성능을 향상시킬 수 있지만, DataSet 개체에서 데이터에 대한 변경내역을 관리할 때, 응용프로그램의 복잡성이 증가하게 됩니다. 응용프로그램의 단순성을 유지하기 위해서는, DataSet 내부에 포함된 DataTable별로 DataAdapter 개체를 생성할 수 있습니다.

    DataSet 개체에 여러 개의 테이블 데이터 채우기

    여러 개의 테이블을 조회하는 SQL 배치 문장을 사용하는 경우, Fill 메서드에서 테이블 명칭을 지정하게 되면, 첫 번째 결과집합에 대해서는 지정한 테이블 명칭이 부여하게 됩니다. 그 다음, 나머지 결과집합을 저장하기 위한 테이블은, Fill 메서드에서 지정된 테이블 명칭 뒤에 숫자를 하나씩 증가시켜 가면서 명칭을 부여하게 됩니다. 예를 들어, 다음과 같은 코드를 실행해 볼 수 있습니다.

    'Visual Basic Dim da As SqlDataAdapter = New SqlDataAdapter("SELECT * FROM Customers; SELECT * FROM Orders;", myConnection) Dim ds As DataSet = New DataSet() da.Fill(ds, "Customers") //C# SqlDataAdapter da = new SqlDataAdapter("SELECT * FROM Customers; SELECT * FROM Orders;", myConnection); DataSet ds = new DataSet(); da.Fill(ds, "Customers");

    Customers 테이블에서 조회한 결과는 "Customers" 테이블에 입력됩니다. 그 다음, Orders 테이블에서 조회한 결과는 "Customers1"이라는 명칭이 부여된 DataTable 개체로 입력됩니다.

    DataSet 개체에 데이터가 채워진 다음, 해당 DataTable 개체의 TableName 속성을 "Customers1"에서 "Orders"로 쉽게 변경할 수 있습니다. 하지만, "Customers" 테이블에 대해 데이터 채우기 작업을 다시 수행하게 되면, 수정된 "Orders" 테이블은 무시되고, 또 다른 "Customers1" 테이블이 다시 생성됩니다. 이러한 문제점을 해결하기 위해서는, "Customers1" 테이블을 "Orders" 테이블로 매핑하기 위해서 DataTableMapping 개체를 생성해야 합니다. 예를 들면, 아래의 코드와 같이 DataTableMapping 개체를 생성할 수 있습니다.

    'Visual Basic Dim da As SqlDataAdapter = New SqlDataAdapter("SELECT * FROM Customers; SELECT * FROM Orders;", myConnection) da.TableMappings.Add("Customers1", "Orders") Dim ds As DataSet = New DataSet() da.Fill(ds, "Customers") //C# SqlDataAdapter da = new SqlDataAdapter("SELECT * FROM Customers; SELECT * FROM Orders;", myConnection); da.TableMappings.Add("Customers1", "Orders"); DataSet ds = new DataSet(); da.Fill(ds, "Customers");

    DataReader 개체 사용

    일반적인 DataReader 개체의 사용에 관련된 질문사항에 대한 답변과 최적의 성능을 보장하기 위한 몇 가지 팁을 소개합니다.

    • DataReader 는 연결된 Command 속성에 설정된 출력파라미터에 접근하기 전에 반드시 종료(close)되어야 합니다.
    • 데이터에 대한 읽기 작업이 종료되는 시점에 DataReader 개체는 항상 종료되어야 합니다. Connection 개체가 DataReader 개체에서 반환하는 데이터만을 위해 사용된다면, DataReader 개체를 종료한 다음, 해당 Connection 개체도 바로 종료해야 합니다. Connection 개체를 명시적으로 종료하는 방법의 대안으로는, ExecuteReader 메서드에 CommandBehavior.CloseConnection 인수 값을 전달하여, DataReader 개체가 종료되는 시점에서 관련된 Connection 개체를 종료할 수 있도록 설정할 수 있습니다. 이러한 방법은 ExecuteReader 메서드로부터 DataReader 개체가 반환된 다음, DataReader 개체나 관련된 Connection 개체를 종료시키기 위한 별도 절차가 필요하지 않기 때문에, 특히 유용하게 사용됩니다.
    • DataReader 개체는 응용프로그램 계층간에 원격으로 사용될 수 없습니다. DataReader 개체는 반드시 연결된 데이터 액세스 모델로 설계되어야 합니다.
    • 열의 데이터에 접근할 때, GetString, GetInt32 등과 같은 형식화된 접근자(accessor)를 사용할 수 있습니다. 이러한 형식화된 접근자를 사용하게 되면, GetValue 메서드로부터 반환된 개체를 특정한 데이터형으로 형변환하기 위한 추가적인 처리절차가 필요하지 않습니다.
    • 하나의 Connection개체를 사용하여, 한 번에 하나의 DataReader 개체만 열 수 있습니다. ADO에서는 하나의 Connection 개체를 연 다음, 전진지향, 읽기 전용 커서를 사용하여, 두 개의 레코드집합을 요청하는 경우, 암시적으로 제 2의 풀링되지 않은 연결을 생성하여, 커서를 관리하기 위한 정보를 저장하고, 처리가 완료되면 암시적으로 종료시킵니다. ADO.NET에서는 예전에 사용하여왔던 관행에서 벗어나야만 합니다. 동일한 데이터 저장소를 대상으로, 동시에 두 개의 DataReader 개체를 열어야 한다면, 명시적으로 두 개의 Connection 개체를 연 다음, 각 연결을 사용하여 DataReader 개체를 하나씩 열어야 합니다. 이러한 방법을 사용하여, ADO.NET에서는 풀링된 연결에 대한 사용을 통제할 수 있습니다.
    • 기본값으로, DataReader 개체는 읽기작업의 대상이 되는 전체 행 정보를 메모리로 업로드합니다. 그렇기 때문에, 현재 행의 각 열에 대해서는 임의 접근이 가능합니다. 만약, 열에 대한 임의 접근이 필요없는 경우라면, 성능향상을 위해 ExecuteReader 메서드를 호출할 때, CommandBehavior.SequentialAccess 속성을 지정하여 전달할 수 있습니다. CommandBehavior.SequentialAccess 속성이 지정되면, DataReader 개체의 기본동작방법이 변경되어 요청이 발생한 데이터만 메모리로 업로드하게 됩니다. CommandBehavior.SequentialAccess 속성이 지정되면, 반환되는 각 열의 순서에 따라 데이터에 접근하게 됩니다. 즉, 한 번 순서에 따라 반환된 열에 대한 읽기 작업을 수행하고 나면, 다시 그 값을 읽을 수는 없게 됩니다.
    • DataReader 개체로부터 데이터 읽기작업을 완료한 다음에도, DataReader 개체 내부에 읽지 않은 데이터가 계속 존재하는 경우라면, Close 메서드를 호출하기 전에 Command 개체의 Cancel 메서드를 호출해야 합니다. 먼저, DataReader 개체의 Close 메서드를 호출되면, DataReader 내에 읽지 않은 상태로 대기 중인 결과값을 조회하는 원인이 되고, 커서를 종료하는 작업에 앞서, 이러한 스트림 조회작업을 종료하는 추가작업이 발생하게 되는 원인이 됩니다. 먼저, Command 개체의 Cancel 메서드를 호출하게 되면, DataReader개체의 Close 메서드를 호출할 때, DataReader 내부에 존재하는 읽지 않은 데이터에 대한 조회작업을 수행하지 않도록 하기 위해, 서버상에 존재하는 결과값을 모두 폐기하게 됩니다. 만약, Command 개체가 출력 파라미터를 반환하는 경우, Cancel 메서드를 호출하게 되면 출력파라미터에 할당된 값도 모두 폐기됩니다. 그러므로, 출력 파라미터를 읽어야 하는 경우라면, Command 개체의 Cancel 메서드를 호출하지 말고, DataReader 개체의 Close 메서드만 호출해야 합니다.

    BLOB 데이터 처리

    DataReader 개체를 사용하여 BLOB 데이터를 조회하는 경우, ExecuteReader 메서드를 호출할 때, CommandBehavior.SequentialAccess 속성을 설정해야 합니다. DataReader 개체는 기본적으로 모든 읽기 작업의 대상이 되는 전체 행을 메모리로 업로드하게 되며, BLOB 데이터의 경우, 매우 대용량일 수도 있기 때문에, 단일 BLOB 데이터가 메모리의 상당한 부분을 소모해 버릴 수도 있습니다. SequentialAccess 속성이 설정되게 되면, DataReader 개체는 요청된 데이터만을 메모리에 업로드합니다. 그 다음, GetBytes 메서드나 GetChars 메서드를 사용하여, 한 번에 메모리로 업로드하는 데이터의 양을 통제할 수 있습니다.

    SequentialAccess 속성을 지정하게 되면, DataReader에 지정된 순서를 벗어나서 다른 필드에 접근할 수 없다는 사실을 기억해 두어야 합니다. 즉, 질의결과에 3 개의 열이 포함되어 있고, 첫 번째, 두 번째 열은 일반 데이터이고, 세 번째 열이 BLOB 데이터인 경우, 순서대로 첫 번째 열 값을 읽은 다음, BLOB 열 데이터 값을 읽기 전에 두 번째 열 값을 반드시 읽어야 합니다. 데이터가 순서대로 반환되기 때문에, 한 번 DataReader 개체를 통해 읽기 작업이 수행된 열에 대해서는 다시 읽기 작업을 수행할 수 없게 됩니다.

    ADO.NET을 사용하여 BLOB 데이터를 처리하는 작업에 대한 자세한 내용은 데이터베이스에서 BLOB 데이터 가져오기 (영문) 기사를 참조하십시오.

    Commands 개체 사용

    ADO.NET 에서는 명령을 실행하기 위한 메서드를 제공할 뿐만 아니라, 실행되는 명령을 최적화하기 위한 옵션도 제공합니다. 다음에서는 명령을 실행하는 표준 방법과 실행된 명령의 성능을 최적화하기 위한 몇 가지 팁을 소개합니다.

    OleDbCommand 표준사례

    여러 .NET Framework data provider 간에 명령을 실행하는 방법을 가능한 한 표준화해서 사용해야 합니다. 하지만, 각 데이터 공급자간에 차이점이 존재합니다. 다음에서는 .NET Framework Data Provider for OLE DB를 사용하여 명령을 실행할 때, 최적의 성능을 보장하기 위한 몇 가지 팁을 소개합니다.

    • 저장프로시저를 호출하기 위해서는 ODBC CALL 구문과 함께 CommandType.Text 속성을 사용합니다. CommandType.StoredProcedure 속성을 지정하면, 암시적으로 ODBC CALL 구문을 생성하게 됩니다.
    • 반드시 OleDbParameter 개체의 데이터형, 크기, 정확도 및 배율(numeric,decimal 데이터형인 경우) 등을 지정해야 합니다. 파라미터에 대한 정보를 명시적으로 설정하지 않으면, OleDbCommand 개체에서 명령이 실행될 때마다, 해당 OLE DB 파라미터 접근자(accessor)를 다시 생성하게 됩니다.

    SqlCommand 표준사례

    SqlCommand 개체를 사용하여 저장 프로시저를 실행하는 경우에 적용할 수 있는 몇 가지 팁을 소개합니다. 저장 프로시저를 호출한다면, SqlCommand 개체의 CommandType 속성을 StoredProcedure으로 지정해야 합니다. CommandType 속성을 StoredProcedure으로 지정하면, 실행하는 명령이 명시적으로 저장 프로시저라는 것을 알 수 있기 때문에, 명령을 실행하기 전에 파싱하는 절차가 생략됩니다.

    Prepare 메서드 사용

    Command.Prepare 메서드를 사용하면, 데이터 원본으로부터 파라미터화된 명령을 반복적으로 수행하는 경우 성능을 향상시킬 수 있습니다. Prepare 메서드를 효과적으로 사용하기 위해서는, Prepare 메서드가 호출되었을 때, 데이터 원본에서 어떠한 작업이 수행되는지 명확하게 알고 있어야 합니다. SQL 서버 2000을 데이터 원본으로 사용한 경우에는, 명령이 호출될 때 암시적으로 최적화 작업이 수행되기 때문에, Prepare 메서드를 호출할 필요가 없습니다. SQL 서버 7.0를 데이터원본을 사용하는 경우에만, Prepare 메서드가 성능상 도움을 줄 수 있습니다.

    명시적으로 스키마와 메타데이터 지정

    ADO.NET 에서는 사용자가 명시적으로 메타데이터 정보를 지정하지 않으면, 자동으로 많은 개체를 추론하여 생성하게 됩니다. 예를 들면,

    • DataAdapter.Fill 메서드는 비어있는 DataSet 개체에 테이블과 열을 생성합니다.
    • CommandBuilder 개체는 단일 테이블에 대한 SELECT 문을 처리하기 위한 DataAdapter 개체의 명령 속성을 생성합니다.
    • CommandBuilder.DeriveParameters 는 Command 개체의 Parameters 컬렉션에 데이터를 입력합니다.

    하지만, 이러한 자동 개체 생성작업이 수행될 때마다 성능에 악영향을 미치게 됩니다. 그러므로, 설계시점과 임의 질의를 수행하는 응용프로그램에서만 제한적으로 자동개체생성 기능을 사용해야 합니다. 가능하다면, 스키마 및 메타데이터 정보는 명시적으로 선언되어야 합니다. DataSet 개체에 테이블과 열을 지정하고, DataAdapter 개체에 Command 속성을 지정하고, Command 개체에 Parameter 정보를 지정하는 작업이 모두 명시적으로 수행되어야 합니다.

    ExecuteScalar 메서드와 ExecuteNonQuery 메서드

    Count(*), Sum(Price), Avg(Quantity)와 같은 함수의 결과값만을 반환해야 한다면, Command.ExecuteScalar 메서드를 사용해야 합니다. Command.ExecuteScalar 메서드는 결과집합의 첫 번째 행, 첫 번째 열만을 스칼라 값으로 반환합니다. DataReader 개체의 ExecuterReader 메서드를 호출하는 경우, ExecuterReader 메서드 실행과 반환된 결과값을 가져오는 두 단계작업을 수행하는 것에 비해서, ExecuteScalar 메서드를 사용하면, 하나의 단계로 처리할 수 있기 때문에, 코드를 매우 간단하게 작성할 수 있으며, 성능도 향상시킬 수 있습니다.

    데이터를 변경하기 위한 INSERT, UPDATE, DELETE 문장과 같이, 결과집합을 반환하지 않는 SQL 문장을 실행하는 경우나 출력 파라미터만 반환하는 질의를 실행하는 경우에는, ExecuteNonQuery 메서드를 사용해야 합니다. ExecuteNonQuery 메서드를 사용하면, 비어있는 DataReader 개체를 생성하기 위한 불필요한 처리절차가 발생하지 않습니다.

    좀 더 자세한 정보는 Command 개체 실행 (영문) 기사를 참조하십시오.

    Null 값 테스트

    null 값을 허용하는 열에 경우, null로 설정된 파라미터값과 동일(=)한지에 대한 비교가 불가능합니다. 대신에, WHERE절에 해당 열이 null인지를 검사하는 구문과 파라미터값이 null인지를 검사하는 구문을 사용해야 합니다. 다음 SQL 문장은 LastName 열과 @LastName 파라미터가 모두 null인 경우를 포함하여, @LastName 파라미터와 일치하는 LastName 열의 행을 반환합니다.

    SELECT * FROM Customers WHERE ((LastName = @LastName) OR (LastName IS NULL AND @LastName IS NULL))

    파라미터 값으로 Null 값 전달

    데이터베이스에 전달할 파라미터의 값을 null로 설정하기 위해서, null(Visual Basic .NET의 nothing)을 사용할 수 없습니다. 대신 DBNull.Value 를 사용해야 합니다. 예를 들면, 다음과 같이 사용해야 합니다.

    'Visual Basic Dim param As SqlParameter = New SqlParameter("@Name", SqlDbType.NVarChar, 20) param.Value = DBNull.Value //C# SqlParameter param = new SqlParameter("@Name", SqlDbType.NVarChar, 20); param.Value = DBNull.Value;

    트랜잭션 처리

    ADO.NET에서는 트랜잭션 모델이 변경되었습니다. ADO에서는 StartTransaction 메서드가 호출되면, 해당 호출 이후에 발생한 어떠한 변경사항도 트랜잭션의 일부로 동작하였습니다. 하지만, ADO.NET에서는 Connection.BeginTransaction 메서드가 호출되면, Command 개체의 Transaction 속성을 연결하기 위해 사용되는 Transaction 개체가 반환됩니다. 트랜잭션 모델 설계 변경을 통해, 하나의 연결에서 여러 개의 트랜잭션을 수행할 수 있게 되었습니다. Command.Transaction 속성이 관련 Connection 개체가 시작되었을 때 발생한 Transaction 개체로 설정되지 않으면, Command 개체는 실행이 실패하게 되고, 예외를 발생시키게 됩니다.

    .NET 프레임워크의 차기 릴리즈에서는 현재 존재하는 분산 트랜잭션을 수작업으로 목록화할 수 있는 기능을 제공할 예정입니다. 이는 풀링된 개체는 하나의 연결을 사용한다는 개체 풀링의 개념을 사용하는 것이지만, 하나의 개체는 여러 개의 개별 트랜잭션에 포함될 수 있습니다. 이러한 기능은 .NET 프레임워크 1.0에서는 지원되지 않습니다.

    트랜잭션에 대한 좀 더 자세한 정보는, 트랜잭션 처리 (영문) 기사나 .NET 데이터 액세스 아키텍쳐 가이드 (영문) 기사를 참조하십시오.

    Connection 개체 사용

    고성능 응용프로그램에서는 사용중인 데이터원본에 최소한의 시간만큼만 연결을 사용해야 하며, 연결 풀링과 같은 성능향상을 위한 기술을 적용해야 합니다. 다음에서는 ADO.NET을 사용하여 데이터원본에 접근하는 경우에, 성능을 극대화하기 위해서 고려해야 하는 몇 가지 팁을 소개합니다.

    연결 풀링

    SQL Server, OLE DB, .NET Framework Data Provider for ODBC 모두 암시적으로 연결풀링을 사용합니다. 연결문자열에서 적절한 속성을 지정함으로써 연결 풀링의 동작을 통제할 수 있습니다. 연결 풀링을 동작방법을 통제하기 위한 자세한 정보는 SQL Server .NET Data Provider에서의 연결풀링 사용 (영문) 기사와 OLE DB .NET Data Provider에서의 연결 풀링 사용 (영문)기사를 참조하십시오.

    DataAdapter 개체에서 연결 최적화

    DataAdapter 개체의 Fill 메서드나 Update 메서드의 경우, 연결이 종료된 경우라면, 자동적으로 관련 Command 속성에 지정된 연결을 엽니다. Fill 메서드나 Update 메서드가 연결을 여는 경우, Fill 메서드나 Update 메서드의 작업이 완료되게 되면, 열린 연결을 자동으로 종료시킵니다. 최적의 성능을 보장하기 위해서, 요청이 발생하는 경우에만 데이터베이스에 대한 연결을 열어야 합니다. 또한 여러 개의 작업을 수행하기 위해 연결이 반복적으로 열고 종료되는 횟수를 줄여주어야 합니다.

    Fill 메서드나 Update 메서드 호출이 한 번만 수행되는 경우에는, Fill 메서드나 Update 메서드가 암시적으로 연결을 열고 종료할 수 있도록 허용하는 것을 권장합니다. 하지만, Fill 메서드나 Update 메서드가 여러 번 호출되는 경우에는, 연결을 명시적으로 열고, 여러 번의 Fill 메서드나 Update 메서드를 호출한 다음, 해당 연결을 명시적으로 종료시키는 방법을 권장합니다.

    또한, 트랜잭션을 사용하는 경우, 트랜잭션이 시작할 때 연결을 명시적으로 연 다음, 커밋된 후에 해당 연결을 명시적으로 종료시키는 방법을 권장합니다. 예를 들어,

    'Visual Basic Public Sub RunSqlTransaction(da As SqlDataAdapter, myConnection As SqlConnection, ds As DataSet) myConnection.Open() Dim myTrans As SqlTransaction = myConnection.BeginTransaction() myCommand.Transaction = myTrans Try da.Update(ds) myTrans.Commit() Console.WriteLine("Update successful.") Catch e As Exception Try myTrans.Rollback() Catch ex As SqlException If Not myTrans.Connection Is Nothing Then Console.WriteLine("An exception of type " & ex.GetType().ToString() & _ " was encountered while attempting to roll back the transaction.") End If End Try Console.WriteLine("An exception of type " & e.GetType().ToString() & " was encountered.") Console.WriteLine("Update failed.") End Try myConnection.Close() End Sub //C# public void RunSqlTransaction(SqlDataAdapter da, SqlConnection myConnection, DataSet ds) { myConnection.Open(); SqlTransaction myTrans = myConnection.BeginTransaction(); myCommand.Transaction = myTrans; try { da.Update(ds); myCommand.Transaction.Commit(); Console.WriteLine("Update successful."); } catch(Exception e) { try { myTrans.Rollback(); } catch (SqlException ex) { if (myTrans.Connection != null) { Console.WriteLine("An exception of type " + ex.GetType() + " was encountered while attempting to roll back the transaction."); } } Console.WriteLine(e.ToString()); Console.WriteLine("Update failed."); } myConnection.Close(); }

    Connection 개체와 DataReader 개체 명시적 종료

    Connection 개체와 DataReader 개체는 사용이 종료되었을 때, 항상 명시적으로 종료시켜야 합니다. 가비지 수집이 발생하면 개체를 정리하고 관련된 연결 및 관련 자원을 해제하는 작업을 수행하지만, 이러한 가비지 수집절차는 필요한 경우에만 발생하게 됩니다. 그러므로, 각 자원에 대해 명시적으로 해제작업을 수행해야만 합니다. 또한 명시적으로 종료되지 않은 Connection 개체는 연결 풀로 반환되지 않을 수도 있습니다. 예를 들어, 더 이상 사용되지 않은 연결이 명시적으로 종료되지 않으면, 최대 풀 크기에 도달한 상태로 해당 연결이 여전히 유효한 경우에만, 연결 풀로 반환되게 됩니다.

    알림 클래스 Finalize 메서드에서 Connection, DataReader, 또는 기타 관리되는 개체에 대한 Close 메서드나 Dispose 메서드를 호출하지 마십시오. 종결자(Finalizer)에서는 해당 클래스에서 직접적으로 소유하고 있는 관리되지 않은 자원에 대해서만 자원해제 작업을 수행합니다. 클래스내부에 관리되지 않은 자원이 포함되어 있지 않다면, 클래스에 Finalize 메서드를 포함시키지 마십시오.

    C#에서 "Using" 구문 사용

    C# 프로그래머는, using 구문을 사용하여, Connection 개체나 DataReader 개체가 항상 명시적으로 종료될 수 있도록 설정할 수 있습니다. using 구문은 자동으로 using 구문의 범위를 벗어난 개체에 대해서 Dispose 메서드를 호출합니다. 예를 들어,

    //C# string connString = "Data Source=localhost;Integrated Security=SSPI;Initial Catalog=Northwind;"; using (SqlConnection conn = new SqlConnection(connString)) { SqlCommand cmd = conn.CreateCommand(); cmd.CommandText = "SELECT CustomerId, CompanyName FROM Customers"; conn.Open(); using (SqlDataReader dr = cmd.ExecuteReader()) { while (dr.Read()) Console.WriteLine("{0}\t{1}", dr.GetString(0), dr.GetString(1)); } }

    위의 구문은 Microsoft Visual Basic .NET 에서는 사용할 수 없습니다.

    OleDbConnection.State 속성 사용 제한

    연결이 되면, OleDbConnection.State 속성이 DBPROP_CONNECTIONSTATUS 속성에 대한 DATASOURCEINFO 속성을 설정하기 위해서 IDBProperties.GetProperties 를 호출하기 때문에, 데이터원본으로의 라운트트립이 발생하게 됩니다. 즉, State 속성을 체크하는 작업은 많은 비용을 발생시키게 됩니다. 그러므로 State 속성은 꼭 필요한 경우에만 체크해야 합니다. 만약, State 속성을 자주 확인해야 하는 경우라면, 응용프로그램의 성능을 보장하기 위해, OleDbConnection에 대한 StateChange 이벤트가 발생할 때, 적절한 작업을 수행하도록 설정하는 것이 바람직합니다. StateChange 이벤트에 대한 좀 더 자세한 정보는 연결 이벤트 처리 (영문) 기사를 참조하십시오.

    XML 통합

    ADO.NET 에서는 DataSet 내에서 XML 통합기능을 제공하고, SQL 서버 2000과 이후 버전에서 제공하는 XML 지원 기능의 일부를 제공합니다. SQLXML 3.0을 사용하면, SQL서버 2000과 그 이후 버전에서 제공하는 XML 데이터에 대한 개선된 접근방법을 사용할 수 있습니다. 다음에서는 ADO.NET을 사용하여 XML데이터를 처리하는 몇 가지 팁에 대해서 소개합니다.

    DataSet 개체와 XML

    DataSet 개체는 다음과 같은 기능을 통해 XML과의 통합기능을 지원합니다.

    • 스키마를 로드하거나, XSD 스키마로부터 DataSet 개체의 관계형 구조를 로드합니다.
    • XML로부터 DataSet의 항목을 로드합니다.
    • 스키마가 설정되지 않은 경우, XML 문서로부터 DataSet 스키마를 추론합니다.
    • DataSet 항목을 XML로 저장합니다.
    • DataSet 스키마를 XSD 스키마로 저장합니다.
    • DataSet을 사용하여 관계형 표현방식으로 데이터를 처리할 수도 있고, XmlDataDocument를 사용하여 계층형 표현방식으로 데이터를 처리할 수 있으며, 두 가지 방법에는 동기화된 접근방식이 가능합니다.
    알림 DataSet과 XmlDataDocument간에 동기화 접근방법이 가능하기 때문에, DataSet 개체 내에 포함된 데이터에 XPath 질의나 XSLT 변환과 같은 XML 기능을 적용할 수도 있고, 원본 XML을 유지한 상태에서 XML문서의 전체 또는 일부를 관계형으로 표현할 수 있는 기능을 제공합니다.

    DataSet 개체에서 제공하는 XML 지원기능에 대한 자세한 정보는 XML 과 DataSet (영문) 기사를 참조하십시오.

    스키마 추론

    XML 파일을 DataSet 개체로 로드하는 작업을 수행할 때, XSD 스키마로부터 DataSet의 스키마를 로드하거나 데이터를 로드하기 전에 테이블과 열에 대한 정보를 사전에 정의할 수 있습니다. XSD 스키마를 사용할 수 없고, XML 파일 항목에 대한 테이블과 열 정보를 파악할 수 없는 경우에는, XML 문서의 구조를 근거하여 DataSet의 스키마를 추론할 수 있습니다.

    스키마 추론기능은 마이그레이션 도구로서는 매우 유용하게 사용할 수 있지만, 추론 처리절차에 다음과 같은 몇 가지 제약사항이 있기 때문에 응용프로그램의 설계시점에서만 제한적으로 사용되어야 합니다.

    • XML 파일로부터 스키마 정보를 추론하는 추가적인 처리절차가 필요하기 때문에, 응용프로그램의 성능에 악영향을 미치게 됩니다.
    • 추론된 열의 데이터형은 모두 문자열(string)로 지정됩니다.
    • 추론 처리절차는 확정이지 않습니다. 즉, 의도된 스키마가 아니라, XML 파일의 항목의 구성을 근거로 하여 추론 처리절차를 수행하게 됩니다. 결국, 동일한 의도된 스키마를 근거로 하여 두 개의 XML 파일을 생성하였다면, XML 항목이 서로 다르기 때문에 전혀 다른 두 개의 추론된 스키마가 생성될 수 있습니다.

    좀 더 자세한 정보는, XML로부터 DataSet 관계형 구조 추론 (영문) 기사를 참조합니다.

    SQL Server FOR XML 질의

    SQL 서버 2000의 FOR XML 질의의 결과값을 반환하는 경우, .NET Framework Data Provider for SQL Server의 SqlCommand.ExecuteXmlReader 메서드를 사용하여 XmlReader 개체를 직접적으로 생성할 수 있습니다.

    SQLXML 관리되는 클래스

    .NET 프레임워크에는 SQL 서버 2000의 XML 지원 기능을 제공하는 클래스가 포함되어 있습니다. Microsoft.Data.SqlXml 네임스페이스에 포함되어 있는 클래스를 사용하면, XPath 질의를 실행하고, 데이터를 XSLT와 같은 특정한 형식으로 변환하기 위해, XML 템플릿 파일을 사용할 수 있습니다.

    SQLXML 관리되는 클래스는 XML for Microsoft SQL Server 2000 (SQLXML 2.0)에 포함되어 있으며, XML for Microsoft SQL Server 2000 Web Release 2 (SQLXML 2.0) 기사에서 다운로드 받을 수 있습니다.

    추가 유용한 팁

    다음에서는 ADO.NET 코드를 작성할 때 고려할 수 있는 몇 가지 일반적인 팁을 소개합니다.

    자동 증가 값 충돌 피하기

    대부분의 데이터원본과 같이, DataSet 개체도 새로운 행이 추가될 때마다 자동적으로 값을 증가시키는 식별자 열 기능을 제공합니다. 데이터원본에서 자동증가 열로 지정된 열과 함께, DataSet 개체에 자체에서 제공하는 자동증가 열 기능을 사용하는 경우, DataSet에 행이 추가될 때 발생하는 자동증가 값과 데이터원본에 행이 추가될 때 발생하는 자동증가 값 사이에 차이가 발생하지 않도록 주의해야 합니다.

    예를 들어, CustomerID가 자동증가 기본 키 열로 설정된 테이블의 경우를 살펴보겠습니다. 테이블에 고객정보가 2 건 입력되면, 자동증가 열인 CustomerID는 각각 1과 2로 설정됩니다. 그 다음, DataAdapter의 Update 메서드를 사용하여, 두번째 행만 데이터베이스에 입력하게 하면, 데이터베이스에 새로 추가된 행에는 자동증가 열인 CustomerID 열의 값이 1로 입력됩니다. DataSet에 설정되어 있는 2와는 서로 다른 값으로 설정되는 문제가 발생합니다. 다시, DataAdapter에서 데이터베이스에 있는 두 번째 행을 DataSet에 채우게 되면, 이미 CustomerID가 1인 행이 DataSet 내부에 존재하기 때문에 제약조건 위반 오류가 발생합니다.

    이러한 문제점을 해결하기 위해서는, 데이터원본과 DataSet 개체에서 동시에 자동 증가 열을 사용하는 경우에는, DataSet의 자동증가열의 AutoIncrementStep 을 -1로 설정하고, AutoIncrementSeed 은 0으로 설정하고, 데이터원본의 자동증가 열의 식별자 값은 1로부터 시작하여 점점 값이 커지도록 설정하게 됩니다. 결국, DataSet 개체는 자동증가 열 값을 음수로 생성하기 때문에, 데이터원본에 의해서 생성된 자동증가 열의 양수 값과 절대 충돌이 발생하지 않습니다. 다른 대안으로는 자동증가열대신 GUID 열을 사용하는 방법이 있습니다. GUID 값을 생성하는 알고리즘은 데이터원본에서 생성된 값과 DataSet에서 생성된 값이 절대로 동일하게 생성되지 않도록 보장해 줍니다.

    자동증가 열을 사용하는 이유가 유일한 값을 지정할 목적 외에 다른 이유가 없는 경우에는, 자동증가 열대신 GUID 열을 사용할 것을 고려해 보아야 합니다. GUID 값은 유일하게 식별가능하며, 자동증가 열 값을 사용하는 경우에 발생하는 추가처리작업을 할 필요가 없습니다.

    데이터 원본으로부터 자동증가열 값을 조회하기 위한 사례는 식별자 또는 자동증가숫자 값 조회 (영문) 기사를 참조하십시오.

    긍정적 동시성 위반 점검

    DataSet은 설계상 데이터원본과 비연결상태이기 때문에, 긍정적인 동시성 모델을 사용하여, 다중 클라이언트에서 데이터를 동시에 변경할 때 응용프로그램에서 충돌이 발생하지 않도록 보장해야 합니다.

    긍정적인 동시성 위반여부를 테스트하기 위해서는 몇 가지 방법을 사용할 수 있습니다. 첫째는, 테이블에 timestamp 열을 포함하는 것입니다. 다른 방법으로는, SQL 문장에 WHERE 절을 사용하여, 데이터베이스에 원본 열값을 가지고 있는 행이 존재하는지 확인하는 방법이 있습니다.

    긍정적 동시성에 대한 코드 예제와 관련된 내용은 긍정적 동시성 (영문) 기사를 참조하십시오.

    멀티스레드 프로그래밍

    ADO.NET은 성능, 확장성, 생산성 측면에서 최적화되어 있습니다. ADO.NET 개체는 자원에 잠금설정을 하지 않으며, 단일 스레드에서만 사용해야 합니다. 단, DataSet 개체에서 여러 개의 읽기 작업하기 위해 스레드 안정성을 지원하는 경우는 예외입니다. 하지만, 쓰기작업을 하는 동안에는 DataSet 개체에 대해서 잠금이 설정되어야 합니다.

    꼭 필요한 경우에만 COM Interop을 사용하여 ADO에 접근

    ADO.NET은 대부분의 응용프로그램에 적용할 수 있는 최적의 솔루션으로 설계되었습니다. 하지만, ADOMD와 같은 ADO 개체를 사용해야 하는 응용프로그램이 있을 수 있습니다. 응용프로그램에서 ADO 개체를 사용하기 위해서는, COM Interop를 사용해야 합니다. ADO 개체를 사용하기 위해서 COM Interop을 사용하면, 성능상에 부하가 발생하게 됩니다. 응용프로그램을 설계할 때, ADO 개체를 설계에 포함시키기 전에, 먼저 ADO.NET을 사용하여 설계상 요구조건을 충족시킬 수 있는지 점검해야 합니다.

'General Tech.' 카테고리의 다른 글

땅을 팔려면 포크레인으로~  (0) 2008/01/29
WBS(Work Breakdown Structure)  (0) 2008/01/29
ADO.NET 표준사례  (0) 2008/01/09
Bill Gates 2008 CES Keynote Speech Part 4  (0) 2008/01/08
2008 CES Keynote 'Bill Gates'  (0) 2008/01/08
Bill Gates 2008 CES Keynote Speech Part 3  (0) 2008/01/08
TAG
Posted by -세티-