작성자: 스콧 미첼
이 자습서는 데이터 일괄 처리 업데이트, 삭제 및 삽입을 살펴보는 네 가지 중 첫 번째 자습서입니다. 이 자습서에서는 데이터베이스 트랜잭션을 통해 일괄 처리 수정을 원자성 작업으로 수행하는 방법을 알아봅니다. 이렇게 하면 모든 단계가 성공하거나 모든 단계가 실패합니다.
소개
데이터 삽입, 업데이트 및 삭제에 대한 개요 자습서부터 살본 것처럼 GridView는 행 수준 편집 및 삭제에 대한 기본 제공 지원을 제공합니다. 마우스를 몇 번 클릭하면 행별로 편집 및 삭제에 만족하는 한 코드 줄을 작성하지 않고도 풍부한 데이터 수정 인터페이스를 만들 수 있습니다. 그러나 특정 시나리오에서는 이것이 충분하지 않으며 사용자에게 레코드 일괄 처리를 편집하거나 삭제할 수 있는 기능을 제공해야 합니다.
예를 들어 대부분의 웹 기반 전자 메일 클라이언트는 그리드를 사용하여 각 행에 전자 메일 정보(제목, 보낸 사람 등)와 함께 확인란이 포함된 각 메시지를 나열합니다. 이 인터페이스를 사용하면 사용자가 여러 메시지를 확인한 다음 선택한 메시지 삭제 단추를 클릭하여 여러 메시지를 삭제할 수 있습니다. 일괄 처리 편집 인터페이스는 사용자가 일반적으로 다양한 레코드를 편집하는 경우에 적합합니다. 사용자가 편집을 클릭하여 변경한 다음 수정해야 하는 각 레코드에 대한 업데이트를 클릭하도록 강제하는 대신 일괄 편집 인터페이스가 각 행을 편집 인터페이스로 렌더링합니다. 사용자는 변경해야 하는 행 집합을 빠르게 수정한 다음 모두 업데이트 단추를 클릭하여 이러한 변경 내용을 저장할 수 있습니다. 이 자습서 집합에서는 데이터 일괄 처리를 삽입, 편집 및 삭제하기 위한 인터페이스를 만드는 방법을 살펴봅니다.
일괄 처리 작업을 수행할 때는 다른 작업이 실패하는 동안 일괄 처리의 일부 작업이 성공할 수 있는지 여부를 결정하는 것이 중요합니다. 일괄 처리 삭제 인터페이스를 고려합니다. 첫 번째 선택한 레코드가 성공적으로 삭제되었지만 외래 키 제약 조건 위반으로 인해 두 번째 레코드가 실패하면 어떻게 될까요? 첫 번째 레코드 삭제를 롤백해야 하나요, 아니면 첫 번째 레코드가 삭제된 상태로 유지되는 것이 허용되나요?
일괄 처리 작업을 모든 단계가 성공하거나 모든 단계가 실패하는 원자성 작업으로 처리하려면 데이터베이스 트랜잭션에 대한 지원을 포함하도록 데이터 액세스 계층을 보강해야 합니다. 데이터베이스 트랜잭션은 트랜잭션의 INSERT
우산 아래에서 실행되는 , UPDATE
및 DELETE
문 집합에 대한 원자성을 보장하며 대부분의 최신 데이터베이스 시스템에서 지원하는 기능입니다.
이 자습서에서는 데이터베이스 트랜잭션을 사용하도록 DAL을 확장하는 방법을 살펴보겠습니다. 후속 자습서에서는 인터페이스를 일괄 삽입, 업데이트 및 삭제하기 위해 웹 페이지 구현을 검토합니다. 시작해 보겠습니다!
비고
일괄 처리 트랜잭션에서 데이터를 수정할 때 원자성이 항상 필요한 것은 아닙니다. 일부 시나리오에서는 일부 데이터 수정이 성공하고 웹 기반 전자 메일 클라이언트에서 전자 메일 집합을 삭제하는 경우와 같이 동일한 일괄 처리의 다른 데이터 수정이 실패하는 것이 허용될 수 있습니다. 삭제 프로세스 중간에 데이터베이스 오류가 있는 경우 오류 없이 처리된 레코드가 삭제된 상태로 유지되는 것이 좋습니다. 이러한 경우 데이터베이스 트랜잭션을 지원하도록 DAL을 수정할 필요가 없습니다. 그러나 원자성이 중요한 다른 일괄 처리 작업 시나리오가 있습니다. 고객이 한 은행 계좌에서 다른 은행 계좌로 자금을 옮기면 두 가지 작업을 수행해야 합니다. 즉, 첫 번째 계좌에서 자금을 공제한 다음 두 번째 계좌에 추가해야 합니다. 은행은 첫 번째 단계가 성공하는 것을 신경 쓰지 않을 지 모르지만 두 번째 단계는 실패하지만 고객은 당연히 화가 났을 것입니다. 다음 세 가지 자습서에서 빌드할 인터페이스를 일괄 삽입, 업데이트 및 삭제하는 데 사용할 계획이 없는 경우에도 데이터베이스 트랜잭션을 지원하기 위해 이 자습서를 통해 작업하고 DAL의 향상된 기능을 구현하는 것이 좋습니다.
트랜잭션 개요
대부분의 데이터베이스에는 여러 데이터베이스 명령을 단일 논리 작업 단위로 그룹화할 수 있는 트랜잭션 지원이 포함됩니다. 트랜잭션을 구성하는 데이터베이스 명령은 원자성으로 보장되므로 모든 명령이 실패하거나 모두 성공합니다.
일반적으로 트랜잭션은 다음 패턴을 사용하여 SQL 문을 통해 구현됩니다.
- 트랜잭션의 시작을 나타냅니다.
- 트랜잭션을 구성하는 SQL 문을 실행합니다.
- 2단계의 문 중 하나에 오류가 있는 경우 트랜잭션을 롤백합니다.
- 2단계의 모든 문이 오류 없이 완료되면 트랜잭션을 커밋합니다.
트랜잭션을 만들고, 커밋하고, 롤백하는 데 사용되는 SQL 문은 SQL 스크립트를 작성하거나 저장 프로시저를 만들 때 수동으로 입력하거나 네임스페이스의 ADO.NET 또는 클래스를 사용하는 프로그래밍 방법을 통해 입력할 수 있습니다System.Transactions
. 이 자습서에서는 ADO.NET 사용하여 트랜잭션 관리만 살펴봅니다. 이후 자습서에서는 데이터 액세스 계층에서 저장 프로시저를 사용하는 방법을 살펴보겠습니다. 이때 트랜잭션을 만들고, 롤백하고, 커밋하기 위한 SQL 문을 살펴봅니다. 그 동안 자세한 내용은 SQL Server 저장 프로시저의 트랜잭션 관리를 참조하세요 .
비고
네임스페이스의 클래스 TransactionScope
를 통해 개발자는 트랜잭션 범위 내에서 일련의 문을 프로그래밍 방식으로 래핑할 수 있으며, Microsoft SQL Server 데이터베이스, Oracle 데이터베이스 및 웹 서비스와 같이 서로 다른 두 데이터베이스 또는 다른 유형의 데이터 저장소와 같은 여러 원본이 포함된 복잡한 트랜잭션에 대한 지원을 포함합니다. ADO.NET 데이터베이스 트랜잭션에 더 구체적이고 대부분의 경우 리소스 집약적이 훨씬 적기 때문에 클래스 대신 TransactionScope
이 자습서에 ADO.NET 트랜잭션을 사용하기로 결정했습니다. 또한 특정 시나리오에서 클래스는 TransactionScope
MSDTC(Microsoft Distributed Transaction Coordinator)를 사용합니다. MSDTC를 둘러싼 구성, 구현 및 성능 문제로 인해 이러한 자습서의 범위를 벗어나는 특수하고 고급 항목이 됩니다.
ADO.NET SqlClient 공급자를 사용하는 경우 개체를 반환하는 클래스 메서드SqlConnection
호출BeginTransaction
을 통해 트랜잭션이 SqlTransaction
시작됩니다. 트랜잭션을 구성하는 데이터 수정 문은 블록 내에 try...catch
배치됩니다. 문장에서 try
오류가 발생하면 실행이 catch
블록으로 전송되며, 여기서 트랜잭션은 SqlTransaction
개체의 Rollback
메서드를 통해 롤백될 수 있습니다. 모든 문이 성공적으로 완료되면, 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
기본적으로 Typed DataSet의 TableAdapters는 트랜잭션을 사용하지 않습니다. 트랜잭션에 대한 지원을 제공하려면 위의 패턴을 사용하여 트랜잭션 범위 내에서 일련의 데이터 수정 문을 수행하는 추가 메서드를 포함하도록 TableAdapter 클래스를 보강해야 합니다. 2단계에서는 부분 클래스를 사용하여 이러한 메서드를 추가하는 방법을 살펴보겠습니다.
1단계: 일괄 처리된 데이터 웹 페이지 작업 만들기
데이터베이스 트랜잭션을 지원하기 위해 DAL을 보강하는 방법을 살펴보기 전에 먼저 이 자습서에 필요한 ASP.NET 웹 페이지와 다음 세 가지 웹 페이지를 만들어 보겠습니다. 먼저 명명된 BatchData
새 폴더를 추가한 다음 다음 ASP.NET 페이지를 추가하여 각 페이지를 마스터 페이지와 Site.master
연결합니다.
Default.aspx
Transactions.aspx
BatchUpdate.aspx
BatchDelete.aspx
BatchInsert.aspx
그림 1: SqlDataSource-Related 자습서의 ASP.NET 페이지 추가
다른 폴더와 Default.aspx
마찬가지로 사용자 컨트롤을 SectionLevelTutorialListing.ascx
사용하여 섹션 내의 자습서를 나열합니다. 따라서 솔루션 탐색기에서 이 사용자 컨트롤 Default.aspx
을 페이지의 디자인 보기로 끌어 추가하십시오.
그림 2: 사용자 정의 컨트롤 추가 SectionLevelTutorialListing.ascx
Default.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 직접 패턴 메서드는 전달된 값을 사용하여 적절한 InsertCommand
또는 UpdateCommand
DeleteCommand
문을 실행합니다.
사용되는 업데이트 패턴에 관계없이 TableAdapters 자동 생성 메서드는 트랜잭션을 사용하지 않습니다. 기본적으로 TableAdapter에서 수행하는 각 삽입, 업데이트 또는 삭제는 단일 개별 작업으로 처리됩니다. 예를 들어 BLL의 일부 코드에서 DB-Direct 패턴을 사용하여 데이터베이스에 10개의 레코드를 삽입한다고 상상해 보십시오. 이 코드는 TableAdapter 메서드 Insert
를 10번 호출합니다. 처음 다섯 개의 삽입이 성공하지만 여섯 번째 삽입에서 예외가 발생하면 처음 5개의 삽입된 레코드가 데이터베이스에 유지됩니다. 마찬가지로 Batch 업데이트 패턴을 사용하여 DataTable에서 삽입, 업데이트 및 삭제된 행을 삽입, 업데이트 및 삭제하는 경우 처음 몇 가지 수정이 성공했지만 나중에 오류가 발생한 경우 완료된 이전 수정 내용은 데이터베이스에 유지됩니다.
특정 시나리오에서는 일련의 수정에서 원자성을 보장하려고 합니다. 이 작업을 수행하려면 트랜잭션에서 InsertCommand
, UpdateCommand
, 및 DeleteCommand
를 실행하는 새로운 메서드를 추가하여 TableAdapter를 수동으로 확장해야 합니다.
데이터 액세스 계층 만들기에서 부분 클래스를 사용하여 Typed DataSet 내에서 DataTable의 기능을 확장하는 방법을 살펴보았습니다. 이 기술은 TableAdapters와 함께 사용할 수도 있습니다.
Northwind.xsd
폴더의 App_Code
하위 폴더에 형식화된 데이터 세트 DAL
가 있습니다. 폴더에 하위 폴더를 DAL
만들고 명명 TransactionSupport
ProductsTableAdapter.TransactionSupport.vb
된 새 클래스 파일을 추가합니다(그림 4 참조). 이 파일은 트랜잭션을 사용하여 데이터 수정을 수행하는 메서드를 포함하는 ProductsTableAdapter
의 부분적 구현을 담고 있습니다.
그림 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
키워드는 컴파일러에게 해당 클래스의 내부에 추가된 멤버들이 ProductsTableAdapter
네임스페이스의 NorthwindTableAdapters
클래스에 추가되어야 한다는 것을 알립니다.
Imports System.Data.SqlClient
파일 맨 위에 있는 문장을 주의하세요. TableAdapter는 SqlClient 공급자를 사용하도록 구성되었으므로 내부적으로 개체를 SqlDataAdapter
사용하여 해당 명령을 데이터베이스에 실행합니다. 따라서 우리는 SqlTransaction
클래스를 사용하여 트랜잭션을 시작하고, 이를 커밋하거나 롤백해야 합니다. Microsoft SQL Server 이외의 데이터 저장소를 사용하는 경우 적절한 공급자를 사용해야 합니다.
이러한 메서드는 트랜잭션을 시작, 롤백 및 커밋하는 데 필요한 구성 요소를 제공합니다. 이러한 클래스는 Public
로 표시되어 있어 DAL의 다른 클래스 내에서, 또는 BLL과 같은 아키텍처의 다른 계층 내에서 사용할 수 있습니다.
BeginTransaction
는 TableAdapter의 내부 SqlConnection
(필요한 경우)를 열고 트랜잭션을 시작하고 속성에 Transaction
할당한 다음 트랜잭션을 내부 SqlDataAdapter
SqlCommand
개체에 연결합니다.
CommitTransaction
와 RollbackTransaction
는 Transaction
객체의 Commit
및 Rollback
메서드를 각각 호출한 후, 내부 Connection
객체를 닫습니다.
3단계: 트랜잭션의 우산 아래에서 데이터를 업데이트하고 삭제하는 메서드 추가
이러한 메서드를 설정한 후, 우리는 트랜잭션 내에서 일련의 명령을 수행하는 ProductsDataTable
또는 BLL에 새로운 메서드를 추가할 준비가 되었습니다. 다음 메서드는 Batch 업데이트 패턴을 사용하여 트랜잭션을 사용하여 인스턴스를 ProductsDataTable
업데이트합니다. 메서드를 호출하여 트랜잭션을 시작하고, 그런 다음 블록을 사용하여 데이터 수정 문을 실행합니다. 개체 Adapter
의 Update
메서드를 호출할 때 예외가 발생하면, 트랜잭션이 롤백되고 예외가 다시 throw되는 블록으로 실행이 catch
로 전송됩니다. 이 메서드는 제공된 Update
의 행을 열거하고, 필요한 ProductsDataTable
, InsertCommand
, 및 UpdateCommand
를 수행하여 Batch Update 패턴을 구현합니다. 이러한 명령 중 하나에 오류가 발생하면 트랜잭션이 롤백되어 트랜잭션 수명 동안 이전에 수정한 내용을 실행 취소합니다. 명령문이 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
, Me.BeginTransaction()
, Me.CommitTransaction()
에서의 키워드 Me.RollbackTransaction()
를 Adapter
로 바꿔야 합니다(참고로 Adapter
는 ProductsBLL
유형의 ProductsTableAdapter
안에 있는 속성의 이름입니다).
이 메서드는 UpdateWithTransaction
Batch 업데이트 패턴을 사용하지만 다음 메서드와 같이 트랜잭션 범위 내에서 일련의 DB-Direct 호출을 사용할 수도 있습니다. 메서드는 삭제할 DeleteProductsWithTransaction
을 타입 List(Of T)
의 Integer
입력으로 허용합니다ProductID
. 메서드는 호출을 통해 BeginTransaction
트랜잭션을 시작하고, 그 후 Try
블록에서 제공된 목록을 반복하여 각 Delete
값에 대해 DB-Direct 패턴 ProductID
메서드를 호출합니다.
Delete
호출이 실패하면 트랜잭션이 롤백되고, Catch
블록으로 제어가 전송되어 예외가 다시 발생합니다. 모든 호출이 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
원자성 작업으로 처리할 수 있습니다. 그러나 다른 데이터베이스 테이블에 대한 여러 수정을 원자성으로 수행해야 하는 경우 어떻게 해야 할까요? 예를 들어 범주를 삭제할 때 먼저 현재 제품을 다른 범주에 다시 할당하려고 할 수 있습니다. 제품을 다시 할당하고 범주를 삭제하는 두 단계는 원자성 작업으로 실행되어야 합니다.
ProductsTableAdapter
그러나 테이블을 수정하는 Products
메서드만 포함되며 CategoriesTableAdapter
테이블을 수정하는 Categories
메서드만 포함됩니다. 그렇다면 트랜잭션이 두 TableAdapters를 어떻게 포괄할 수 있을까요?
한 가지 옵션은 CategoriesTableAdapter
에 DeleteCategoryAndReassignProducts(categoryIDtoDelete, reassignToCategoryID)
이라는 메서드를 추가하고, 그 메서드가 저장 프로시저를 호출하여 제품을 다시 할당하고, 저장 프로시저 내에 정의된 트랜잭션의 범위 내에서 범주를 삭제하도록 하는 것입니다. 이후 자습서에서는 저장 프로시저에서 트랜잭션을 시작, 커밋 및 롤백하는 방법을 살펴보겠습니다.
또 다른 옵션은 메서드를 포함하는 DAL에서 도우미 클래스를 만드는 것입니다 DeleteCategoryAndReassignProducts(categoryIDtoDelete, reassignToCategoryID)
. 이 메서드는 CategoriesTableAdapter
및 ProductsTableAdapter
의 인스턴스를 생성한 다음, 이 두 TableAdapters의 Connection
속성을 동일한 SqlConnection
인스턴스로 설정합니다. 이 시점에서는 두 TableAdapters 중 하나가 BeginTransaction
을 사용하여 트랜잭션을 시작하게 됩니다. 제품을 다시 할당하고 범주를 삭제하기 위한 TableAdapters 메서드는 트랜잭션이 커밋되거나 필요에 따라 롤백된 블록에서 Try...Catch
호출됩니다.
4단계: 비즈니스 논리 계층에 메서드 추가UpdateWithTransaction
3단계에서는 DAL에 메서드를 UpdateWithTransaction
추가 ProductsTableAdapter
했습니다. 해당 메서드를 BLL에 추가해야 합니다. 프레젠테이션 계층이 DAL에 직접 접근하여 UpdateWithTransaction
메서드를 호출할 수 있지만, 이 자습서에서는 DAL을 프레젠테이션 계층에서 분리하는 계층화된 아키텍처를 정의하기 위해 노력했습니다. 따라서 이 방법을 계속 진행해야 합니다.
ProductsBLL
클래스 파일을 열고 해당 DAL 메서드를 호출하는 메서드 UpdateWithTransaction
를 추가합니다. 이제 3단계에서 ProductsBLL
추가된 두 가지 UpdateWithTransaction
새 메서드, DeleteProductsWithTransaction
즉 방금 추가한 메서드가 있습니다.
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 페이지 코드 숨김 클래스에서 DataObjectMethodAttribute
직접 이러한 메서드를 호출하기 때문에 클래스의 대부분의 다른 메서드에 할당된 특성을 포함하지 ProductsBLL
않습니다.
DataObjectMethodAttribute
ObjectDataSource의 데이터 원본 구성 마법사 및 어떤 탭(SELECT, UPDATE, INSERT 또는 DELETE)에 표시되어야 하는 메서드에 플래그를 지정하는 데 사용됩니다. GridView에는 일괄 처리 편집 또는 삭제에 대한 기본 제공 지원이 없기 때문에 코드 없는 선언적 접근 방식을 사용하는 대신 프로그래밍 방식으로 이러한 메서드를 호출해야 합니다.
5단계: 프레젠테이션 층에서 데이터베이스 데이터를 원자적으로 업데이트
트랜잭션이 레코드 일괄 처리를 업데이트할 때 미치는 영향을 설명하기 위해 GridView의 모든 제품을 나열하고 클릭할 때 제품 값을 다시 할당하는 단추 웹 컨트롤을 포함하는 사용자 인터페이스를 CategoryID
만들어 보겠습니다. 특히 범주 재할당이 진행되므로 처음 여러 제품에 유효한 CategoryID
값이 할당되고 다른 제품에는 존재하지 않는 CategoryID
값이 의도적으로 할당됩니다. 기존 범주CategoryID
와 일치하지 않는 제품 CategoryID
으로 데이터베이스를 업데이트하려고 하면 외래 키 제약 조건 위반이 발생하고 예외가 발생합니다. 이 예제에서 볼 수 있는 것은 트랜잭션을 사용할 때 외래 키 제약 조건 위반에서 발생한 예외로 인해 이전의 유효한 CategoryID
변경 내용이 롤백된다는 것입니다. 그러나 트랜잭션을 사용하지 않는 경우 초기 범주에 대한 수정 내용은 그대로 유지됩니다.
먼저 폴더에서 Transactions.aspx
BatchData
페이지를 열고 도구 상자에서 디자이너로 GridView를 끕니다.
ID
를 Products
로 설정하고 스마트 태그에서 새 ObjectDataSource인 ProductsDataSource
에 바인딩합니다. 클래스 ProductsBLL
의 메서드에서 해당 데이터를 끌어오도록 ObjectDataSource를 GetProducts
구성합니다. 읽기 전용인 GridView이므로 UPDATE, INSERT 및 DELETE 탭의 드롭다운 목록을 (없음)으로 설정하고 마침을 클릭합니다.
그림 5: Class s ProductsBLL
메서드를 사용하도록 GetProducts
ObjectDataSource 구성(전체 크기 이미지를 보려면 클릭)
그림 6: UPDATE, INSERT 및 DELETE 탭의 Drop-Down 목록을 (없음)으로 설정합니다(전체 크기 이미지를 보려면 클릭).
데이터 원본 구성 마법사를 완료한 후 Visual Studio는 제품 데이터 필드에 대한 BoundFields 및 CheckBoxField를 만듭니다. 제거하고 이러한 모든 필드의 ProductID
, ProductName
, CategoryID
, CategoryName
을 제외하고 ProductName
및 CategoryName
BoundFields HeaderText
속성을 각각 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 위에 세 개의 단추 웹 컨트롤을 추가합니다. 첫 번째 단추의 텍스트 속성을 새로 고침 그리드로 설정하고, 두 번째는 범주 수정(WITH TRANSACTION)으로 설정하고, 세 번째 속성은 범주 수정(WITHOUT 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에 표시된 스크린샷과 유사하게 표시됩니다.
그림 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 메서드 Products
를 호출 DataBind
하여 데이터를 GridView에 다시 바인딩하기만 하면 됩니다.
두 번째 이벤트 처리기는 제품을 CategoryID
다시 할당하고 BLL의 새 트랜잭션 메서드를 사용하여 트랜잭션의 우산 아래 데이터베이스 업데이트를 수행합니다. 각 제품의 CategoryID
는 임의로 해당 제품의 ProductID
과 동일한 값으로 설정됩니다. 이러한 제품에는 유효한 ProductID
s에 매핑되는 값이 있으므로 CategoryID
처음 몇 가지 제품에는 잘 작동합니다. 그러나 ProductID
이 너무 커지면, 이 우연한 겹침은 ProductID
과 CategoryID
의 경우 더 이상 적용되지 않습니다.
세 번째 Click
이벤트 처리기는 동일한 방식으로 제품을 CategoryID
업데이트하지만 기본 ProductsTableAdapter
메서드를 사용하여 데이터베이스에 Update
업데이트를 보냅니다. 이 Update
메서드는 트랜잭션 내에서 일련의 명령을 래핑하지 않으므로 처음 발생한 외래 키 제약 조건 위반 오류가 지속되기 전에 변경 내용이 적용됩니다.
이 동작을 보여 주려면 브라우저를 통해 이 페이지를 방문하세요. 처음에는 그림 8과 같이 데이터의 첫 번째 페이지가 표시됩니다. 다음으로 범주 수정(거래와 함께) 단추를 클릭합니다. 이렇게 하면 포스트백이 발생하고 모든 제품 CategoryID
값을 업데이트하려고 시도하지만 외래 키 제약 조건 위반이 발생합니다(그림 9 참조).
그림 8: 제품이 Pageable GridView에 표시됩니다(전체 크기 이미지를 보려면 클릭).
그림 9: 범주를 다시 할당하면 외래 키 제약 조건 위반이 발생합니다(전체 크기 이미지를 보려면 클릭).
이제 브라우저의 뒤로 단추를 누른 다음 그리드 새로 고침 단추를 클릭합니다. 데이터를 새로 고치는 경우 그림 8과 정확히 동일한 출력이 표시됩니다. 즉, 일부 제품이 CategoryID
법적 값으로 변경되고 데이터베이스에서 업데이트되었지만 외래 키 제약 조건 위반이 발생했을 때 롤백되었습니다.
이제 범주 수정(트랜잭션 없음) 단추를 클릭해 봅니다. 이로 인해 동일한 외래 키 제약 조건 위반 오류가 발생하지만(그림 9 참조) 이번에는 값이 CategoryID
법적 값으로 변경된 제품은 롤백되지 않습니다. 브라우저의 뒤로 단추를 누린 다음 그리드 새로 고침 단추를 누릅니다. 그림 10에서 보여 주듯이 처음 CategoryID
8개 제품의 번호가 재할당되었습니다. 예를 들어 그림 8에서 장씨는 1을 가졌 CategoryID
지만 그림 10에서는 2로 재할당되었습니다.
그림 10: 일부 제품 값이 업데이트되었지만 다른 제품 CategoryID
값은 업데이트되지 않았습니다(전체 크기 이미지를 보려면 클릭).
요약
기본적으로 TableAdapter의 메서드는 트랜잭션 범위 내에서 실행된 데이터베이스 문을 래핑하지 않지만 약간의 작업으로 트랜잭션을 만들고, 커밋하고, 롤백하는 메서드를 추가할 수 있습니다. 이 자습서에서는 클래스에서 ProductsTableAdapter
세 가지 BeginTransaction
CommitTransaction
RollbackTransaction
메서드를 만들었습니다. 블록과 함께 이러한 메서드를 Try...Catch
사용하여 일련의 데이터 수정 문을 원자성으로 만드는 방법을 알아보았습니다. 특히 Batch 업데이트 패턴을 사용하여 제공된 UpdateWithTransaction
행에 필요한 수정을 수행하는 메서드ProductsTableAdapter
를 만들었습니다ProductsDataTable
. 또한 BLL의 DeleteProductsWithTransaction
클래스에 메서드를 추가 ProductsBLL
했습니다. 이 메서드는 값을 입력으로 허용 List
ProductID
하고 각각Delete
에 대한 DB-Direct 패턴 메서드 ProductID
를 호출합니다. 두 방법 모두 트랜잭션을 만든 다음 블록 내에서 Try...Catch
데이터 수정 문을 실행하는 것으로 시작합니다. 예외가 발생하면 트랜잭션이 롤백되고, 그렇지 않으면 커밋됩니다.
5단계에서는 트랜잭션 일괄 업데이트와 트랜잭션 사용을 소홀히 한 일괄 업데이트의 효과를 보여 줍니다. 다음 세 자습서에서는 이 자습서에 마련된 기초를 기반으로 하여 일괄 업데이트, 삭제 및 삽입을 수행하기 위한 사용자 인터페이스를 만듭니다.
행복한 프로그래밍!
추가 읽기
이 자습서에서 설명하는 항목에 대한 자세한 내용은 다음 리소스를 참조하세요.
작성자 정보
7개의 ASP/ASP.NET 책의 저자이자 4GuysFromRolla.com 창립자인 Scott Mitchell은 1998년부터 Microsoft 웹 기술을 연구해 왔습니다. Scott은 독립 컨설턴트, 트레이너 및 작가로 일합니다. 그의 최신 책은 Sams Teach Yourself ASP.NET 2.0 in 24 Hours입니다. 그에게 mitchell@4GuysFromRolla.com으로 연락할 수 있습니다.
특별히 감사드립니다.
이 자습서 시리즈는 많은 유용한 검토자가 검토했습니다. 이 자습서의 수석 검토자는 데이브 가드너, 힐튼 기제나우, 테레사 머피였습니다. 예정된 MSDN 문서를 검토하는 데 관심이 있으신가요? 그렇다면 mitchell@4GuysFromRolla.com으로 메시지를 보내 주세요.