SqlDataSource를 사용하여 낙관적 동시성 구현(VB)Implementing Optimistic Concurrency with the SqlDataSource (VB)

Scott Mitchellby Scott Mitchell

샘플 앱 다운로드 또는 PDF 다운로드Download Sample App or Download PDF

이 자습서에서는 낙관적 동시성 제어의 필수 사항을 검토 한 다음 SqlDataSource 컨트롤을 사용 하 여 구현 하는 방법을 탐색 합니다.In this tutorial we review the essentials of optimistic concurrency control and then explore how to implement it using the SqlDataSource control.

소개Introduction

앞의 자습서에서는 삽입, 업데이트 및 삭제 기능을 SqlDataSource 컨트롤에 추가 하는 방법을 살펴보았습니다.In the preceding tutorial we examined how to add inserting, updating, and deleting capabilities to the SqlDataSource control. 간단히 말해서 이러한 기능을 제공 하려면 컨트롤의 InsertCommand, UpdateCommand또는 DeleteCommand 속성에서 해당 하는 INSERT, UPDATE또는 DELETE SQL 문을 InsertParameters, UpdateParametersDeleteParameters 컬렉션의 적절 한 매개 변수와 함께 지정 해야 합니다.In short, to provide these features we needed to specify the corresponding INSERT, UPDATE, or DELETE SQL statement in the control s InsertCommand, UpdateCommand, or DeleteCommand properties, along with the appropriate parameters in the InsertParameters, UpdateParameters, and DeleteParameters collections. 이러한 속성 및 컬렉션을 수동으로 지정할 수 있지만 데이터 원본 구성 마법사의 고급 단추를 사용 하면 SELECT 문을 기반으로 이러한 문을 자동으로 만들 수 있는 INSERT, UPDATEDELETE 문 생성 확인란이 제공 됩니다.While these properties and collections can be specified manually, the Configure Data Source wizard s Advanced button offers a Generate INSERT, UPDATE, and DELETE statements checkbox that will auto-create these statements based on the SELECT statement.

INSERT, UPDATEDELETE 문 생성 확인란을 사용 하는 고급 SQL 생성 옵션 대화 상자에는 낙관적 동시성 사용 옵션이 포함 되어 있습니다 (그림 1 참조).Along with the Generate INSERT, UPDATE, and DELETE statements checkbox, the Advanced SQL Generation Options dialog box includes a Use optimistic concurrency option (see Figure 1). 이 확인란을 선택 하면 사용자가 모눈에 데이터를 마지막으로 로드 한 이후 기본 데이터베이스 데이터가 수정 되지 않은 경우에만 자동 생성 된 UPDATEDELETE 문의 WHERE 절이 업데이트 또는 삭제를 수행 하도록 수정 됩니다.When checked, the WHERE clauses in the autogenerated UPDATE and DELETE statements are modified to only perform the update or delete if the underlying database data hasn't been modified since the user last loaded the data into the grid.

고급 SQL 생성 옵션 대화 상자에서 낙관적 동시성 지원을 추가할 수 있습니다.

그림 1: 고급 SQL 생성 옵션 대화 상자에서 낙관적 동시성 지원을 추가할 수 있습니다.Figure 1: You Can Add Optimistic Concurrency Support from the Advanced SQL Generation Options Dialog Box

낙관적 동시성 구현 자습서로 돌아가서 낙관적 동시성 제어의 기본 사항과 ObjectDataSource에 추가 하는 방법을 살펴보았습니다.Back in the Implementing Optimistic Concurrency tutorial we examined the fundamentals of optimistic concurrency control and how to add it to the ObjectDataSource. 이 자습서에서는 낙관적 동시성 제어의 필수 사항을 재손질 하 고 SqlDataSource를 사용 하 여 구현 하는 방법을 탐색 합니다.In this tutorial we'll retouch on the essentials of optimistic concurrency control and then explore how to implement it using the SqlDataSource.

낙관적 동시성에 대 한 요약A Recap of Optimistic Concurrency

여러 사용자가 동일한 데이터를 편집 하거나 삭제할 수 있도록 하는 웹 응용 프로그램의 경우 한 사용자가 실수로 다른 변경 내용을 덮어쓸 수 있습니다.For web applications that allow multiple, simultaneous users to edit or delete the same data, there exists a possibility that one user may accidentally overwrite another s changes. 낙관적 동시성 구현 자습서에서 다음 예제를 제공 했습니다.In the Implementing Optimistic Concurrency tutorial I provided the following example:

Jisun 및 Sam 이라는 두 사용자가 모두 응용 프로그램의 페이지를 방문 하 여 방문자가 GridView 컨트롤을 통해 제품을 업데이트 하 고 삭제할 수 있다고 가정 합니다.Imagine that two users, Jisun and Sam, were both visiting a page in an application that allowed visitors to update and delete products through a GridView control. 둘 다 동시에 Chai에 대 한 편집 단추를 클릭 합니다.Both click the Edit button for Chai around the same time. Jisun은 제품 이름을 Chai Tea로 변경 하 고 업데이트 단추를 클릭 합니다.Jisun changes the product name to Chai Tea and clicks the Update button. Net result는 데이터베이스로 전송 되는 UPDATE 문으로, 모든 제품의 업데이트 가능한 필드 ProductName를 설정 합니다 (Jisun은 한 필드만 업데이트 된 경우에도).The net result is an UPDATE statement that is sent to the database, which sets all of the product s updateable fields (even though Jisun only updated one field, ProductName). 이 시점에서 데이터베이스에는이 특정 제품에 대 한 Chai Tea, category 음료, 공급자 Exotic Liquids 등의 값이 있습니다.At this point in time, the database has the values Chai Tea, the category Beverages, the supplier Exotic Liquids, and so on for this particular product. 그러나 Sam s에서 GridView는 편집 가능한 GridView 행의 제품 이름을 Chai으로 표시 합니다.However, the GridView on Sam s screen still shows the product name in the editable GridView row as Chai. Jisun 변경이 커밋된 후 몇 초 후에 Sam이 범주를 조미료로 업데이트 하 고 업데이트를 클릭 합니다.A few seconds after Jisun s changes have been committed, Sam updates the category to Condiments and clicks Update. 이로 인해 UPDATE 문이 제품 이름을 Chai, CategoryID 해당 조미료 범주 ID로 설정 하는 데이터베이스로 전송 됩니다.This results in an UPDATE statement sent to the database that sets the product name to Chai, the CategoryID to the corresponding Condiments category ID, and so on. 제품 이름에 대 한 jisun 변경 내용을 덮어썼습니다.Jisun s changes to the product name have been overwritten.

