트랜잭션 내에서 래핑된 데이터베이스 수정(VB)

작성자 : Scott Mitchell

PDF 다운로드

이 자습서는 데이터 일괄 처리 업데이트, 삭제 및 삽입을 살펴보는 네 가지 중 첫 번째 자습서입니다. 이 자습서에서는 데이터베이스 트랜잭션을 통해 일괄 처리 수정을 원자성 작업으로 수행하는 방법을 알아봅니다. 이렇게 하면 모든 단계가 성공하거나 모든 단계가 실패합니다.

소개

데이터 삽입, 업데이트 및 삭제에 대한 개요 자습서부터 살듯이 GridView는 행 수준 편집 및 삭제에 대한 기본 제공 지원을 제공합니다. 마우스를 몇 번 클릭하면 행별로 편집 및 삭제에 만족하는 한 코드 줄을 작성하지 않고 풍부한 데이터 수정 인터페이스를 만들 수 있습니다. 그러나 특정 시나리오에서는 이것이 충분하지 않으며 사용자에게 레코드 일괄 처리를 편집하거나 삭제할 수 있는 기능을 제공해야 합니다.

예를 들어 대부분의 웹 기반 전자 메일 클라이언트는 그리드를 사용하여 각 행에 이메일 정보(제목, 보낸 사람 등)와 함께 확인란이 포함된 각 메시지를 나열합니다. 이 인터페이스를 사용하면 사용자가 여러 메시지를 확인한 다음 선택한 메시지 삭제 단추를 클릭하여 여러 메시지를 삭제할 수 있습니다. 일괄 편집 인터페이스는 사용자가 일반적으로 다양한 레코드를 편집하는 경우에 이상적입니다. 사용자가 편집을 클릭하고 변경한 다음 수정해야 하는 각 레코드에 대해 업데이트를 클릭하도록 강제하는 대신 일괄 편집 인터페이스는 편집 인터페이스를 사용하여 각 행을 렌더링합니다. 사용자는 변경해야 하는 행 집합을 빠르게 수정한 다음 모두 업데이트 단추를 클릭하여 이러한 변경 내용을 저장할 수 있습니다. 이 자습서 집합에서는 데이터 일괄 처리를 삽입, 편집 및 삭제하기 위한 인터페이스를 만드는 방법을 살펴봅니다.

일괄 처리 작업을 수행할 때는 다른 작업이 실패하는 동안 일괄 처리의 일부 작업이 성공할 수 있는지 여부를 결정하는 것이 중요합니다. 일괄 처리 삭제 인터페이스를 고려합니다. 첫 번째 선택한 레코드가 성공적으로 삭제되었지만 두 번째 레코드가 실패하는 경우 외래 키 제약 조건 위반으로 인해 어떻게 해야 하나요? 첫 번째 레코드 삭제를 롤백해야 하나요, 아니면 첫 번째 레코드가 삭제된 상태로 유지되는 것이 허용되나요?

일괄 처리 작업을 모든 단계가 성공하거나 모든 단계가 실패하는 원자성 작업으로 처리하려면 데이터베이스 트랜잭션에 대한 지원을 포함하도록 데이터 액세스 계층을 보강해야 합니다. 데이터베이스 트랜잭션은 트랜잭션의 INSERT우산 아래에서 실행되는 , UPDATEDELETE 문 집합에 대한 원자성을 보장하며 대부분의 최신 데이터베이스 시스템에서 지원하는 기능입니다.

이 자습서에서는 데이터베이스 트랜잭션을 사용하도록 DAL을 확장하는 방법을 살펴보겠습니다. 후속 자습서에서는 인터페이스를 일괄 삽입, 업데이트 및 삭제하기 위해 웹 페이지 구현을 검토합니다. 시작해 보겠습니다!

참고

일괄 처리 트랜잭션에서 데이터를 수정할 때 원자성이 항상 필요한 것은 아닙니다. 일부 시나리오에서는 일부 데이터 수정이 성공하고 웹 기반 전자 메일 클라이언트에서 전자 메일 집합을 삭제하는 경우와 같이 동일한 일괄 처리에서 다른 데이터 수정이 실패하는 것이 허용될 수 있습니다. 삭제 프로세스 중간에 데이터베이스 오류가 있는 경우 오류 없이 처리된 레코드가 삭제된 상태로 유지되는 것이 좋습니다. 이러한 경우 데이터베이스 트랜잭션을 지원하도록 DAL을 수정할 필요가 없습니다. 그러나 원자성이 중요한 다른 일괄 처리 작업 시나리오가 있습니다. 고객이 한 은행 계좌에서 다른 은행 계좌로 자금을 옮기면 두 가지 작업을 수행해야 합니다. 즉, 첫 번째 계좌에서 자금을 공제한 다음 두 번째 계좌에 추가해야 합니다. 은행은 첫 번째 단계가 성공하는 것을 신경 쓰지 않을 수도 있지만 두 번째 단계는 실패하지만 고객은 당연히 화가 납니다. 다음 세 가지 자습서에서 빌드할 인터페이스를 일괄 처리 삽입, 업데이트 및 삭제하는 데 사용할 계획이 없는 경우에도 데이터베이스 트랜잭션을 지원하기 위해 이 자습서를 통해 작업하고 DAL의 향상된 기능을 구현하는 것이 좋습니다.

