작성자 스콧 미첼
이 자습서에서는 낙관적 동시성 제어의 필수 사항을 검토한 다음 SqlDataSource 컨트롤을 사용하여 구현하는 방법을 살펴봅니다.
소개
이전 자습서에서는 SqlDataSource 컨트롤에 삽입, 업데이트 및 삭제 기능을 추가하는 방법을 검토했습니다. 요컨대, 이러한 기능을 제공하기 위해 컨트롤 속성의 해당 INSERT
, UPDATE
, 또는 DELETE
SQL 문을 지정하고, InsertCommand
, UpdateCommand
, 그리고 DeleteCommand
컬렉션에 적절한 매개 변수를 설정해야 했습니다. 이러한 속성 및 컬렉션을 수동으로 지정할 수도 있지만, 데이터 원본 구성 마법사의 고급 버튼에는 INSERT
문에 기반하여 UPDATE
, DELETE
, 및 SELECT
문을 자동으로 생성하는 확인란이 제공됩니다.
고급 SQL 생성 옵션 대화 상자에는 생성 INSERT
, UPDATE
, 및 DELETE
문 확인란과 함께 옵티미스틱 동시성 사용 옵션이 포함되어 있습니다(그림 1 참조). 선택 시, 자동 생성된 WHERE
및 UPDATE
문서의 DELETE
절이, 사용자가 데이터를 그리드에 마지막으로 로드한 이후 기본 데이터베이스 데이터에 변경이 없었을 경우에만 업데이트 또는 삭제를 수행하도록 수정됩니다.
그림 1: 고급 SQL 생성 옵션 대화 상자에서 낙관적 동시성 지원을 추가할 수 있습니다.
낙관적 동시성 구현 자습서에서는 낙관적 동시성 제어의 기본 사항과 ObjectDataSource에 추가하는 방법을 살펴보했습니다. 이 자습서에서는 낙관적 동시성 제어의 필수 사항을 수정한 다음 SqlDataSource를 사용하여 구현하는 방법을 살펴봅니다.
낙관적 동시성 요약
동시에 여러 사용자가 동일한 데이터를 편집하거나 삭제할 수 있는 웹 애플리케이션의 경우 한 사용자가 실수로 다른 변경 내용을 덮어쓸 수 있습니다. 낙관적 동시성 구현 자습서에서 다음 예제를 제공했습니다.
Jisun과 Sam이라는 두 사용자가 모두 방문자가 GridView 컨트롤을 통해 제품을 업데이트하고 삭제할 수 있는 애플리케이션의 페이지를 방문했다고 상상해 보세요. 둘 다 동시에 Chai에 대한 편집 단추를 클릭합니다. Jisun은 제품 이름을 Chai Tea로 변경하고 업데이트 단추를 클릭합니다. 최종 결과는 UPDATE
데이터베이스로 전송되는 문장입니다. Jisun이 하나의 필드만 업데이트했음에도 불구하고 제품의 모든 업데이트 가능한 필드를 설정합니다.ProductName
. 이 시점에서 데이터베이스에는 이 특정 제품에 대한 값으로 차이 티, 카테고리인 음료, 공급업체인 Exotic Liquids 등이 있습니다. 그러나 Sam의 GridView 화면에는 편집 가능한 GridView 행의 제품 이름이 Chai로 표시됩니다. Jisun의 변경 내용이 커밋된 후 몇 초 후에 Sam은 범주를 Condiments로 업데이트하고 업데이트를 클릭합니다. 이렇게 하면 UPDATE
이 데이터베이스로 보내져 제품 이름을 'Chai'로 설정하고, CategoryID
를 해당 Condiments 카테고리 ID로 지정하는 SQL 문이 실행됩니다. 제품 이름에 대한 Jisun의 변경 사항이 덮어쓰여졌습니다.
그림 2에서는 이러한 상호 작용을 보여 줍니다.
그림 2: 두 사용자가 동시에 레코드를 업데이트할 때 한 사용자가 다른 사용자를 덮어쓸 가능성이 있습니다(전체 크기 이미지를 보려면 클릭).
이 시나리오가 전개되지 않도록 하려면 동시성 제어 의 형태를 구현해야 합니다. 낙관적 동시성 이 자습서의 초점은 때때로 동시성 충돌이 있을 수 있지만 대부분의 경우 이러한 충돌이 발생하지 않는다는 가정하에 작동합니다. 따라서 충돌이 발생하는 경우 낙관적 동시성 제어는 다른 사용자가 동일한 데이터를 수정했기 때문에 변경 내용을 저장할 수 없다는 것을 사용자에게 알릴 뿐입니다.
비고
동시성 충돌이 많거나 이러한 충돌을 견딜 수 없는 경우 비관적 동시성 제어를 대신 사용할 수 있습니다. 비관적 동시성 제어에 대한 보다 철저한 논의는 낙관적 동시성 구현 자습서를 다시 참조하세요.
낙관적 동시성 제어는 업데이트 또는 삭제되는 레코드가 업데이트 또는 삭제 프로세스가 시작될 때와 동일한 값을 가지도록 하여 작동합니다. 예를 들어 편집 가능한 GridView에서 편집 단추를 클릭하면 레코드 값이 데이터베이스에서 읽혀지고 TextBoxes 및 기타 웹 컨트롤에 표시됩니다. 이러한 원래 값은 GridView에 의해 저장됩니다. 나중에 사용자가 변경하고 업데이트 단추를 UPDATE
클릭한 후 사용된 문은 원래 값과 새 값을 고려하고 사용자가 편집하기 시작한 원래 값이 데이터베이스의 값과 동일한 경우에만 기본 데이터베이스 레코드를 업데이트해야 합니다. 그림 3에서는 이 이벤트 시퀀스를 보여 줍니다.
그림 3: 업데이트 또는 삭제가 성공하려면 원래 값이 현재 데이터베이스 값과 같아야 합니다(전체 크기 이미지를 보려면 클릭).
낙관적 동시성을 구현하는 다양한 방법이 있습니다(다양한 옵션을 간략하게 살펴보려면 Peter A. Bromberg의 낙관적 동시성 업데이트 논리 참조). SqlDataSource와 데이터 액세스 계층에 사용되는 ADO.NET 형식화된 데이터 세트에서 사용되는 기법은 WHERE
절을 확장하여 모든 원본 값들에 대한 비교를 포함합니다. 예를 들어 다음 UPDATE
문은 현재 데이터베이스 값이 GridView에서 레코드를 업데이트할 때 원래 검색된 값과 같은 경우에만 제품의 이름과 가격을 업데이트합니다. 및 @ProductName
매개 변수는 @UnitPrice
사용자가 입력한 새 값을 포함하는 반면 @original_ProductName
@original_UnitPrice
편집 단추를 클릭할 때 원래 GridView에 로드된 값을 포함합니다.
UPDATE Products SET
ProductName = @ProductName,
UnitPrice = @UnitPrice
WHERE
ProductID = @original_ProductID AND
ProductName = @original_ProductName AND
UnitPrice = @original_UnitPrice
이 자습서에서 볼 수 있듯이 SqlDataSource를 사용하여 낙관적 동시성 제어를 사용하도록 설정하는 것은 확인란을 확인하는 것만큼 간단합니다.
1단계: 낙관적 동시성을 지원하는 SqlDataSource 만들기
먼저 OptimisticConcurrency.aspx
폴더에서 SqlDataSource
페이지를 엽니다. 도구 상자에서 디자이너로 SqlDataSource 컨트롤을 끌어서 해당 ID
속성을 ProductsDataSourceWithOptimisticConcurrency
로 설정합니다. 그런 다음 컨트롤의 스마트 태그에서 데이터 원본 구성 링크를 클릭합니다. 마법사의 첫 번째 화면에서 NORTHWINDConnectionString
와 작업하도록 선택하고 다음을 클릭합니다.
그림 4: NORTHWINDConnectionString
과(와) 함께 작업하기 선택 (전체 크기 이미지를 보려면 클릭)
이 예제에서는 사용자가 테이블을 편집할 수 있는 GridView를 Products
추가하겠습니다. 따라서 [문 선택 구성] 화면에서 드롭다운 목록에서 Products
테이블을 선택하고, ProductID
, ProductName
, UnitPrice
, Discontinued
열을 그림 5와 같이 선택합니다.
그림 5: Products
테이블에서 , ProductID
, ProductName
및 UnitPrice
열을 반환Discontinued
합니다(전체 크기 이미지를 보려면 클릭).
열을 선택한 후 고급 단추를 클릭하여 고급 SQL 생성 옵션 대화 상자를 표시합니다. 생성 INSERT
, UPDATE
, 및 DELETE
문을 확인하고 낙관적 동시성 확인란을 선택한 후 확인 버튼을 클릭하세요 (스크린샷은 그림 1에서 확인할 수 있습니다). 다음을 클릭한 다음 마침을 클릭하여 마법사를 완료합니다.
데이터 원본 구성 마법사를 완료한 후 잠시 시간을 내어 결과 DeleteCommand
및 속성 및 UpdateCommand
DeleteParameters
UpdateParameters
컬렉션을 검사합니다. 이 작업을 수행하는 가장 쉬운 방법은 왼쪽 아래 모서리에 있는 원본 탭을 클릭하여 페이지의 선언적 구문을 확인하는 것입니다. 그곳에서 다음의 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
컬렉션에 7개의 매개 변수가 있습니다.UpdateParameters
<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
컬렉션은 다음과 같이 표시됩니다.
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>
낙관적 동시성 사용 옵션을 선택하면, WHERE
절에 UpdateCommand
및 DeleteCommand
속성을 추가하고 각 매개 변수 컬렉션에 추가 매개 변수를 더하는 것 외에, 두 가지 다른 속성이 조정됩니다.
(기본값)에서속성을 로 변경합니다. -
OldValuesParameterFormatString
속성을 (기본값)에서 {0} original_{0} 변경합니다.
데이터 웹 컨트롤이 SqlDataSource Update()
또는 Delete()
메서드를 호출하면 원래 값이 전달됩니다. SqlDataSource의 ConflictDetection
속성을 설정 CompareAllValues
하면 이러한 원래 값이 명령에 추가됩니다. 이 속성은 OldValuesParameterFormatString
이러한 원래 값 매개 변수에 사용되는 명명 패턴을 제공합니다. 데이터 원본 구성 마법사는 original_{0} 사용하며, 이에 따라 각 원본 매개 변수와 UpdateCommand
속성 및 DeleteCommand
UpdateParameters
컬렉션의 DeleteParameters
이름을 지정합니다.
비고
SqlDataSource 컨트롤의 삽입 기능을 사용하지 않으므로, InsertCommand
속성과 InsertParameters
컬렉션을 자유롭게 제거하시면 됩니다.
값 올바르게 처리NULL
아쉽게도 낙관적 동시성을 사용할 때 데이터 원본 구성 마법사에서 자동으로 생성된 보강된 UPDATE
및 DELETE
문은 값을 포함한 레코드에서는 NULL
. 이유를 확인하려면 SqlDataSource 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
UnitPrice
열은 Products
테이블에서 NULL
값을 가질 수 있습니다. 특정 레코드에 대한 NULL
UnitPrice
값이 WHERE
있는 경우 절 부분은 [UnitPrice] = @original_UnitPrice
항상 False를 반환하기 때문에 NULL = NULL
항상 False로 평가됩니다. 따라서 NULL
값을 포함하는 레코드는 UPDATE
할 수 없으며, DELETE
및 WHERE
문장의 절들이 업데이트하거나 삭제할 행을 반환하지 않기 때문입니다.
비고
이 버그는 2004년 6월 SqlDataSource에서 잘못된 SQL 문을 생성 하여 Microsoft에 처음 보고되었으며 다음 버전의 ASP.NET 수정될 예정입니다.
이 문제를 해결하려면, WHERE
값을 가질 수 있는 UpdateCommand
열의 DeleteCommand
및 속성에 있는 NULL
절을 수동으로 업데이트해야 합니다. 일반적으로 [ColumnName] = @original_ColumnName
을 다음과 같이 변경합니다.
(
([ColumnName] IS NULL AND @original_ColumnName IS NULL)
OR
([ColumnName] = @original_ColumnName)
)
이 수정은 선언적 태그를 통해, 속성 창의 UpdateQuery 또는 DeleteQuery 옵션을 통해 또는 데이터 원본 구성 마법사의 사용자 지정 SQL 문 또는 저장 프로시저 지정 옵션의 UPDATE 및 DELETE 탭을 통해 직접 수행할 수 있습니다. 다시 말하지만, 값을 포함 할 수 있는 and UpdateCommand
s 절의 DeleteCommand
WHERE
열에 NULL
대해 이 수정이 이루어져야 합니다.
예제에 이를 적용하면 다음과 같은 수정 UpdateCommand
된 값과 DeleteCommand
값이 생성됩니다.
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 추가
낙관적 동시성을 지원하도록 구성된 SqlDataSource를 사용하면 이 동시성 컨트롤을 활용하는 페이지에 데이터 웹 컨트롤을 추가할 수 있습니다. 이 자습서에서는 편집 및 삭제 기능을 모두 제공하는 GridView를 추가해 보겠습니다. 이렇게 하려면 도구 상자에서 GridView를 디자이너로 끌어서 대상 ID
을 Products
로 설정합니다. GridView의 스마트 태그에서 1단계에 추가된 ProductsDataSourceWithOptimisticConcurrency
SqlDataSource 컨트롤에 바인딩합니다. 마지막으로 스마트 태그에서 편집 사용 및 삭제 사용 옵션을 선택합니다.
그림 6: GridView를 SqlDataSource에 바인딩하고 편집 및 삭제 사용(전체 크기 이미지를 보려면 클릭)
GridView를 추가한 후 BoundField를 ProductID
제거하고, BoundField 속성을 ProductName
Product로 변경 HeaderText
하고, 해당 속성이 단순히 Price가 되도록 BoundField를 UnitPrice
업데이트 HeaderText
하여 모양을 구성합니다. 편집 인터페이스를 개선하여 ProductName
값에 대한 RequiredFieldValidator와 UnitPrice
값이 올바르게 형식화된 숫자 값인지 확인하기 위한 CompareValidator를 포함하는 것이 이상적입니다. GridView의 편집 인터페이스 사용자 지정에 대한 자세한 내용은 데이터 수정 인터페이스 사용자 지정 자습서를 참조하세요.
비고
GridView에서 SqlDataSource로 전달된 원래 값이 뷰 상태에 저장되므로 GridView의 뷰 상태를 사용하도록 설정해야 합니다.
GridView를 수정한 후 GridView 및 SqlDataSource 선언적 태그는 다음과 유사하게 표시됩니다.
<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
를 로드하십시오. 두 브라우저에서 첫 번째 제품의 편집 단추를 클릭합니다. 한 브라우저에서 제품 이름을 변경하고 업데이트를 클릭합니다. 브라우저가 포스트백되고 GridView가 편집 전 모드로 돌아가 방금 편집한 레코드의 새 제품 이름을 표시합니다.
두 번째 브라우저 창에서 가격을 변경하고(제품 이름을 원래 값으로 유지) 업데이트를 클릭합니다. 포스트백 시 그리드는 사전 편집 모드로 반환되지만 가격 변경 내용은 기록되지 않습니다. 두 번째 브라우저는 새 제품 이름과 동일한 값을 이전 가격으로 표시합니다. 두 번째 브라우저 창에서 변경한 내용이 손실되었습니다. 또한 동시성 위반이 방금 발생했음을 나타내는 예외나 메시지가 없으므로 변경 내용이 다소 조용히 손실되었습니다.
그림 7: 두 번째 브라우저 창의 변경 내용이 자동으로 손실되었습니다(전체 크기 이미지를 보려면 클릭).
두 번째 브라우저의 변경 내용이 커밋되지 않은 이유는 UPDATE
문장의 WHERE
절이 모든 레코드를 필터링하여 어떤 행에도 영향을 미치지 않았기 때문입니다. 문장 UPDATE
을 다시 살펴보겠습니다.
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
지정된 원래 제품 이름이 기존 제품 이름과 일치하지 않습니다(첫 번째 브라우저에서 변경했기 때문에). 따라서 문 [ProductName] = @original_ProductName
은 False를 반환하며 레코드에는 UPDATE
영향을 주지 않습니다.
비고
삭제는 동일한 방식으로 작동합니다. 두 개의 브라우저 창이 열려 있는 상태에서 먼저 지정된 제품을 편집한 다음 변경 내용을 저장합니다. 한 브라우저에서 변경 내용을 저장한 후 다른 브라우저에서 동일한 제품에 대한 삭제 단추를 클릭합니다. 원래 값이 DELETE
문 WHERE
절에서 일치하지 않아서 삭제가 조용히 실패합니다.
두 번째 브라우저 창의 최종 사용자 관점에서, 업데이트 단추를 클릭한 후 그리드가 사전 편집 모드로 반환되지만 변경 내용이 손실되었습니다. 그러나 변경 내용이 달라지지 않았다는 시각적 피드백은 없습니다. 사용자의 변경 내용이 동시성 위반으로 손실되는 경우 사용자에게 알리고 그리드를 편집 모드로 유지하는 것이 가장 좋습니다. 이 작업을 수행하는 방법을 살펴보겠습니다.
3단계: 동시성 위반이 발생한 시기 확인
동시성 위반은 변경 내용을 거부하므로 동시성 위반이 발생했을 때 사용자에게 경고하는 것이 좋습니다. 사용자에게 경고하려면 속성에 다음 메시지가 표시되는 페이지 맨 ConcurrencyViolationMessage
Text
위에 레이블 웹 컨트롤을 추가해 보겠습니다. 다른 사용자가 동시에 업데이트한 레코드를 업데이트하거나 삭제하려고 했습니다. 다른 사용자의 변경 내용을 검토한 다음 업데이트를 다시 실행하거나 삭제하세요. 레이블 컨트롤의 CssClass
속성을 경고로 설정합니다. 이 클래스는 텍스트를 빨간색, 기울임꼴, 굵게 및 큰 글꼴로 표시하는 CSS 클래스 Styles.css
입니다. 마지막으로 레이블 Visible
및 EnableViewState
속성을 false
.로 설정합니다. 이렇게 하면 해당 속성을 Visible
으로 명시적으로 설정한 포스트백을 제외하고 레이블이 true
숨겨지게 됩니다.
그림 8: 페이지에 레이블 컨트롤을 추가하여 경고 표시(전체 크기 이미지를 보려면 클릭)
업데이트 또는 삭제를 수행할 때, 데이터 소스 제어가 요청된 업데이트 또는 삭제를 수행한 후에, GridView의 RowUpdated
및 RowDeleted
이벤트 처리기가 실행됩니다. 이러한 이벤트 처리기의 작업에 의해 영향을 받은 행 수를 확인할 수 있습니다. 0개의 행이 영향을 받은 경우 레이블을 ConcurrencyViolationMessage
표시하려고 합니다.
RowUpdated
이벤트와 RowDeleted
이벤트 모두에 대한 이벤트 처리기를 만들고 다음 코드를 추가합니다.
protected void Products_RowUpdated(object sender, GridViewUpdatedEventArgs e)
{
if (e.AffectedRows == 0)
{
ConcurrencyViolationMessage.Visible = true;
e.KeepInEditMode = true;
// Rebind the data to the GridView to show the latest changes
Products.DataBind();
}
}
protected void Products_RowDeleted(object sender, GridViewDeletedEventArgs e)
{
if (e.AffectedRows == 0)
ConcurrencyViolationMessage.Visible = true;
}
두 이벤트 처리기에서 e.AffectedRows
속성을 확인하고, 0이면 ConcurrencyViolationMessage
Label의 Visible
속성을 true
으로 설정합니다.
RowUpdated
이벤트 처리기에서 KeepInEditMode
속성을 true로 설정하여 GridView가 편집 모드를 유지하도록 지시합니다. 이렇게 하려면 다른 사용자의 데이터가 편집 인터페이스에 로드되도록 데이터를 그리드에 다시 바인딩해야 합니다. 이 작업은 GridView의 DataBind()
메서드를 호출하여 수행됩니다.
그림 9에서 알 수 있듯이 이러한 두 이벤트 처리기를 사용하면 동시성 위반이 발생할 때마다 매우 눈에 띄는 메시지가 표시됩니다.
그림 9: 동시성 위반의 얼굴에 메시지가 표시됩니다(전체 크기 이미지를 보려면 클릭).
요약
여러 동시 사용자가 동일한 데이터를 편집할 수 있는 웹 애플리케이션을 만들 때 동시성 제어 옵션을 고려하는 것이 중요합니다. 기본적으로 ASP.NET 데이터 웹 컨트롤 및 데이터 원본 컨트롤은 동시성 제어를 사용하지 않습니다. 이 자습서에서 보았듯이 SqlDataSource를 사용하여 낙관적 동시성 제어를 구현하는 것은 비교적 빠르고 쉽습니다. SqlDataSource는 자동 생성된 WHERE
및 UPDATE
문에 개선된 DELETE
절을 추가하는 대부분의 작업을 처리하지만, 값 열을 NULL
로 다루는 데 있어서는 "값을 정확하게 처리" 섹션에서 설명된 대로 몇 가지 미묘한 차이가 있습니다.
이 자습서에서는 SqlDataSource에 대한 검사를 완료합니다. 나머지 자습서는 ObjectDataSource 및 계층화된 아키텍처를 사용하여 데이터 작업으로 돌아갑니다.
행복한 프로그래밍!
작성자 정보
7개의 ASP/ASP.NET 책의 저자이자 4GuysFromRolla.com 창립자인 Scott Mitchell은 1998년부터 Microsoft 웹 기술을 연구해 왔습니다. Scott은 독립 컨설턴트, 트레이너 및 작가로 일합니다. 그의 최신 책은 샘스 티치 유어셀프 ASP.NET 2.0 24시간 안에 배우기입니다. 그는 mitchell@4GuysFromRolla.com로 연락할 수 있습니다.