그림 2에서는이 상호 작용을 보여 줍니다.Figure 2 illustrates this interaction.

두 사용자가 레코드를 동시에 업데이트 하는 경우 한 사용자가 다른 사용자의 변경 내용을 덮어쓸 가능성이 있습니다.When Two Users Simultaneously Update a Record There s Potential for One User s Changes to Overwrite the Other s

그림 2: 두 사용자가 동시에 레코드를 업데이트 하는 경우 한 사용자가 변경 하 여 다른를 덮어쓸 가능성이 있습니다 (전체 크기 이미지를 보려면 클릭).Figure 2: When Two Users Simultaneously Update a Record There s Potential for One User s Changes to Overwrite the Other s (Click to view full-size image)

펼치기에서이 시나리오를 방지 하려면 동시성 제어 의 형태를 구현 해야 합니다.To prevent this scenario from unfolding, a form of concurrency control must be implemented. 낙관적 동시성 이 자습서에서는 동시성 충돌이 발생할 수 있는 것으로 가정 하 고 이러한 충돌은 거의 발생 하지 않습니다.Optimistic concurrency the focus of this tutorial works on the assumption that while there may be concurrency conflicts every now and then, the vast majority of the time such conflicts won't arise. 따라서 충돌이 발생 하면 낙관적 동시성 제어는 다른 사용자가 동일한 데이터를 수정 했기 때문에 변경 내용을 저장할 수 있음을 사용자에 게 알립니다.Therefore, if a conflict does arise, optimistic concurrency control simply informs the user that their changes can t be saved because another user has modified the same data.

Note

많은 동시성 충돌이 있거나 이러한 충돌이 지속할 않는 것으로 간주 되는 응용 프로그램의 경우 비관적 동시성 제어를 대신 사용할 수 있습니다.For applications where it is assumed that there will be many concurrency conflicts or if such conflicts are not tolerable, then pessimistic concurrency control can be used instead. 비관적 동시성 제어에 대 한 자세한 설명은 낙관적 동시성 구현 자습서를 다시 참조 하세요.Refer back to the Implementing Optimistic Concurrency tutorial for a more thorough discussion on pessimistic concurrency control.

낙관적 동시성 제어는 업데이트 또는 삭제 하는 레코드의 값이 업데이트 또는 삭제 프로세스가 시작 된 것과 동일한 지 확인 하는 방식으로 작동 합니다.Optimistic concurrency control works by ensuring that the record being updated or deleted has the same values as it did when the updating or deleting process started. 예를 들어 편집 가능한 GridView에서 편집 단추를 클릭 하면 레코드의 값이 데이터베이스에서 읽어서 텍스트 상자와 다른 웹 컨트롤에 표시 됩니다.For example, when clicking the Edit button in an editable GridView, the record s values are read from the database and displayed in TextBoxes and other Web controls. 이러한 원래 값은 GridView에 의해 저장 됩니다.These original values are saved by the GridView. 나중에 사용자가 변경 하 고 업데이트 단추를 클릭 한 후에는 사용 되는 UPDATE 문이 원래 값과 새 값을 고려해 야 하며, 사용자가 편집을 시작한 원래 값이 데이터베이스에 있는 값과 동일한 경우에만 기본 데이터베이스 레코드를 업데이트 해야 합니다.Later, after the user makes her changes and clicks the Update button, the UPDATE statement used must take into account the original values plus the new values and only update the underlying database record if the original values that the user started editing are identical to the values still in the database. 그림 3에서는 이러한 이벤트 시퀀스를 보여 줍니다.Figure 3 depicts this sequence of events.

업데이트 또는 삭제에 대 한 성공 하려면 원래 값이 현재 데이터베이스 값과 같아야 합니다.For the Update or Delete to Succeed, the Original Values Must Be Equal to the Current Database Values

그림 3: 업데이트 또는 삭제가 성공 하려면 원래 값이 현재 데이터베이스 값과 같아야 합니다 (전체 크기 이미지를 보려면 클릭).Figure 3: For the Update or Delete to Succeed, the Original Values Must Be Equal to the Current Database Values (Click to view full-size image)