트랜잭션 개요

대부분의 데이터베이스에는 여러 데이터베이스 명령을 단일 논리 작업 단위로 그룹화할 수 있는 트랜잭션 지원이 포함됩니다. 트랜잭션을 구성하는 데이터베이스 명령은 원자성으로 보장됩니다. 즉, 모든 명령이 실패하거나 모두 성공합니다.

일반적으로 트랜잭션은 다음 패턴을 사용하여 SQL 문을 통해 구현됩니다.

  1. 트랜잭션의 시작을 나타냅니다.
  2. 트랜잭션을 구성하는 SQL 문을 실행합니다.
  3. 2단계의 문 중 하나에 오류가 있는 경우 트랜잭션을 롤백합니다.
  4. 2단계의 모든 문이 오류 없이 완료되면 트랜잭션을 커밋합니다.

트랜잭션을 만들고, 커밋하고, 롤백하는 데 사용되는 SQL 문은 SQL 스크립트를 작성하거나 저장 프로시저를 만들 때 수동으로 입력하거나 네임스페이스의 ADO.NET 또는 클래스를 사용하는 프로그래밍 방법을 통해 입력할 수 있습니다System.Transactions. 이 자습서에서는 ADO.NET 사용하여 트랜잭션 관리만 살펴봅니다. 이후 자습서에서는 데이터 액세스 계층에서 저장 프로시저를 사용하는 방법을 살펴보겠습니다. 이때 트랜잭션을 만들고, 롤백하고, 커밋하기 위한 SQL 문을 살펴봅니다. 그 동안 자세한 내용은 SQL Server 저장 프로시저에서 트랜잭션 관리를 참조하세요.

참고

네임스페이스의 클래스 System.Transactions 를 사용하면 개발자가 TransactionScope 트랜잭션의 scope 내에서 일련의 문을 프로그래밍 방식으로 래핑할 수 있으며 두 개의 서로 다른 데이터베이스 또는 Microsoft SQL Server 데이터베이스, Oracle 데이터베이스 및 웹 서비스와 같은 다른 유형의 데이터 저장소와 같은 여러 원본을 포함하는 복잡한 트랜잭션에 대한 지원이 포함됩니다. ADO.NET 데이터베이스 트랜잭션에 더 구체적이고 대부분의 경우 리소스 집약적이 훨씬 적기 때문에 클래스 대신 TransactionScope 이 자습서에 ADO.NET 트랜잭션을 사용하기로 결정했습니다. 또한 특정 시나리오에서 클래스는 TransactionScope MSDTC(Microsoft Distributed Transaction Coordinator)를 사용합니다. MSDTC와 관련된 구성, 구현 및 성능 문제는 이러한 자습서의 scope 넘어 다소 전문적이고 고급적인 토픽으로 만듭니다.

ADO.NET SqlClient 공급자를 사용하는 경우 개체를 반환하는 클래스의 BeginTransaction 메서드 호출을 SqlConnection 통해 트랜잭션이 SqlTransaction시작됩니다. 트랜잭션을 구성하는 데이터 수정 문은 블록 내에 try...catch 배치됩니다. 블록의 문에서 try 오류가 발생하면 실행은 개체의 Rollback 메서드를 통해 SqlTransaction 트랜잭션을 롤백할 수 있는 블록으로 전송 catch 됩니다. 모든 문이 성공적으로 완료되면 블록 끝에 try 있는 SqlTransaction 개체의 Commit 메서드를 호출하면 트랜잭션이 커밋됩니다. 다음 코드 조각은 이 패턴을 보여 줍니다.

' Create the SqlTransaction object
Dim myTransaction As SqlTransaction = SqlConnectionObject.BeginTransaction();
Try
    '
    ' ... Perform the database transaction�s data modification statements...
    '
    ' If we reach here, no errors, so commit the transaction
    myTransaction.Commit()
Catch
    ' If we reach here, there was an error, so rollback the transaction
    myTransaction.Rollback()
    Throw
End Try

기본적으로 형식화된 데이터 세트의 TableAdapters는 트랜잭션을 사용하지 않습니다. 트랜잭션에 대한 지원을 제공하려면 위의 패턴을 사용하여 트랜잭션의 scope 내에서 일련의 데이터 수정 문을 수행하는 추가 메서드를 포함하도록 TableAdapter 클래스를 보강해야 합니다. 2단계에서는 부분 클래스를 사용하여 이러한 메서드를 추가하는 방법을 알아보세요.

1단계: 일괄 처리된 데이터 웹 페이지 작업 만들기

데이터베이스 트랜잭션을 지원하기 위해 DAL을 보강하는 방법을 살펴보기 전에 먼저 이 자습서에 필요한 ASP.NET 웹 페이지와 다음 세 가지 웹 페이지를 만들어 보겠습니다. 먼저 라는 BatchData 새 폴더를 추가한 다음 다음 ASP.NET 페이지를 추가하여 각 페이지를 master 페이지와 Site.master 연결합니다.

  • Default.aspx
  • Transactions.aspx
  • BatchUpdate.aspx
  • BatchDelete.aspx
  • BatchInsert.aspx

SqlDataSource-Related 자습서에 대한 ASP.NET 페이지 추가

그림 1: SqlDataSource-Related 자습서에 대한 ASP.NET 페이지 추가

다른 폴더와 마찬가지로 는 Default.aspx 사용자 컨트롤을 SectionLevelTutorialListing.ascx 사용하여 해당 섹션 내의 자습서를 나열합니다. 따라서 솔루션 탐색기 페이지의 디자인 보기로 끌어 이 사용자 컨트롤 Default.aspx 을 에 추가합니다.

SectionLevelTutorialListing.ascx 사용자 컨트롤을 추가하여 Default.aspx

그림 2: 사용자 컨트롤을 SectionLevelTutorialListing.ascxDefault.aspx 추가합니다(전체 크기 이미지를 보려면 클릭).

마지막으로 이러한 네 페이지를 파일에 항목으로 추가합니다 Web.sitemap . 특히 사이트 맵 <siteMapNode>사용자 지정 다음에 다음 태그를 추가합니다.

<siteMapNode title="Working with Batched Data" 
    url="~/BatchData/Default.aspx" 
    description="Learn how to perform batch operations as opposed to 
                 per-row operations.">
    
    <siteMapNode title="Adding Support for Transactions" 
        url="~/BatchData/Transactions.aspx" 
        description="See how to extend the Data Access Layer to support 
                     database transactions." />
    <siteMapNode title="Batch Updating" 
        url="~/BatchData/BatchUpdate.aspx" 
        description="Build a batch updating interface, where each row in a 
                      GridView is editable." />
    <siteMapNode title="Batch Deleting" 
        url="~/BatchData/BatchDelete.aspx" 
        description="Explore how to create an interface for batch deleting 
                     by adding a CheckBox to each GridView row." />
    <siteMapNode title="Batch Inserting" 
        url="~/BatchData/BatchInsert.aspx" 
        description="Examine the steps needed to create a batch inserting 
                     interface, where multiple records can be created at the 
                     click of a button." />
</siteMapNode>

를 업데이트 Web.sitemap한 후 잠시 시간을 내어 브라우저를 통해 자습서 웹 사이트를 봅니다. 이제 왼쪽 메뉴에 일괄 처리된 데이터 자습서 작업에 대한 항목이 포함됩니다.

이제 사이트 맵에 일괄 처리된 데이터 작업 자습서에 대한 항목이 포함됩니다.

그림 3: 이제 사이트 맵에 일괄 처리된 데이터 작업 자습서에 대한 항목이 포함되어 있습니다.

2단계: 데이터베이스 트랜잭션을 지원하도록 데이터 액세스 계층 업데이트

첫 번째 자습서인 데이터 액세스 계층 만들기에서 설명한 대로 DAL의 형식화된 데이터 세트는 DataTables 및 TableAdapters로 구성됩니다. DataTable은 데이터를 보유하는 반면 TableAdapters는 데이터베이스에서 DataTable로 데이터를 읽고, DataTables에 대한 변경 내용으로 데이터베이스를 업데이트하는 기능을 제공합니다. TableAdapters는 일괄 업데이트 및 DB-Direct라고 하는 데이터를 업데이트하기 위한 두 가지 패턴을 제공합니다. Batch 업데이트 패턴을 사용하면 TableAdapter에 DataSet, DataTable 또는 DataRows 컬렉션이 전달됩니다. 이 데이터는 열거되고 삽입, 수정 또는 삭제된 각 행에 InsertCommand대해 , UpdateCommand또는 DeleteCommand 가 실행됩니다. DB-Direct 패턴을 사용하면 TableAdapter는 단일 레코드를 삽입, 업데이트 또는 삭제하는 데 필요한 열 값을 대신 전달합니다. 그런 다음 DB Direct 패턴 메서드는 전달된 값을 사용하여 적절한 InsertCommand, UpdateCommand또는 DeleteCommand 문을 실행합니다.

사용되는 업데이트 패턴에 관계없이 TableAdapters 자동 생성된 메서드는 트랜잭션을 사용하지 않습니다. 기본적으로 TableAdapter에서 수행하는 각 삽입, 업데이트 또는 삭제는 단일 불연속 작업으로 처리됩니다. instance 경우 BLL의 일부 코드에서 DB-Direct 패턴을 사용하여 데이터베이스에 10개의 레코드를 삽입한다고 상상해 보십시오. 이 코드는 TableAdapter의 Insert 메서드를 10번 호출합니다. 처음 다섯 개의 삽입이 성공하지만 여섯 번째 삽입으로 인해 예외가 발생하면 처음 5개의 삽입된 레코드가 데이터베이스에 유지됩니다. 마찬가지로 Batch 업데이트 패턴을 사용하여 DataTable에서 삽입, 수정 및 삭제된 행에 대한 삽입, 업데이트 및 삭제를 수행하는 경우 처음 몇 가지 수정이 성공했지만 나중에 오류가 발생한 경우 완료된 이전 수정 내용은 데이터베이스에 유지됩니다.