낙관적 동시성을 구현 하는 방법에는 여러 가지가 있습니다 (여러 옵션에 대 한 간략 한 개요는 Peter Bromberg낙관적 동시성 업데이트 논리 참조).There are various approaches to implementing optimistic concurrency (see Peter A. Bromberg's Optimistic Concurrency Updating Logic for a brief look at a number of options). SqlDataSource에서 사용 하는 기술 (데이터 액세스 계층에 사용 되는 ADO.NET 형식화 된 데이터 집합)은 모든 원래 값의 비교를 포함 하도록 WHERE 절을 보강 합니다.The technique used by the SqlDataSource (as well as by the ADO.NET Typed DataSets used in our Data Access Layer) augments the WHERE clause to include a comparison of all of the original values. 예를 들어 다음 UPDATE 문은 현재 데이터베이스 값이 GridView의 레코드를 업데이트할 때 원래 검색 된 값과 동일한 경우에만 제품의 이름과 가격을 업데이트 합니다.The following UPDATE statement, for example, updates the name and price of a product only if the current database values are equal to the values that were originally retrieved when updating the record in the GridView. @ProductName@UnitPrice 매개 변수는 사용자가 입력 한 새 값을 포함 하는 반면 @original_ProductName@original_UnitPrice에는 편집 단추를 클릭 했을 때 원래 GridView로 로드 된 값이 포함 됩니다.The @ProductName and @UnitPrice parameters contain the new values entered by the user, whereas @original_ProductName and @original_UnitPrice contain the values that were originally loaded into the GridView when the Edit button was clicked:

UPDATE Products SET
    ProductName = @ProductName,
    UnitPrice = @UnitPrice
WHERE
    ProductID = @original_ProductID AND
    ProductName = @original_ProductName AND
    UnitPrice = @original_UnitPrice

이 자습서에서 볼 수 있듯이, SqlDataSource를 사용 하 여 낙관적 동시성 제어를 사용 하도록 설정 하는 것은 확인란을 선택 하는 것 만큼 간단 합니다.As we'll see in this tutorial, enabling optimistic concurrency control with the SqlDataSource is as simple as checking a checkbox.

1 단계: 낙관적 동시성을 지 원하는 SqlDataSource 만들기Step 1: Creating a SqlDataSource that Supports Optimistic Concurrency

먼저 SqlDataSource 폴더에서 OptimisticConcurrency.aspx 페이지를 엽니다.Start by opening the OptimisticConcurrency.aspx page from the SqlDataSource folder. SqlDataSource 컨트롤을 도구 상자에서 디자이너로 끌어 ID 속성이 ProductsDataSourceWithOptimisticConcurrency되도록 설정 합니다.Drag a SqlDataSource control from the Toolbox onto the Designer, settings its ID property to ProductsDataSourceWithOptimisticConcurrency. 그런 다음 컨트롤의 스마트 태그에서 데이터 소스 구성 링크를 클릭 합니다.Next, click on the Configure Data Source link from the control s smart tag. 마법사의 첫 번째 화면에서 NORTHWINDConnectionString 작업을 선택 하 고 다음을 클릭 합니다.From the first screen in the wizard, choose to work with the NORTHWINDConnectionString and click Next.

NORTHWINDConnectionString를 사용 하 여 작업 선택 합니다.Choose to Work with the NORTHWINDConnectionString

그림 4: NORTHWINDConnectionString 사용 하도록 선택 (전체 크기 이미지를 보려면 클릭)Figure 4: Choose to Work with the NORTHWINDConnectionString (Click to view full-size image)

이 예에서는 사용자가 Products 테이블을 편집할 수 있도록 하는 GridView를 추가 합니다.For this example we'll be adding a GridView that enables users to edit the Products table. 따라서 그림 5와 같이 Select 문 구성 화면의 드롭다운 목록에서 Products 테이블을 선택 하 고 ProductID, ProductName, UnitPriceDiscontinued 열을 선택 합니다.Therefore, from the Configure the Select Statement screen, choose the Products table from the drop-down list and select the ProductID, ProductName, UnitPrice, and Discontinued columns, as shown in Figure 5.

Products 테이블에서 ProductID, ProductName, UnitPrice 및 지원 되지 않는 열을 반환 합니다.From the Products Table, Return the ProductID, ProductName, UnitPrice, and Discontinued Columns

그림 5: Products 테이블에서 ProductID, ProductName, UnitPriceDiscontinued 열 반환 (전체 크기 이미지를 보려면 클릭)Figure 5: From the Products Table, Return the ProductID, ProductName, UnitPrice, and Discontinued Columns (Click to view full-size image)

열을 선택 하 고 고급 단추를 클릭 하 여 고급 SQL 생성 옵션 대화 상자를 표시 합니다.After picking the columns, click the Advanced button to bring up the Advanced SQL Generation Options dialog box. INSERT, UPDATEDELETE 문을 생성 하 고 낙관적 동시성 확인란을 선택 하 고 확인을 클릭 합니다 (스크린샷을 보려면 그림 1로 다시 참조).Check the Generate INSERT, UPDATE, and DELETE statements and Use optimistic concurrency checkboxes and click OK (refer back to Figure 1 for a screenshot). 다음, 마침을 차례로 클릭 하 여 마법사를 완료 합니다.Complete the wizard by clicking Next, then Finish.

데이터 원본 구성 마법사를 완료 한 후에는 잠시 후에 결과 DeleteCommand를 검사 하 고 속성과 DeleteParametersUpdateParameters 컬렉션을 UpdateCommand 합니다.After completing the Configure Data Source wizard, take a moment to examine the resulting DeleteCommand and UpdateCommand properties and the DeleteParameters and UpdateParameters collections. 이 작업을 수행 하는 가장 쉬운 방법은 왼쪽 아래 모서리의 원본 탭을 클릭 하 여 페이지의 선언 구문을 확인 하는 것입니다.The easiest way to do this is to click on the Source tab in the lower left corner to see the page s declarative syntax. 여기에서 UpdateCommand 값을 찾을 수 있습니다.There you will find an UpdateCommand value of:

UPDATE [Products] SET
     [ProductName] = @ProductName,
     [UnitPrice] = @UnitPrice,
     [Discontinued] = @Discontinued
WHERE
     [ProductID] = @original_ProductID AND
     [ProductName] = @original_ProductName AND
     [UnitPrice] = @original_UnitPrice AND
     [Discontinued] = @original_Discontinued

UpdateParameters 컬렉션에 7 개의 매개 변수가 있습니다.With seven parameters in the UpdateParameters collection:

<asp:SqlDataSource ID="ProductsDataSourceWithOptimisticConcurrency"
    runat="server" ...>
    <DeleteParameters>
      ...
    </DeleteParameters>
    <UpdateParameters>
        <asp:Parameter Name="ProductName" Type="String" />
        <asp:Parameter Name="UnitPrice" Type="Decimal" />
        <asp:Parameter Name="Discontinued" Type="Boolean" />
        <asp:Parameter Name="original_ProductID" Type="Int32" />
        <asp:Parameter Name="original_ProductName" Type="String" />
        <asp:Parameter Name="original_UnitPrice" Type="Decimal" />
        <asp:Parameter Name="original_Discontinued" Type="Boolean" />
    </UpdateParameters>
    ...
</asp:SqlDataSource>

마찬가지로 DeleteCommand 속성 및 DeleteParameters 컬렉션은 다음과 같습니다.Similarly, the DeleteCommand property and DeleteParameters collection should look like the following:

DELETE FROM [Products]
WHERE
     [ProductID] = @original_ProductID AND
     [ProductName] = @original_ProductName AND
     [UnitPrice] = @original_UnitPrice AND
     [Discontinued] = @original_Discontinued
<asp:SqlDataSource ID="ProductsDataSourceWithOptimisticConcurrency"
    runat="server" ...>
    <DeleteParameters>
        <asp:Parameter Name="original_ProductID" Type="Int32" />
        <asp:Parameter Name="original_ProductName" Type="String" />
        <asp:Parameter Name="original_UnitPrice" Type="Decimal" />
        <asp:Parameter Name="original_Discontinued" Type="Boolean" />
    </DeleteParameters>
    <UpdateParameters>
        ...
    </UpdateParameters>
    ...
</asp:SqlDataSource>

낙관적 동시성 사용 옵션을 선택 하 여 UpdateCommandWHERE 절을 확대 하 고 속성을 DeleteCommand 하 고 해당 매개 변수 컬렉션에 추가 매개 변수를 추가 하는 것 외에도 다음 두 가지 속성을 조정 합니다.In addition to augmenting the WHERE clauses of the UpdateCommand and DeleteCommand properties (and adding the additional parameters to the respective parameter collections), selecting the Use optimistic concurrency option adjusts two other properties:

데이터 웹 컨트롤이 SqlDataSource s Update() 또는 Delete() 메서드를 호출 하면 원래 값이 전달 됩니다.When the data Web control invokes the SqlDataSource s Update() or Delete() method, it passes in the original values. SqlDataSource s ConflictDetection 속성이 CompareAllValues로 설정 된 경우 이러한 원래 값이 명령에 추가 됩니다.If the SqlDataSource s ConflictDetection property is set to CompareAllValues, these original values are added to the command. OldValuesParameterFormatString 속성은 이러한 원래 값 매개 변수에 사용 되는 명명 패턴을 제공 합니다.The OldValuesParameterFormatString property provides the naming pattern used for these original value parameters. 데이터 원본 구성 마법사는 원래_{0}를 사용 하 고 UpdateCommand에서 원래 매개 변수의 이름을 각각, DeleteCommand 속성 및 UpdateParameters 하 고 컬렉션을 적절 하 게 DeleteParameters 합니다.The Configure Data Source wizard uses original_{0} and names each original parameter in the UpdateCommand and DeleteCommand properties and UpdateParameters and DeleteParameters collections accordingly.

Note

SqlDataSource 컨트롤 삽입 기능을 사용 하지 않으므로 InsertCommand 속성과 InsertParameters 컬렉션을 자유롭게 제거할 수 있습니다.Since we re not using the SqlDataSource control s inserting capabilities, feel free to remove the InsertCommand property and its InsertParameters collection.

NULL값 올바르게 처리Correctly HandlingNULLValues

아쉽게도 낙관적 동시성을 사용 하는 경우 데이터 원본 구성 마법사에서 자동으로 생성 된 확장 UPDATEDELETE 문은 NULL 값이 포함 된 레코드에서 작동 하지 않습니다 .Unfortunately, the augmented UPDATE and DELETE statements autogenerated by the Configure Data Source wizard when using optimistic concurrency do not work with records that contain NULL values. 그 이유를 확인 하려면 다음 UpdateCommand을 수행 하십시오.To see why, consider our SqlDataSource s UpdateCommand:

UPDATE [Products] SET
     [ProductName] = @ProductName,
     [UnitPrice] = @UnitPrice,
     [Discontinued] = @Discontinued
WHERE
     [ProductID] = @original_ProductID AND
     [ProductName] = @original_ProductName AND
     [UnitPrice] = @original_UnitPrice AND
     [Discontinued] = @original_Discontinued

Products 테이블의 UnitPrice 열에는 NULL 값이 있을 수 있습니다.The UnitPrice column in the Products table can have NULL values. 특정 레코드에 UnitPrice에 대 한 NULL 값이 있는 경우 NULL = NULL는 항상 False를 반환 하므로 WHERE 절 부분 [UnitPrice] = @original_UnitPrice항상 false로 평가 됩니다.If a particular record has a NULL value for UnitPrice, the WHERE clause portion [UnitPrice] = @original_UnitPrice will always evaluate to False because NULL = NULL always returns False. 따라서 NULL 값을 포함 하는 레코드는 UPDATEDELETEWHERE 절은 업데이트 하거나 삭제할 행을 반환 하지 않으므로 편집 하거나 삭제할 수 없습니다.Therefore, records that contain NULL values cannot be edited or deleted, as the UPDATE and DELETE statements WHERE clauses won't return any rows to update or delete.

Note

이 버그는 처음에는 2004의 6 월에 잘못 된 SQL 문을 생성 하 고 다음 버전의 ASP.NET에서 수정 하도록 예약 된 따르면에 처음으로 보고 되었습니다.This bug was first reported to Microsoft in June of 2004 in SqlDataSource Generates Incorrect SQL Statements and is reportedly scheduled to be fixed in the next version of ASP.NET.

이 문제를 해결 하려면 NULL 값을 가질 수 있는 모든 열에 대 한 UpdateCommandDeleteCommand 속성에서 WHERE 절을 수동으로 업데이트 해야 합니다.To fix this, we have to manually update the WHERE clauses in both the UpdateCommand and DeleteCommand properties for all columns that can have NULL values. 일반적으로 [ColumnName] = @original_ColumnName를로 변경 합니다.In general, change [ColumnName] = @original_ColumnName to:

(
   ([ColumnName] IS NULL AND @original_ColumnName IS NULL)
     OR
   ([ColumnName] = @original_ColumnName)
)

이 수정은 속성 창의 UpdateQuery 또는 DeleteQuery 옵션을 통해 또는 데이터 구성에서 사용자 지정 SQL 문 또는 저장 프로시저 지정 옵션의 업데이트 및 삭제 탭을 통해 선언적 태그를 통해 직접 만들 수 있습니다. 원본 마법사.This modification can be made directly through the declarative markup, via the UpdateQuery or DeleteQuery options from the Properties window, or through the UPDATE and DELETE tabs in the Specify a custom SQL statement or stored procedure option in the Configure Data Source wizard. 또한 UpdateCommandDeleteCommand s WHERE 절에서 NULL 값을 포함할 수 있는 모든 열에 대해 이러한 수정 작업을 수행 해야 합니다.Again, this modification must be made for every column in the UpdateCommand and DeleteCommand s WHERE clause that can contain NULL values.

이를 예제에 적용 하면 다음과 같은 수정 된 UpdateCommandDeleteCommand 값이 생성 됩니다.Applying this to our example results in the following modified UpdateCommand and DeleteCommand values:

UPDATE [Products] SET
     [ProductName] = @ProductName,
     [UnitPrice] = @UnitPrice,
     [Discontinued] = @Discontinued
WHERE
     [ProductID] = @original_ProductID AND
     [ProductName] = @original_ProductName AND
     (([UnitPrice] IS NULL AND @original_UnitPrice IS NULL)
        OR ([UnitPrice] = @original_UnitPrice)) AND
     [Discontinued] = @original_Discontinued
DELETE FROM [Products]
WHERE
     [ProductID] = @original_ProductID AND
     [ProductName] = @original_ProductName AND
     (([UnitPrice] IS NULL AND @original_UnitPrice IS NULL)
        OR ([UnitPrice] = @original_UnitPrice)) AND
     [Discontinued] = @original_Discontinued

2 단계: 편집 및 삭제 옵션을 사용 하 여 GridView 추가Step 2: Adding a GridView with Edit and Delete Options

SqlDataSource가 낙관적 동시성을 지원 하도록 구성 된 경우이 동시성 제어를 활용 하는 페이지에 데이터 웹 컨트롤을 추가 하는 것만 남았습니다.With the SqlDataSource configured to support optimistic concurrency, all that remains is to add a data Web control to the page that utilizes this concurrency control. 이 자습서에서는 편집 및 삭제 기능을 모두 제공 하는 GridView를 추가 해 보겠습니다.For this tutorial, let s add a GridView that provides both edit and delete functionality. 이렇게 하려면 GridView를 도구 상자에서 디자이너로 끌고 IDProducts로 설정 합니다.To accomplish this, drag a GridView from the Toolbox onto the Designer and set its ID to Products. GridView s 스마트 태그에서 1 단계에서 추가한 ProductsDataSourceWithOptimisticConcurrency SqlDataSource 컨트롤에 바인딩합니다.From the GridView s smart tag, bind it to the ProductsDataSourceWithOptimisticConcurrency SqlDataSource control added in Step 1. 마지막으로 스마트 태그에서 편집 사용 및 삭제 옵션 사용 옵션을 선택 합니다.Finally, check the Enable Editing and Enable Deleting options from the smart tag.

GridView에 GridView를 바인딩하고 편집 및 삭제를 사용 하도록 설정 합니다.Bind the GridView to the SqlDataSource and Enable Editing and Deleting

그림 6: SqlDataSource에 GridView 바인딩 및 편집 및 삭제 사용 (전체 크기 이미지를 보려면 클릭)Figure 6: Bind the GridView to the SqlDataSource and Enable Editing and Deleting (Click to view full-size image)

GridView를 추가한 후에는 ProductID BoundField를 제거 하 고 ProductName BoundField s HeaderText 속성을 Product로 변경 하 고 HeaderText 속성이 간단히 Price가 되도록 UnitPrice BoundField를 업데이트 하 여 모양을 구성 합니다.After adding the GridView, configure its appearance by removing the ProductID BoundField, changing the ProductName BoundField s HeaderText property to Product, and updating the UnitPrice BoundField so that its HeaderText property is simply Price. 이상적으로는 ProductName 값에 대해 RequiredFieldValidator를 포함 하도록 편집 인터페이스를 개선 하 고 UnitPrice 값에 대해 CompareValidator를 포함 하 여 적절 한 형식의 숫자 값을 보장 합니다.Ideally, we d enhance the editing interface to include a RequiredFieldValidator for the ProductName value and a CompareValidator for the UnitPrice value (to ensure it s a properly formatted numeric value). GridView의 편집 인터페이스를 사용자 지정 하는 방법에 대 한 자세한 내용은 데이터 수정 인터페이스 사용자 지정 자습서를 참조 하세요.Refer to the Customizing the Data Modification Interface tutorial for a more in-depth look at customizing the GridView s editing interface.

Note

Gridview에서 SqlDataSource로 전달 된 원래 값이 뷰 상태에 저장 되므로 GridView의 뷰 상태를 사용 하도록 설정 해야 합니다.The GridView s view state must be enabled since the original values passed from the GridView to the SqlDataSource are stored in view state.

GridView를 수정한 후 GridView 및 SqlDataSource 선언 태그는 다음과 같이 표시 됩니다.After making these modifications to the GridView, the GridView and SqlDataSource declarative markup should look similar to the following:

<asp:SqlDataSource ID="ProductsDataSourceWithOptimisticConcurrency"
    runat="server" ConflictDetection="CompareAllValues"
    ConnectionString="<%$ ConnectionStrings:NORTHWNDConnectionString %>"
    DeleteCommand=
        "DELETE FROM [Products]
         WHERE [ProductID] = @original_ProductID
         AND [ProductName] = @original_ProductName
         AND (([UnitPrice] IS NULL AND @original_UnitPrice IS NULL)
              OR ([UnitPrice] = @original_UnitPrice))
         AND [Discontinued] = @original_Discontinued"
    OldValuesParameterFormatString=
        "original_{0}"
    SelectCommand=
        "SELECT [ProductID], [ProductName], [UnitPrice], [Discontinued]
         FROM [Products]"
    UpdateCommand=
        "UPDATE [Products]
         SET [ProductName] = @ProductName, [UnitPrice] = @UnitPrice,
            [Discontinued] = @Discontinued
         WHERE [ProductID] = @original_ProductID
         AND [ProductName] = @original_ProductName
         AND (([UnitPrice] IS NULL AND @original_UnitPrice IS NULL)
            OR ([UnitPrice] = @original_UnitPrice))
        AND [Discontinued] = @original_Discontinued">
    <DeleteParameters>
        <asp:Parameter Name="original_ProductID" Type="Int32" />
        <asp:Parameter Name="original_ProductName" Type="String" />
        <asp:Parameter Name="original_UnitPrice" Type="Decimal" />
        <asp:Parameter Name="original_Discontinued" Type="Boolean" />
    </DeleteParameters>
    <UpdateParameters>
        <asp:Parameter Name="ProductName" Type="String" />
        <asp:Parameter Name="UnitPrice" Type="Decimal" />
        <asp:Parameter Name="Discontinued" Type="Boolean" />
        <asp:Parameter Name="original_ProductID" Type="Int32" />
        <asp:Parameter Name="original_ProductName" Type="String" />
        <asp:Parameter Name="original_UnitPrice" Type="Decimal" />
        <asp:Parameter Name="original_Discontinued" Type="Boolean" />
    </UpdateParameters>
</asp:SqlDataSource>
<asp:GridView ID="Products" runat="server"
    AutoGenerateColumns="False" DataKeyNames="ProductID"
    DataSourceID="ProductsDataSourceWithOptimisticConcurrency">
    <Columns>
        <asp:CommandField ShowDeleteButton="True" ShowEditButton="True" />
        <asp:BoundField DataField="ProductName" HeaderText="Product"
            SortExpression="ProductName" />
        <asp:BoundField DataField="UnitPrice" HeaderText="Price"
            SortExpression="UnitPrice" />
        <asp:CheckBoxField DataField="Discontinued" HeaderText="Discontinued"
            SortExpression="Discontinued" />
    </Columns>
</asp:GridView>

작동 중인 낙관적 동시성 제어를 보려면 두 브라우저 창을 열고 둘 다에서 OptimisticConcurrency.aspx 페이지를 로드 합니다.To see the optimistic concurrency control in action, open two browser windows and load the OptimisticConcurrency.aspx page in both. 두 브라우저에서 첫 번째 제품에 대 한 편집 단추를 클릭 합니다.Click on the Edit buttons for the first product in both browsers. 한 브라우저에서 제품 이름을 변경 하 고 업데이트를 클릭 합니다.In one browser, change the product name and click Update. 브라우저가 다시 게시 되 고 GridView가 미리 편집 모드로 돌아가 앞서 편집한 레코드의 새 제품 이름을 표시 합니다.The browser will postback and the GridView will return to its pre-editing mode, showing the new product name for the record just edited.

두 번째 브라우저 창에서 가격을 변경 하 고 제품 이름을 원래 값으로 그대로 두고 업데이트를 클릭 합니다.In the second browser window, change the price (but leave the product name as its original value) and click Update. 다시 게시 시 그리드는 미리 편집 모드로 반환 되지만 가격에 대 한 변경 내용은 기록 되지 않습니다.On postback, the grid returns to its pre-editing mode, but the change to the price is not recorded. 두 번째 브라우저는 이전 가격과 새 제품 이름 중 첫 번째 값과 동일한 값을 보여 줍니다.The second browser shows the same value as the first one the new product name with the old price. 두 번째 브라우저 창에서 변경한 내용이 손실 되었습니다.The changes made in the second browser window were lost. 뿐만 아니라 동시성 위반이 발생 했다는 것을 나타내는 예외 나 메시지가 없기 때문에 변경 내용이 자동으로 손실 되었습니다.Moreover, the changes were lost rather quietly, as there was no exception or message indicating that a concurrency violation just occurred.

두 번째 브라우저 창의 변경 내용이 자동으로 손실 The Changes in the Second Browser Window Were Silently Lost

그림 7: 두 번째 브라우저 창의 변경 내용이 자동으로 손실 됨 (전체 크기 이미지를 보려면 클릭)Figure 7: The Changes in the Second Browser Window Were Silently Lost (Click to view full-size image)

UPDATE 문 s WHERE 절이 모든 레코드를 필터링 하 여 행에 영향을 주지 않기 때문에 두 번째 브라우저의 변경 내용이 커밋되지 않은 이유는입니다.The reason why the second browser s changes were not committed was because the UPDATE statement s WHERE clause filtered out all records and therefore did not affect any rows. UPDATE 문을 다시 살펴보겠습니다.Let s look at the UPDATE statement again:

UPDATE [Products] SET
     [ProductName] = @ProductName,
     [UnitPrice] = @UnitPrice,
     [Discontinued] = @Discontinued
WHERE
     [ProductID] = @original_ProductID AND
     [ProductName] = @original_ProductName AND
     (([UnitPrice] IS NULL AND @original_UnitPrice IS NULL) OR
        ([UnitPrice] = @original_UnitPrice)) AND
     [Discontinued] = @original_Discontinued

두 번째 브라우저 창이 레코드를 업데이트 하면 WHERE 절에 지정 된 원래 제품 이름이 첫 번째 브라우저에서 변경 되었으므로 기존 제품 이름과 일치 하지 않습니다.When the second browser window updates the record, the original product name specified in the WHERE clause doesn t match up with the existing product name (since it was changed by the first browser). 따라서 문이 [ProductName] = @original_ProductName False를 반환 하 고 UPDATE 레코드에 영향을 주지 않습니다.Therefore, the statement [ProductName] = @original_ProductName returns False, and the UPDATE does not affect any records.

Note

Delete는 동일한 방식으로 작동 합니다.Delete works in the same manner. 두 브라우저 창을 연 상태에서 지정 된 제품을 편집 하 고 변경 내용을 저장 하 여 시작 합니다.With two browser windows open, start by editing a given product with one, and then saving its changes. 한 브라우저에 변경 내용을 저장 한 후 다른 브라우저에서 동일한 제품에 대 한 삭제 단추를 클릭 합니다.After saving the changes in the one browser, click the Delete button for the same product in the other. DELETE 문 s WHERE 절에서 원래 값이 일치 하지 않으므로 삭제는 자동으로 실패 합니다.Since the original values don t match up in the DELETE statement s WHERE clause, the delete silently fails.

두 번째 브라우저 창의 최종 사용자 관점에서 업데이트 단추를 클릭 한 후 그리드는 미리 편집 모드로 반환 되지만 변경 내용은 손실 되었습니다.From the end user s perspective in the second browser window, after clicking the Update button the grid returns to the pre-editing mode, but their changes were lost. 그러나 변경 내용이 적용 되지 않은 시각적 피드백은 없습니다.However, there s no visual feedback that their changes didn't stick. 이상적으로 사용자의 변경 내용이 동시성 위반에 대해 손실 된 경우에는이를 알리고, 표를 편집 모드로 유지 합니다.Ideally, if a user s changes are lost to a concurrency violation, we d notify them and, perhaps, keep the grid in edit mode. 이를 수행 하는 방법을 살펴보겠습니다.Let s look at how to accomplish this.

3 단계: 동시성 위반이 발생 한 경우 확인Step 3: Determining When a Concurrency Violation Has Occurred

동시성 위반으로 인해 발생 한 변경 내용이 거부 되므로 동시성 위반이 발생 했을 때 사용자에 게 경고 하는 것이 좋습니다.Since a concurrency violation rejects the changes one has made, it would be nice to alert the user when a concurrency violation has occurred. 사용자에 게 경고를 표시 하려면 ConcurrencyViolationMessage 페이지 맨 위에 레이블 웹 컨트롤을 추가 합니다. Text 속성이 다음 메시지를 표시 합니다. 다른 사용자가 동시에 업데이트 한 레코드를 업데이트 하거나 삭제 하려고 했습니다.To alert the user, let s add a Label Web control to the top of the page named ConcurrencyViolationMessage whose Text property displays the following message: You have attempted to update or delete a record that was simultaneously updated by another user. 다른 사용자의 변경 내용을 검토 한 다음 업데이트 또는 삭제를 다시 실행 하십시오.Please review the other user's changes and then redo your update or delete. 레이블 컨트롤 s CssClass 속성을 경고로 설정 합니다 .이 클래스는 빨강, 기울임꼴, 굵은 글꼴 및 크게 텍스트를 표시 하는 Styles.css에 정의 된 CSS 클래스입니다.Set the Label control s CssClass property to Warning, which is a CSS class defined in Styles.css that displays text in a red, italic, bold, and large font. 마지막으로 Visible 레이블 및 EnableViewState 속성을 False로 설정 합니다.Finally, set the Label s Visible and EnableViewState properties to False. 이렇게 하면 명시적으로 Visible 속성을 True로 설정 하는 포스트백만 제외 하 고 레이블이 숨겨집니다.This will hide the Label except for only those postbacks where we explicitly set its Visible property to True.

경고를 표시 하는 레이블 컨트롤을 페이지에 추가 Add a Label Control to the Page to Display the Warning

그림 8: 페이지에 레이블 컨트롤을 추가 하 여 경고 표시 (전체 크기 이미지를 보려면 클릭)Figure 8: Add a Label Control to the Page to Display the Warning (Click to view full-size image)

업데이트 또는 삭제를 수행 하는 경우 데이터 소스 제어에서 요청 된 업데이트 또는 삭제를 수행한 후 GridView s RowUpdatedRowDeleted 이벤트 처리기가 발생 합니다.When performing an update or delete, the GridView s RowUpdated and RowDeleted event handlers fire after its data source control has performed the requested update or delete. 이러한 이벤트 처리기에서 작업의 영향을 받은 행 수를 확인할 수 있습니다.We can determine how many rows were affected by the operation from these event handlers. 0 개 행이 영향을 받는 경우 ConcurrencyViolationMessage 레이블을 표시 하려고 합니다.If zero rows were affected, we want to display the ConcurrencyViolationMessage Label.

RowUpdatedRowDeleted 이벤트 모두에 대 한 이벤트 처리기를 만들고 다음 코드를 추가 합니다.Create an event handler for both the RowUpdated and RowDeleted events and add the following code:

Protected Sub Products_RowUpdated(sender As Object, e As GridViewUpdatedEventArgs) _
    Handles Products.RowUpdated
    If e.AffectedRows = 0 Then
        ConcurrencyViolationMessage.Visible = True
        e.KeepInEditMode = True
        ' Rebind the data to the GridView to show the latest changes
        Products.DataBind()
    End If
End Sub
Protected Sub Products_RowDeleted(sender As Object, e As GridViewDeletedEventArgs) _
    Handles Products.RowDeleted
    If e.AffectedRows = 0 Then
        ConcurrencyViolationMessage.Visible = True
    End If
End Sub

두 이벤트 처리기 모두 e.AffectedRows 속성을 확인 하 고, 0과 같으면 ConcurrencyViolationMessage 레이블 s Visible 속성을 True로 설정 합니다.In both event handlers we check the e.AffectedRows property and, if it equals 0, set the ConcurrencyViolationMessage Label s Visible property to True. 또한 RowUpdated 이벤트 처리기에서 KeepInEditMode 속성을 true로 설정 하 여 GridView에 편집 모드를 유지 하도록 지시 합니다.In the RowUpdated event handler, we also instruct the GridView to stay in edit mode by setting the KeepInEditMode property to true. 이렇게 하려면 다른 사용자의 데이터가 편집 인터페이스에 로드 되도록 데이터를 모눈에 다시 바인딩해야 합니다.In doing so, we need to rebind the data to the grid so that the other user s data is loaded into the editing interface. 이는 GridView s DataBind() 메서드를 호출 하 여 수행 됩니다.This is accomplished by calling the GridView s DataBind() method.

그림 9와 같이이 두 이벤트 처리기를 사용 하면 동시성 위반이 발생할 때마다 매우 눈에 띄는 메시지가 표시 됩니다.As Figure 9 shows, with these two event handlers, a very noticeable message is displayed whenever a concurrency violation occurs.

동시성 위반이 발생 한 경우 메시지를 표시 A Message is Displayed in the Face of a Concurrency Violation

그림 9: 동시성 위반이 발생 한 경우 (전체 크기 이미지를 보려면 클릭) 메시지가 표시 됨Figure 9: A Message is Displayed in the Face of a Concurrency Violation (Click to view full-size image)

요약Summary

여러 사용자가 동시에 동일한 데이터를 편집할 수 있는 웹 응용 프로그램을 만들 때 동시성 제어 옵션을 고려해 야 합니다.When creating a web application where multiple, concurrent users may be editing the same data, it is important to consider concurrency control options. 기본적으로 ASP.NET 데이터 웹 컨트롤과 데이터 소스 컨트롤은 동시성 제어를 사용 하지 않습니다.By default, the ASP.NET data Web controls and data source controls do not employ any concurrency control. 이 자습서에서 살펴본 것 처럼 SqlDataSource를 사용 하 여 낙관적 동시성 제어를 구현 하는 것은 비교적 빠르고 간단 합니다.As we saw in this tutorial, implementing optimistic concurrency control with the SqlDataSource is relatively quick and easy. SqlDataSource는 추가 WHERE 절을 자동 생성 된 UPDATEDELETE 문에 추가 하는 데 필요한 대부분의 투자할를 처리 하지만 NULL 값을 올바르게 처리 하는 방법 섹션에서 설명 하는 것 처럼 NULL 값 열을 처리할 때 약간의 미묘한 내용이 있습니다.The SqlDataSource handles most of the legwork for your adding augmented WHERE clauses to the autogenerated UPDATE and DELETE statements but there are a few subtleties in handling NULL value columns, as discussed in the Correctly Handling NULL Values section.

이 자습서에서는 SqlDataSource 검사를 마칩니다.This tutorial concludes our examination of the SqlDataSource. 나머지 자습서에서는 ObjectDataSource 및 계층화 된 아키텍처를 사용 하 여 데이터 작업으로 돌아갑니다.Our remaining tutorials will return to working with data using the ObjectDataSource and tiered architecture.

행복 한 프로그래밍Happy Programming!

저자 정보About the Author

Scott Mitchell(7 개의 ASP/ASP. NET books 및 4GuysFromRolla.com창립자)은 1998부터 Microsoft 웹 기술을 사용 하 여 작업 했습니다.Scott Mitchell, author of seven ASP/ASP.NET books and founder of 4GuysFromRolla.com, has been working with Microsoft Web technologies since 1998. Scott은 독립 컨설턴트, 강사 및 기록기로 작동 합니다.Scott works as an independent consultant, trainer, and writer. 최신 책은 24 시간 이내에 ASP.NET 2.0을 sams teach yourself것입니다.His latest book is Sams Teach Yourself ASP.NET 2.0 in 24 Hours. mitchell@4GuysFromRolla.com에 도달할 수 있습니다 .He can be reached at mitchell@4GuysFromRolla.com. 또는 블로그를 통해 http://ScottOnWriting.NET에서 찾을 수 있습니다.or via his blog, which can be found at http://ScottOnWriting.NET.