특정 시나리오에서는 일련의 수정에서 원자성을 보장하려고 합니다. 이렇게 하려면 트랜잭션의 우산 아래에 , UpdateCommandDeleteCommand 를 실행하는 InsertCommand새 메서드를 추가하여 TableAdapter를 수동으로 확장해야 합니다. 데이터 액세스 계층 만들기에서 부분 클래스를 사용하여 형식화된 데이터 세트 내에서 DataTable의 기능을 확장하는 방법을 살펴보았습니다. 이 기술은 TableAdapters와 함께 사용할 수도 있습니다.

형식화된 데이터 세트 Northwind.xsd 는 폴더의 DAL 하위 폴더에 App_Code 있습니다. 라는 폴더에 하위 폴더를 DAL 만들고 라는 TransactionSupportProductsTableAdapter.TransactionSupport.vb 새 클래스 파일을 추가합니다(그림 4 참조). 이 파일은 트랜잭션을 사용하여 데이터 수정을 ProductsTableAdapter 수행하는 메서드를 포함하는 의 부분 구현을 포함합니다.

TransactionSupport라는 폴더와 이름이 ProductsTableAdapter.TransactionSupport.vb 클래스 파일을 추가합니다.

그림 4: 라는 폴더 및 라는 TransactionSupport 클래스 파일 추가 ProductsTableAdapter.TransactionSupport.vb

파일에 다음 코드를 입력합니다 ProductsTableAdapter.TransactionSupport.vb .

Imports System.Data
Imports System.Data.SqlClient
Namespace NorthwindTableAdapters
    Partial Public Class ProductsTableAdapter
        Private _transaction As SqlTransaction
        Private Property Transaction() As SqlTransaction
            Get
                Return Me._transaction
            End Get
            Set(ByVal Value As SqlTransaction)
                Me._transaction = Value
            End Set
        End Property
        Public Sub BeginTransaction()
            ' Open the connection, if needed
            If Me.Connection.State <> ConnectionState.Open Then
                Me.Connection.Open()
            End If
            ' Create the transaction and assign it to the Transaction property
            Me.Transaction = Me.Connection.BeginTransaction()
            ' Attach the transaction to the Adapters
            For Each command As SqlCommand In Me.CommandCollection
                command.Transaction = Me.Transaction
            Next
            Me.Adapter.InsertCommand.Transaction = Me.Transaction
            Me.Adapter.UpdateCommand.Transaction = Me.Transaction
            Me.Adapter.DeleteCommand.Transaction = Me.Transaction
        End Sub
        Public Sub CommitTransaction()
            ' Commit the transaction
            Me.Transaction.Commit()
            ' Close the connection
            Me.Connection.Close()
        End Sub
        Public Sub RollbackTransaction()
            ' Rollback the transaction
            Me.Transaction.Rollback()
            ' Close the connection
            Me.Connection.Close()
        End Sub
    End Class
End Namespace

Partial 여기서 클래스 선언의 키워드(keyword) 에 추가된 멤버가 네임스페이스의 클래스에 추가되어야 한다는 것을 ProductsTableAdapter 컴파일러에 NorthwindTableAdapters 나타냅니다. Imports System.Data.SqlClient 파일 맨 위에 있는 문을 적어 둡니다. TableAdapter는 SqlClient 공급자를 사용하도록 구성되었으므로 내부적으로 개체를 SqlDataAdapter 사용하여 데이터베이스에 명령을 실행합니다. 따라서 클래스를 사용하여 트랜잭션을 SqlTransaction 시작한 다음 커밋하거나 롤백해야 합니다. Microsoft SQL Server 이외의 데이터 저장소를 사용하는 경우 적절한 공급자를 사용해야 합니다.

이러한 메서드는 트랜잭션을 시작, 롤백 및 커밋하는 데 필요한 구성 요소를 제공합니다. 이러한 클래스는 에서 , DAL의 다른 클래스 또는 아키텍처의 ProductsTableAdapter다른 계층(예: BLL)에서 사용할 수 있도록 으로 표시됩니다Public. BeginTransaction TableAdapter의 내부 SqlConnection (필요한 경우)를 열고, 트랜잭션을 시작하고, 속성에 Transaction 할당하고, 트랜잭션을 내부 SqlDataAdapterSqlCommand 개체에 연결합니다. CommitTransactionRollbackTransaction 는 내부 Connection 개체를 Transaction 닫기 전에 개체 및 CommitRollback 메서드를 각각 호출합니다.

3단계: 트랜잭션의 우산 아래에서 데이터를 업데이트하고 삭제하는 메서드 추가

이러한 메서드가 완료되면 메서드를 또는 트랜잭션의 우산 아래에서 일련의 명령을 수행하는 BLL에 추가할 ProductsDataTable 준비가 된 것입니다. 다음 메서드는 Batch 업데이트 패턴을 사용하여 트랜잭션을 ProductsDataTable 사용하여 instance 업데이트합니다. 메서드를 호출하여 트랜잭션을 BeginTransaction 시작한 다음 블록을 사용하여 Try...Catch 데이터 수정 문을 실행합니다. 개체의 Update 메서드를 호출하면 Adapter 예외가 발생하면 트랜잭션이 롤백되고 예외가 다시 throw되는 블록으로 실행이 전송 catch 됩니다. 메서드는 Update 제공된 ProductsDataTable 의 행을 열거하고 필요한 InsertCommand, UpdateCommandDeleteCommand s를 수행하여 Batch 업데이트 패턴을 구현합니다. 이러한 명령 중 하나에 오류가 발생하면 트랜잭션이 롤백되어 트랜잭션 수명 동안 이전에 수정한 내용이 실행 취소됩니다. 명령문이 Update 오류 없이 완료되면 트랜잭션 전체가 커밋됩니다.

Public Function UpdateWithTransaction _
    (ByVal dataTable As Northwind.ProductsDataTable) As Integer
    
    Me.BeginTransaction()
    Try
        ' Perform the update on the DataTable
        Dim returnValue As Integer = Me.Adapter.Update(dataTable)
        ' If we reach here, no errors, so commit the transaction
        Me.CommitTransaction()
        Return returnValue
    Catch
        ' If we reach here, there was an error, so rollback the transaction
        Me.RollbackTransaction()
        Throw
    End Try
End Function

UpdateWithTransaction partial 클래스를 ProductsTableAdapter 통해 클래스에 메서드를 추가합니다 ProductsTableAdapter.TransactionSupport.vb. 또는 이 메서드를 몇 가지 사소한 구문 변경과 함께 비즈니스 논리 계층의 ProductsBLL 클래스에 추가할 수 있습니다. 즉, , Me.CommitTransaction()의 키워드(keyword) 로 바꿔 Adapter 야 합니다(형식의 속성 ProductsBLLProductsTableAdapter이름인 Adapter 회수). MeMe.BeginTransaction()Me.RollbackTransaction()

메서드는 UpdateWithTransaction Batch 업데이트 패턴을 사용하지만 다음 메서드와 같이 트랜잭션의 scope 내에서 일련의 DB-Direct 호출을 사용할 수도 있습니다. 메서드는 DeleteProductsWithTransaction 를 삭제할 형식Integer의 입력으로 List(Of T) 허용합니다ProductID. 메서드는 에 대한 호출 BeginTransaction 을 통해 트랜잭션을 시작한 다음 블록에서 TryProductID 값에 대한 DB-Direct 패턴 Delete 메서드를 호출하는 제공된 목록을 반복합니다. 에 대한 Delete 호출이 실패하면 트랜잭션이 롤백되고 Catch 예외가 다시 throw되는 블록으로 컨트롤이 전송됩니다. 모든 호출이 Delete 성공하면 트랜잭션이 커밋됩니다. 클래스에 이 메서드를 추가합니다 ProductsBLL .

Public Sub DeleteProductsWithTransaction _
    (ByVal productIDs As System.Collections.Generic.List(Of Integer))
    
    ' Start the transaction
    Adapter.BeginTransaction()
    Try
        ' Delete each product specified in the list
        For Each productID As Integer In productIDs
            Adapter.Delete(productID)
        Next
        ' Commit the transaction
        Adapter.CommitTransaction()
    Catch
        ' There was an error - rollback the transaction
        Adapter.RollbackTransaction()
        Throw
    End Try
End Sub

여러 TableAdapter에 트랜잭션 적용

이 자습서에서 검사한 트랜잭션 관련 코드를 사용하면 에 대한 여러 문을 ProductsTableAdapter 원자성 작업으로 처리할 수 있습니다. 그러나 다른 데이터베이스 테이블에 대한 여러 수정을 원자성으로 수행해야 하는 경우 어떻게 해야 할까요? instance 경우 범주를 삭제할 때 먼저 현재 제품을 다른 범주에 다시 할당할 수 있습니다. 제품을 다시 할당하고 범주를 삭제하는 두 단계는 원자성 작업으로 실행되어야 합니다. 그러나 는 ProductsTableAdapter 테이블을 수정하기 Products 위한 메서드만 포함하고 는 테이블을 CategoriesTableAdapter 수정하기 Categories 위한 메서드만 포함합니다. 그렇다면 트랜잭션이 두 TableAdapters를 어떻게 포괄할 수 있을까요?

한 가지 옵션은 명명 DeleteCategoryAndReassignProducts(categoryIDtoDelete, reassignToCategoryID) 된 에 메서드를 CategoriesTableAdapter 추가하고 해당 메서드가 제품을 다시 할당하고 저장 프로시저 내에 정의된 트랜잭션의 scope 내에서 범주를 삭제하는 저장 프로시저를 호출하도록 하는 것입니다. 이후 자습서에서는 저장 프로시저에서 트랜잭션을 시작, 커밋 및 롤백하는 방법을 살펴보겠습니다.

또 다른 옵션은 메서드가 포함된 DAL에서 도우미 클래스를 만드는 것입니다 DeleteCategoryAndReassignProducts(categoryIDtoDelete, reassignToCategoryID) . 이 메서드는 및 ProductsTableAdapterCategoriesTableAdapter instance 만든 다음 이러한 두 TableAdapters Connection 속성을 동일한 SqlConnection instance 설정합니다. 이 시점에서 두 TableAdapters 중 하나가 에 대한 호출을 사용하여 트랜잭션을 시작합니다 BeginTransaction. 제품을 다시 할당하고 범주를 삭제하기 위한 TableAdapters 메서드는 트랜잭션이 필요에 따라 커밋되거나 롤백된 블록에서 Try...Catch 호출됩니다.

4단계: 비즈니스 논리 계층에 메서드 추가UpdateWithTransaction

3단계에서는 DAL의 UpdateWithTransaction 에 메서드 ProductsTableAdapter 를 추가했습니다. 해당 메서드를 BLL에 추가해야 합니다. 프레젠테이션 계층에서 DAL을 직접 호출하여 메서드를 호출 UpdateWithTransaction 할 수 있지만, 이러한 자습서에서는 프레젠테이션 계층에서 DAL을 격리하는 계층화된 아키텍처를 정의하기 위해 노력했습니다. 따라서 이 접근 방식을 계속 진행하는 것이 좋습니다.

ProductsBLL 클래스 파일을 열고 해당 DAL 메서드를 호출하는 라는 UpdateWithTransaction 메서드를 추가합니다. 이제 두 개의 새 메서드가 있습니다. ProductsBLLUpdateWithTransaction방금 추가한 및 DeleteProductsWithTransaction3단계에서 추가된 입니다.

Public Function UpdateWithTransaction _
    (ByVal products As Northwind.ProductsDataTable) As Integer
    
    Return Adapter.UpdateWithTransaction(products)
End Function
Public Sub DeleteProductsWithTransaction _
    (ByVal productIDs As System.Collections.Generic.List(Of Integer))
    
    ' Start the transaction
    Adapter.BeginTransaction()
    Try
        ' Delete each product specified in the list
        For Each productID As Integer In productIDs
            Adapter.Delete(productID)
        Next
        ' Commit the transaction
        Adapter.CommitTransaction()
    Catch
        ' There was an error - rollback the transaction
        Adapter.RollbackTransaction()
        Throw
    End Try
End Sub

참고

이러한 메서드는 ASP.NET 페이지 코드 숨김 클래스에서 ProductsBLL 직접 이러한 메서드를 호출하기 때문에 클래스의 대부분의 다른 메서드에 할당된 특성을 포함하지 DataObjectMethodAttribute 않습니다. DataObjectMethodAttribute ObjectDataSource의 데이터 원본 구성 마법사 및 탭(SELECT, UPDATE, INSERT 또는 DELETE)에 표시해야 하는 메서드에 플래그를 지정하는 데 사용됩니다. GridView에는 일괄 처리 편집 또는 삭제에 대한 기본 제공 지원이 없으므로 코드 없는 선언적 접근 방식을 사용하는 대신 프로그래밍 방식으로 이러한 메서드를 호출해야 합니다.

5단계: 프레젠테이션 계층에서 데이터베이스 데이터 원자성 업데이트

레코드 일괄 처리를 업데이트할 때 트랜잭션이 미치는 영향을 설명하기 위해 GridView의 모든 제품을 나열하고 클릭할 때 제품 값을 다시 할당하는 단추 웹 컨트롤을 포함하는 사용자 인터페이스를 CategoryID 만들어 보겠습니다. 특히 범주 재할당이 진행되므로 처음 여러 제품에 유효한 CategoryID 값이 할당되고 다른 제품에는 존재하지 않는 CategoryID 값이 의도적으로 할당됩니다. 기존 범주CategoryID와 일치하지 않는 제품 CategoryID 으로 데이터베이스를 업데이트하려고 하면 외래 키 제약 조건 위반이 발생하고 예외가 발생합니다. 이 예제에서 볼 수 있는 것은 트랜잭션을 사용할 때 외래 키 제약 조건 위반으로 인해 발생한 예외로 인해 이전 CategoryID 유효한 변경 내용이 롤백된다는 것입니다. 그러나 트랜잭션을 사용하지 않는 경우 초기 범주에 대한 수정 내용은 그대로 유지됩니다.

먼저 폴더에서 Transactions.aspxBatchData 페이지를 열고 Toolbox에서 Designer GridView를 끌어옵니다. 를 IDProducts 로 설정하고 스마트 태그에서 라는 ProductsDataSource새 ObjectDataSource에 바인딩합니다. 클래스의 GetProducts 메서드에서 데이터를 끌어오도록 ObjectDataSource를 ProductsBLL 구성합니다. 읽기 전용인 GridView이므로 UPDATE, INSERT 및 DELETE 탭의 드롭다운 목록을 (없음)으로 설정하고 마침을 클릭합니다.

ProductsBLL 클래스의 GetProducts 메서드를 사용하도록 ObjectDataSource 구성

그림 5: 클래스의 GetProducts 메서드를 사용하도록 ProductsBLL ObjectDataSource 구성(전체 크기 이미지를 보려면 클릭)

UPDATE, INSERT 및 DELETE 탭의 Drop-Down Lists (없음)으로 설정합니다.

그림 6: UPDATE, INSERT 및 DELETE 탭의 Drop-Down Lists (없음)으로 설정합니다(전체 크기 이미지를 보려면 클릭).

데이터 원본 구성 마법사를 완료한 후 Visual Studio는 제품 데이터 필드에 대한 BoundFields 및 CheckBoxField를 만듭니다. , , ProductNameCategoryNameCategoryID및 를 제외한 ProductID이러한 필드를 모두 제거하고 및 CategoryName BoundFields HeaderText 속성의 ProductName 이름을 각각 Product 및 Category로 바꿉니다. 스마트 태그에서 페이징 사용 옵션을 검사. 이러한 수정을 수행한 후 GridView 및 ObjectDataSource의 선언적 태그는 다음과 같이 표시됩니다.

<asp:GridView ID="Products" runat="server" AllowPaging="True" 
    AutoGenerateColumns="False" DataKeyNames="ProductID" 
    DataSourceID="ProductsDataSource">
    <Columns>
        <asp:BoundField DataField="ProductID" HeaderText="ProductID" 
            InsertVisible="False" ReadOnly="True" 
            SortExpression="ProductID" />
        <asp:BoundField DataField="ProductName" HeaderText="Product" 
            SortExpression="ProductName" />
        <asp:BoundField DataField="CategoryID" HeaderText="CategoryID" 
            SortExpression="CategoryID" />
        <asp:BoundField DataField="CategoryName" HeaderText="Category" 
            SortExpression="CategoryName" />
    </Columns>
</asp:GridView>
<asp:ObjectDataSource ID="ProductsDataSource" runat="server" 
    OldValuesParameterFormatString="original_{0}"
    SelectMethod="GetProducts" TypeName="ProductsBLL">
</asp:ObjectDataSource>

다음으로 GridView 위에 세 개의 단추 웹 컨트롤을 추가합니다. 첫 번째 Button의 Text 속성을 Refresh Grid로 설정하고, 두 번째 속성을 범주 수정(WITH TRANSACTION)으로 설정하고, 세 번째 속성을 범주 수정(트랜잭션 없음)으로 설정합니다.

<p>
    <asp:Button ID="RefreshGrid" runat="server" Text="Refresh Grid" />
</p>
<p>
    <asp:Button ID="ModifyCategoriesWithTransaction" runat="server"
        Text="Modify Categories (WITH TRANSACTION)" />
</p>
<p>
    <asp:Button ID="ModifyCategoriesWithoutTransaction" runat="server"
        Text="Modify Categories (WITHOUT TRANSACTION)" />
</p>

이때 Visual Studio의 디자인 보기는 그림 7에 표시된 스크린샷과 유사합니다.

페이지에 GridView 및 세 개의 단추 웹 컨트롤이 포함되어 있습니다.

그림 7: 페이지에 GridView 및 세 개의 단추 웹 컨트롤이 포함되어 있습니다(전체 크기 이미지를 보려면 클릭).

세 개의 Button Click 이벤트 각각에 대한 이벤트 처리기를 만들고 다음 코드를 사용합니다.

Protected Sub RefreshGrid_Click _
    (ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles RefreshGrid.Click
    
    Products.DataBind()
End Sub
Protected Sub ModifyCategoriesWithTransaction_Click _
    (ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles ModifyCategoriesWithTransaction.Click
    
    ' Get the set of products
    Dim productsAPI As New ProductsBLL()
    Dim productsData As Northwind.ProductsDataTable = productsAPI.GetProducts()
    ' Update each product's CategoryID
    For Each product As Northwind.ProductsRow In productsData
        product.CategoryID = product.ProductID
    Next
    ' Update the data using a transaction
    productsAPI.UpdateWithTransaction(productsData)
    ' Refresh the Grid
    Products.DataBind()
End Sub
Protected Sub ModifyCategoriesWithoutTransaction_Click _
    (ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles ModifyCategoriesWithoutTransaction.Click
    
    ' Get the set of products
    Dim productsAPI As New ProductsBLL()
    Dim productsData As Northwind.ProductsDataTable = productsAPI.GetProducts()
    ' Update each product's CategoryID
    For Each product As Northwind.ProductsRow In productsData
        product.CategoryID = product.ProductID
    Next
    ' Update the data WITHOUT using a transaction
    Dim productsAdapter As New NorthwindTableAdapters.ProductsTableAdapter()
    productsAdapter.Update(productsData)
    ' Refresh the Grid
    Products.DataBind()
End Sub

Refresh Button의 Click 이벤트 처리기는 GridView의 DataBind 메서드를 호출 Products 하여 데이터를 GridView에 다시 바인딩하기만 하면 됩니다.

두 번째 이벤트 처리기는 제품을 CategoryID 재할당하고 BLL의 새 트랜잭션 메서드를 사용하여 트랜잭션의 우산 아래 데이터베이스 업데이트를 수행합니다. 각 제품의 는 임의 CategoryID 로 와 ProductID동일한 값으로 설정됩니다. 이러한 제품에는 유효한 CategoryID 에 매핑되는 값이 있으므로 ProductID 처음 몇 가지 제품에 대해 잘 작동합니다. 그러나 s가 ProductID 너무 커지면 이 우연한 겹 ProductID 치는 와 CategoryID 의 중복은 더 이상 적용되지 않습니다.

세 번째 Click 이벤트 처리기는 동일한 방식으로 제품을 CategoryID 업데이트하지만 기본 Update 메서드를 사용하여 데이터베이스에 ProductsTableAdapter 업데이트를 보냅니다. 이 Update 메서드는 트랜잭션 내에서 일련의 명령을 래핑하지 않으므로 처음 발견된 외래 키 제약 조건 위반 오류가 지속되기 전에 이러한 변경 내용이 적용됩니다.

이 동작을 보여 주려면 브라우저를 통해 이 페이지를 방문하세요. 처음에는 그림 8과 같이 데이터의 첫 번째 페이지가 표시됩니다. 다음으로 범주 수정(WITH TRANSACTION) 단추를 클릭합니다. 이로 인해 포스트백이 발생하고 모든 제품 CategoryID 값을 업데이트하려고 시도하지만 외래 키 제약 조건 위반이 발생합니다(그림 9 참조).

제품은 Pageable GridView에 표시됩니다.

그림 8: 제품이 Pageable GridView에 표시됩니다(전체 크기 이미지를 보려면 클릭).

범주를 다시 할당하면 외래 키 제약 조건 위반이 발생합니다.

그림 9: 범주를 다시 할당하면 외래 키 제약 조건 위반이 발생합니다(전체 크기 이미지를 보려면 클릭).

이제 브라우저의 뒤로 단추를 누른 다음 눈금 새로 고침 단추를 클릭합니다. 데이터를 새로 고치는 경우 그림 8과 정확히 동일한 출력이 표시됩니다. 즉, 일부 제품이 CategoryID 법적 값으로 변경되고 데이터베이스에서 업데이트되었지만 외래 키 제약 조건 위반이 발생했을 때 롤백되었습니다.

이제 범주 수정(트랜잭션 없음) 단추를 클릭해 보세요. 이로 인해 동일한 외래 키 제약 조건 위반 오류가 발생하지만(그림 9 참조) 이번에는 값이 CategoryID 법적 값으로 변경된 제품이 롤백되지 않습니다. 브라우저의 뒤로 단추를 누린 다음 그리드 새로 고침 단추를 누릅니다. 그림 10에서 보여 주듯이 처음 CategoryID 8개 제품의 s가 재할당되었습니다. 예를 들어 그림 8에서 Chang의 은 CategoryID 1이지만 그림 10에서는 2로 재할당되었습니다.

일부 제품 CategoryID 값은 업데이트되었지만 다른 제품은 업데이트되지 않았습니다.

그림 10: 일부 제품 값이 CategoryID 업데이트되었지만 다른 제품 값은 업데이트되지 않았습니다(전체 크기 이미지를 보려면 클릭).

요약

기본적으로 TableAdapter의 메서드는 트랜잭션의 scope 내에서 실행된 데이터베이스 문을 래핑하지 않지만 약간의 작업으로 트랜잭션을 만들고, 커밋하고, 롤백하는 메서드를 추가할 수 있습니다. 이 자습서에서는 클래스에서 , CommitTransactionRollbackTransaction의 세 가지 BeginTransaction메서드를 ProductsTableAdapter 만들었습니다. 블록과 함께 이러한 메서드를 Try...Catch 사용하여 일련의 데이터 수정 문을 원자성으로 만드는 방법을 알아보았습니다. 특히 에서 Batch 업데이트 패턴을 사용하여 제공된 ProductsDataTable의 행에 필요한 수정을 수행하는 메서드ProductsTableAdapter를 만들었습니다UpdateWithTransaction. 또한 BLL의 DeleteProductsWithTransactionProductsBLL 클래스에 메서드를 추가했습니다. 이 메서드는 값의 ProductIDList 입력으로 수락하고 각 ProductID에 대한 DB-Direct 패턴 메서드를 Delete 호출합니다. 두 메서드 모두 트랜잭션을 만든 다음 블록 내에서 데이터 수정 문을 실행하는 것으로 Try...Catch 시작합니다. 예외가 발생하면 트랜잭션이 롤백되고, 그렇지 않으면 커밋됩니다.

5단계에서는 트랜잭션 일괄 업데이트와 트랜잭션 사용을 소홀히 한 일괄 업데이트의 영향을 보여 줍니다. 다음 세 자습서에서는 이 자습서에 마련된 기초를 기반으로 하고 일괄 업데이트, 삭제 및 삽입을 수행하기 위한 사용자 인터페이스를 만듭니다.

행복한 프로그래밍!

추가 정보

이 자습서에서 설명하는 topics 대한 자세한 내용은 다음 리소스를 참조하세요.

저자 정보

7개의 ASP/ASP.NET 책의 저자이자 4GuysFromRolla.com 창립자인 Scott Mitchell은 1998년부터 Microsoft 웹 기술로 작업해 왔습니다. Scott은 독립 컨설턴트, 트레이너 및 작가로 일합니다. 그의 최신 책은 샘스 티치 유어셀프 ASP.NET 24시간 만에 2.0입니다. 그는 에서mitchell@4GuysFromRolla.com 또는 에서 찾을 http://ScottOnWriting.NET수있는 자신의 블로그를 통해 도달 할 수 있습니다.

특별 감사

이 자습서 시리즈는 많은 유용한 검토자가 검토했습니다. 이 자습서의 수석 검토자는 데이브 가드너, 힐튼 기세나우, 테레사 머피였습니다. 예정된 MSDN 문서를 검토하는 데 관심이 있으신가요? 그렇다면 에 줄을 놓습니다 mitchell@4GuysFromRolla.com.