일괄 업데이트(C#)

작성자 : Scott Mitchell

PDF 다운로드

단일 작업에서 여러 데이터베이스 레코드를 업데이트하는 방법을 알아봅니다. 사용자 인터페이스 계층에서 각 행을 편집할 수 있는 GridView를 빌드합니다. 데이터 액세스 계층에서 트랜잭션 내에서 여러 업데이트 작업을 래핑하여 모든 업데이트가 성공하거나 모든 업데이트가 롤백되도록 합니다.

소개

이전 자습서에서는 데이터 액세스 계층을 확장하여 데이터베이스 트랜잭션에 대한 지원을 추가하는 방법을 알아보았습니다. 데이터베이스 트랜잭션은 일련의 데이터 수정 문이 하나의 원자성 작업으로 처리되도록 보장하여 모든 수정이 실패하거나 모두 성공하도록 합니다. 이 낮은 수준의 DAL 기능을 사용하면 일괄 처리 데이터 수정 인터페이스를 만드는 데 주의를 기울일 준비가 된 것입니다.

이 자습서에서는 각 행을 편집할 수 있는 GridView를 빌드합니다(그림 1 참조). 각 행은 편집 인터페이스에서 렌더링되므로 편집, 업데이트 및 취소 단추의 열이 필요하지 않습니다. 대신 페이지에는 GridView 행을 열거하고 데이터베이스를 업데이트하는 두 개의 제품 업데이트 단추가 있습니다.

GridView의 각 행은 편집 가능

그림 1: GridView의 각 행은 편집 가능(전체 크기 이미지를 보려면 클릭)

시작해 보겠습니다!

참고

Batch 업데이트 수행 자습서에서는 DataList 컨트롤을 사용하여 일괄 편집 인터페이스를 만들었습니다. 이 자습서는 GridView를 사용하는 의 이전 자습서와 다르며 일괄 업데이트는 트랜잭션의 scope 내에서 수행됩니다. 이 자습서를 완료한 후 이전 자습서로 돌아가서 이전 자습서에 추가된 데이터베이스 트랜잭션 관련 기능을 사용하도록 업데이트하는 것이 좋습니다.

모든 GridView 행을 편집 가능하게 만들기 위한 단계 검사

데이터 삽입, 업데이트 및 삭제 개요 자습서에서 설명한 대로 GridView는 기본 데이터 편집을 행별로 기본적으로 지원합니다. 내부적으로 GridView는 속성을 통해 편집할 수 있는 행을 설명합니다EditIndex. GridView는 데이터 원본에 바인딩되므로 각 행을 확인하여 행의 인덱스가 의 값 EditIndex과 같은지 확인합니다. 이 경우 해당 행의 필드는 편집 인터페이스를 사용하여 렌더링됩니다. BoundFields의 경우 편집 인터페이스는 속성에 Text BoundField 속성으로 지정된 데이터 필드 값이 할당된 TextBox입니다 DataField . TemplateFields의 EditItemTemplate 경우 는 대신 ItemTemplate사용됩니다.

사용자가 행의 편집 단추를 클릭하면 편집 워크플로가 시작됩니다. 이렇게 하면 포스트백이 발생하고 GridView의 EditIndex 속성이 클릭한 행의 인덱스로 설정되고 데이터가 그리드에 다시 바인딩됩니다. 행의 취소 단추를 클릭하면 포스트백 EditIndex 시 는 데이터를 그리드에 다시 바인딩하기 전에 값 -1 으로 설정됩니다. GridView의 행이 0에서 인덱싱을 시작하므로 -1 를 로 설정 EditIndex 하면 읽기 전용 모드로 GridView를 표시하는 효과가 있습니다.

속성은 EditIndex 행별 편집에 적합하지만 일괄 편집용으로 설계되지 않았습니다. 전체 GridView를 편집 가능하도록 하려면 편집 인터페이스를 사용하여 각 행을 렌더링해야 합니다. 이 작업을 수행하는 가장 쉬운 방법은 편집 가능한 각 필드가 에 정의된 ItemTemplate편집 인터페이스를 사용하여 TemplateField로 구현되는 위치를 만드는 것입니다.

다음 몇 단계를 통해 완전히 편집 가능한 GridView를 만듭니다. 1단계에서는 GridView 및 해당 ObjectDataSource를 만들고 BoundFields 및 CheckBoxField를 TemplateFields로 변환합니다. 2단계와 3단계에서는 편집 인터페이스를 TemplateFields EditItemTemplate 에서 해당 ItemTemplate 인터페이스로 이동합니다.

1단계: 제품 정보 표시

행을 편집할 수 있는 GridView를 만드는 것에 대해 걱정하기 전에 먼저 제품 정보를 표시하기만 하면 됩니다. 폴더에서 BatchUpdate.aspxBatchData 페이지를 열고 도구 상자에서 Designer GridView를 끌어옵니다. GridView를 IDProductsGrid 설정하고 스마트 태그에서 라는 ProductsDataSource새 ObjectDataSource에 바인딩하도록 선택합니다. 클래스의 GetProducts 메서드에서 해당 데이터를 검색하도록 ObjectDataSource를 ProductsBLL 구성합니다.

ProductsBLL 클래스를 사용하도록 ObjectDataSource 구성

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

GetProducts 메서드를 사용하여 제품 데이터 검색

그림 3: 메서드를 사용하여 GetProducts 제품 데이터 검색(전체 크기 이미지를 보려면 클릭)

GridView와 마찬가지로 ObjectDataSource의 수정 기능은 행별로 작동하도록 설계되었습니다. 레코드 집합을 업데이트하려면 데이터를 일괄 처리하고 BLL에 전달하는 ASP.NET 페이지의 코드 숨김 클래스에 약간의 코드를 작성해야 합니다. 따라서 ObjectDataSource의 UPDATE, INSERT 및 DELETE 탭의 드롭다운 목록을 (없음)으로 설정합니다. 마침을 클릭하여 마법사를 완료합니다.

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

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

데이터 원본 구성 마법사를 완료한 후 ObjectDataSource의 선언적 태그는 다음과 같습니다.

<asp:ObjectDataSource ID="ProductsDataSource" runat="server" 
    OldValuesParameterFormatString="original_{0}"
    SelectMethod="GetProducts" TypeName="ProductsBLL">
</asp:ObjectDataSource>

데이터 원본 구성 마법사를 완료하면 Visual Studio에서 GridView의 제품 데이터 필드에 대한 BoundFields 및 CheckBoxField를 만들 수도 있습니다. 이 자습서에서는 사용자가 제품 이름, 범주, 가격 및 중단된 상태 보고 편집할 수 있도록 허용해 보겠습니다. , , UnitPriceCategoryNameDiscontinued 필드를 제외한 ProductName모든 필드를 제거하고 처음 세 필드의 속성 이름을 각각 Product, Category 및 Price로 바꿉 HeaderText 니다. 마지막으로 GridView의 스마트 태그에서 페이징 사용 및 정렬 사용 확인란을 검사.

이 시점에서 GridView에는 3개의 BoundFields(ProductName, CategoryName및 )와 UnitPriceCheckBoxField(Discontinued)가 있습니다. 이러한 4개의 필드를 TemplateFields로 변환한 다음, 편집 인터페이스를 TemplateField의 EditItemTemplate 에서 로 ItemTemplate이동해야 합니다.

참고

데이터 수정 인터페이스 사용자 지정 자습서에서 TemplateFields를 만들고 사용자 지정하는 방법을 살펴보했습니다. BoundFields 및 CheckBoxField를 TemplateFields로 변환하고 편집 인터페이스 ItemTemplate 를 정의하는 단계를 안내하지만, 중단되거나 새로 고침이 필요한 경우 주저하지 말고 이 이전 자습서를 다시 참조하세요.

GridView의 스마트 태그에서 열 편집 링크를 클릭하여 필드 대화 상자를 엽니다. 다음으로, 각 필드를 선택하고 이 필드를 TemplateField로 변환 링크를 클릭합니다.

기존 BoundFields 및 CheckBoxField를 TemplateField로 변환

그림 5: 기존 BoundFields 및 CheckBoxField를 TemplateField로 변환

이제 각 필드가 TemplateField이므로 편집 인터페이스를 의 에서 EditItemTemplate s로 ItemTemplate 이동할 준비가 되었습니다.

2단계: 만들기ProductNameUnitPriceDiscontinued인터페이스 편집

ProductName, UnitPriceDiscontinued 편집 인터페이스를 만드는 것은 이 단계의 주제이며 각 인터페이스가 TemplateField의 EditItemTemplate에 이미 정의되어 있기 때문에 매우 간단합니다. 해당 범주의 CategoryName DropDownList를 만들어야 하므로 편집 인터페이스를 만드는 것이 좀 더 복잡합니다. 이 CategoryName 편집 인터페이스는 3단계에서 다루어집니다.

TemplateField부터 ProductName 시작하겠습니다. GridView의 스마트 태그에서 템플릿 편집 링크를 클릭하고 TemplateField의 EditItemTemplateProductName 드릴다운합니다. TextBox를 선택하고 클립보드에 복사한 다음 TemplateField의 에 ProductName 붙여넣습니다 ItemTemplate. TextBox의 ID 속성을 로 변경합니다 ProductName.

다음으로, RequiredFieldValidator를 에 ItemTemplate 추가하여 사용자가 각 제품 이름에 대한 값을 제공하는지 확인합니다. ControlToValidate 속성을 ProductName으로 설정하고, ErrorMessage 속성을 You로 설정하려면 제품 이름을 제공해야 합니다. 및 속성을 Text *로 지정합니다. 를 추가한 후 화면은 ItemTemplate그림 6과 유사하게 표시됩니다.

이제 ProductName TemplateField에 TextBox 및 RequiredFieldValidator가 포함됩니다.

그림 6: ProductName 이제 TemplateField에 TextBox 및 RequiredFieldValidator가 포함되어 있습니다(전체 크기 이미지를 보려면 클릭).

편집 인터페이스의 UnitPrice 경우 에서 로 TextBox EditItemTemplate 를 복사하여 시작합니다 ItemTemplate. 다음으로 TextBox 앞에 $를 배치하고 해당 ID 속성을 UnitPrice로 설정하고 해당 Columns 속성을 8로 설정합니다.

또한 CompareValidator를 UnitPriceItemTemplate 추가하여 사용자가 입력한 값이 $0.00보다 크거나 같은 유효한 통화 값인지 확인합니다. 유효성 검사기의 ControlToValidate 속성을 UnitPrice로 설정하고 해당 ErrorMessage 속성을 유효한 통화 값을 입력해야 합니다. 통화 기호를 생략하십시오., 해당 Text 속성은 *로, 속성CurrencyType 로, 속성GreaterThanEqualOperator 로, 속성은 ValueToCompare 0으로 생략하십시오.

CompareValidator를 추가하여 입력한 가격이 음수가 아닌 통화 값인지 확인합니다.

그림 7: CompareValidator를 추가하여 입력한 가격이 음수가 아닌 통화 값인지 확인합니다(전체 크기 이미지를 보려면 클릭).

TemplateField의 Discontinued 경우 에 이미 정의된 CheckBox를 ItemTemplate사용할 수 있습니다. 간단히 해당 ID 을 Discontinued로 설정하고 해당 Enabled 속성을 로 true설정합니다.

3단계: 편집 인터페이스 만들기CategoryName

TemplateField의 EditItemTemplate 편집 인터페이스 CategoryName 에는 데이터 필드의 값을 표시하는 TextBox가 CategoryName 포함되어 있습니다. 가능한 범주를 나열하는 DropDownList로 바꿔야 합니다.

참고

데이터 수정 인터페이스 사용자 지정 자습서에는 TextBox가 아닌 DropDownList를 포함하도록 템플릿을 사용자 지정하는 방법에 대한 보다 철저하고 완전한 설명이 포함되어 있습니다. 이 단계가 완료된 동안에는 세세하게 표시됩니다. DropDownList 범주를 만들고 구성하는 방법에 대한 자세한 내용은 데이터 수정 인터페이스 사용자 지정 자습서를 참조하세요.

도구 상자에서 TemplateField의 ItemTemplate로 DropDownList를 CategoryName 끌어서 로 IDCategories설정합니다. 이 시점에서는 일반적으로 스마트 태그를 통해 DropDownLists의 데이터 원본을 정의하여 새 ObjectDataSource를 만듭니다. 그러나 이렇게 하면 에 ObjectDataSource가 ItemTemplate추가되므로 각 GridView 행에 대해 ObjectDataSource instance 만들어집니다. 대신 GridView의 TemplateFields 외부에 ObjectDataSource를 만들어 보겠습니다. 템플릿 편집을 종료하고 Toolbox에서 ObjectDataSource 아래 ProductsDataSource 의 Designer ObjectDataSource를 끌어옵니다. 새 ObjectDataSource CategoriesDataSource 의 이름을 지정하고 클래스의 GetCategories 메서드를 CategoriesBLL 사용하도록 구성합니다.

CategoriesBLL 클래스를 사용하도록 ObjectDataSource 구성

그림 8: 클래스를 사용하도록 CategoriesBLL ObjectDataSource 구성(전체 크기 이미지를 보려면 클릭)

GetCategories 메서드를 사용하여 범주 데이터 검색

그림 9: 메서드를 사용하여 GetCategories 범주 데이터 검색(전체 크기 이미지를 보려면 클릭)

이 ObjectDataSource는 데이터를 검색하는 데만 사용되므로 UPDATE 및 DELETE 탭의 드롭다운 목록을 (없음)으로 설정합니다. 마침을 클릭하여 마법사를 완료합니다.

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

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

마법사 CategoriesDataSource 를 완료한 후 선언적 태그는 다음과 같이 표시됩니다.

<asp:ObjectDataSource ID="CategoriesDataSource" runat="server" 
    OldValuesParameterFormatString="original_{0}"
    SelectMethod="GetCategories" TypeName="CategoriesBLL">
</asp:ObjectDataSource>

만들고 구성한 CategoriesDataSource 상태에서 TemplateField s CategoryNameItemTemplate 로 돌아가서 DropDownList의 스마트 태그에서 데이터 원본 선택 링크를 클릭합니다. 데이터 원본 구성 마법사의 첫 번째 드롭다운 목록에서 옵션을 선택하고 CategoriesDataSource 표시에 및 CategoryID 를 값으로 사용하도록 선택합니다CategoryName.

DropDownList를 CategoriesDataSource에 바인딩

그림 11: DropDownList를 CategoriesDataSource 에 바인딩합니다(전체 크기 이미지를 보려면 클릭).

이 시점에서 Categories DropDownList는 모든 범주를 나열하지만 GridView 행에 바인딩된 제품에 대한 적절한 범주를 아직 자동으로 선택하지는 않습니다. 이렇게 하려면 DropDownList를 CategoriesSelectedValue 제품 CategoryID 값으로 설정해야 합니다. DropDownList의 스마트 태그에서 DataBindings 편집 링크를 클릭하고 그림 12와 같이 속성을 데이터 필드와 CategoryID 연결 SelectedValue 합니다.

Product의 CategoryID 값을 DropDownList의 SelectedValue 속성에 바인딩

그림 12: DropDownList의 CategoryIDSelectedValue 속성에 제품 값 바인딩

마지막 문제 중 하나는 제품에 지정된 값이 CategoryID 없으면 의 databinding 문에서 SelectedValue 예외가 발생합니다. DropDownList는 범주에 대한 항목만 포함하고 에 대한 데이터베이스 값CategoryID이 있는 NULL 제품에 대한 옵션을 제공하지 않기 때문입니다. 이 문제를 해결하려면 DropDownList의 AppendDataBoundItems 속성을 true 로 설정하고 DropDownList에 새 항목을 추가하여 선언적 구문에서 속성을 생략 Value 합니다. 즉, DropDownList의 선언적 구문이 다음과 같은지 확인 Categories 합니다.

<asp:DropDownList ID="Categories" runat="server" AppendDataBoundItems="True" 
    DataSourceID="CategoriesDataSource" DataTextField="CategoryName" 
    DataValueField="CategoryID" SelectedValue='<%# Bind("CategoryID") %>'>
    <asp:ListItem Value=">-- Select One --</asp:ListItem>
</asp:DropDownList>

Select One의 특성이 Value 빈 문자열로 명시적으로 설정된 방법을 <asp:ListItem Value=""> 확인합니다. 사례를 처리하는 NULL 데 이 추가 DropDownList 항목이 필요한 이유와 빈 문자열에 속성을 할당 Value 하는 것이 필수적인 이유에 대한 자세한 내용은 데이터 수정 인터페이스 사용자 지정 자습서를 참조하세요.

참고

여기에는 언급할 가치가 있는 잠재적인 성능 및 확장성 문제가 있습니다. 각 행에는 를 데이터 원본 CategoriesBLL 으로 사용하는 CategoriesDataSource DropDownList가 있으므로 클래스의 GetCategories 메서드는 페이지 방문당 n번 호출됩니다. 여기서 n은 GridView의 행 수입니다. 이러한 n 호출은 GetCategories 데이터베이스에 대한 n 개의 쿼리를 생성합니다. SQL 캐싱 종속성 또는 매우 짧은 시간 기반 만료를 사용하여 요청당 캐시 또는 캐싱 계층을 통해 반환된 범주를 캐싱하여 데이터베이스에 미치는 영향을 줄 수 있습니다.

4단계: 편집 인터페이스 완료

진행 상황을 보기 위해 일시 중지하지 않고 GridView 템플릿을 여러 번 변경했습니다. 잠시 시간을 내어 브라우저를 통해 진행 상황을 확인합니다. 그림 13과 같이 각 행은 셀의 편집 인터페이스를 포함하는 를 ItemTemplate사용하여 렌더링됩니다.

각 GridView 행은 편집 가능

그림 13: 각 GridView 행을 편집할 수 있습니다(전체 크기 이미지를 보려면 클릭).

이 시점에서 처리해야 하는 몇 가지 사소한 서식 문제가 있습니다. 먼저 값에 UnitPrice 소수점이 4개 포함되어 있습니다. 이 문제를 해결하려면 TemplateField s UnitPriceItemTemplate 로 돌아가서 TextBox의 스마트 태그에서 DataBindings 편집 링크를 클릭합니다. 다음으로, 속성의 서식을 Text 숫자로 지정합니다.

Text 속성을 숫자로 서식 지정

그림 14: 속성을 숫자로 서식 지정 Text

둘째, 확인란을 왼쪽 맞춤이 아닌 열의 Discontinued 가운데에 배치해 보겠습니다. GridView의 스마트 태그에서 열 편집을 클릭하고 왼쪽 아래 모서리에 있는 필드 목록에서 TemplateField를 선택합니다 Discontinued . 로 드릴다운 ItemStyle 하고 그림 15와 같이 속성을 Center로 설정합니다 HorizontalAlign .

중단된 확인란 가운데 맞춤

그림 15: CheckBox 가운데 맞춤 Discontinued

다음으로, 페이지에 ValidationSummary 컨트롤을 추가하고 해당 속성을 true 로 설정하고 해당 ShowMessageBoxShowSummary 속성을 로 false설정합니다. 또한 단추 웹 컨트롤을 추가합니다. 이 컨트롤을 클릭하면 사용자의 변경 내용이 업데이트됩니다. 특히 두 개의 Button Web 컨트롤을 추가합니다. 하나는 GridView 위에, 다른 하나는 그 아래에 추가되며, 두 컨트롤 Text 속성을 모두 제품 업데이트로 설정합니다.

GridView의 편집 인터페이스는 TemplateFields ItemTemplateEditItemTemplate 정의되어 있으므로 s는 불필요하며 삭제될 수 있습니다.

위에서 언급한 서식 변경, 단추 컨트롤 추가 및 불필요한 EditItemTemplate 를 제거한 후 페이지의 선언적 구문은 다음과 같이 표시됩니다.

<p>
    <asp:Button ID="UpdateAllProducts1" runat="server" Text="Update Products" />
</p>
<p>
    <asp:GridView ID="ProductsGrid" runat="server" AutoGenerateColumns="False" 
        DataKeyNames="ProductID" DataSourceID="ProductsDataSource" 
        AllowPaging="True" AllowSorting="True">
        <Columns>
            <asp:TemplateField HeaderText="Product" SortExpression="ProductName">
                <ItemTemplate>
                    <asp:TextBox ID="ProductName" runat="server" 
                        Text='<%# Bind("ProductName") %>'></asp:TextBox>
                    <asp:RequiredFieldValidator ID="RequiredFieldValidator1" 
                        ControlToValidate="ProductName"
                        ErrorMessage="You must provide the product's name." 
                        runat="server">*</asp:RequiredFieldValidator>
                </ItemTemplate>
            </asp:TemplateField>
            <asp:TemplateField HeaderText="Category" 
                SortExpression="CategoryName">
                <ItemTemplate>
                    <asp:DropDownList ID="Categories" runat="server" 
                        AppendDataBoundItems="True" 
                        DataSourceID="CategoriesDataSource"
                        DataTextField="CategoryName" 
                        DataValueField="CategoryID" 
                        SelectedValue='<%# Bind("CategoryID") %>'>
                        <asp:ListItem>-- Select One --</asp:ListItem>
                    </asp:DropDownList>
                </ItemTemplate>
            </asp:TemplateField>
            <asp:TemplateField HeaderText="Price" 
                SortExpression="UnitPrice">
                <ItemTemplate>
                    $<asp:TextBox ID="UnitPrice" runat="server" Columns="8" 
                        Text='<%# Bind("UnitPrice", "{0:N}") %>'></asp:TextBox>
                    <asp:CompareValidator ID="CompareValidator1" runat="server" 
                        ControlToValidate="UnitPrice"
                        ErrorMessage="You must enter a valid currency value. 
                                      Please omit any currency symbols."
                        Operator="GreaterThanEqual" Type="Currency" 
                        ValueToCompare="0">*</asp:CompareValidator>
                </ItemTemplate>
            </asp:TemplateField>
            <asp:TemplateField HeaderText="Discontinued" SortExpression="Discontinued">
                <ItemTemplate>
                    <asp:CheckBox ID="Discontinued" runat="server" 
                        Checked='<%# Bind("Discontinued") %>' />
                </ItemTemplate>
                <ItemStyle HorizontalAlign="Center" />
            </asp:TemplateField>
        </Columns>
    </asp:GridView>
</p>
<p>
    <asp:Button ID="UpdateAllProducts2" runat="server" Text="Update Products" />
    <asp:ObjectDataSource ID="ProductsDataSource" runat="server" 
        OldValuesParameterFormatString="original_{0}"
        SelectMethod="GetProducts" TypeName="ProductsBLL">
    </asp:ObjectDataSource>
    <asp:ObjectDataSource ID="CategoriesDataSource" runat="server" 
        OldValuesParameterFormatString="original_{0}"
        SelectMethod="GetCategories" TypeName="CategoriesBLL">
    </asp:ObjectDataSource>
    <asp:ValidationSummary ID="ValidationSummary1" runat="server" 
        ShowMessageBox="True" ShowSummary="False" />
</p>

그림 16에서는 단추 웹 컨트롤이 추가되고 서식이 변경된 후 브라우저를 통해 볼 때 이 페이지를 보여 줍니다.

이제 페이지에 두 개의 업데이트 제품 단추가 포함됩니다.

그림 16: 이제 페이지에 두 개의 제품 업데이트 단추가 포함되어 있습니다(전체 크기 이미지를 보려면 클릭).

5단계: 제품 업데이트

사용자가 이 페이지를 방문하면 수정한 다음 두 가지 제품 업데이트 단추 중 하나를 클릭합니다. 이 시점에서 각 행에 대해 사용자가 입력한 값을 instance 저장한 다음 BLL 메서드에 ProductsDataTable 전달한 다음 해당 ProductsDataTable instance DAL UpdateWithTransaction 메서드에 전달해야 합니다. 이전 자습서에서 만든 메서드는 UpdateWithTransaction 변경 내용 일괄 처리가 원자성 작업으로 업데이트되도록 합니다.

에서 BatchUpdate.aspx.cs 라는 BatchUpdate 메서드를 만들고 다음 코드를 추가합니다.

private void BatchUpdate()
{
    // Enumerate the GridView's Rows collection and create a ProductRow
    ProductsBLL productsAPI = new ProductsBLL();
    Northwind.ProductsDataTable products = productsAPI.GetProducts();
    foreach (GridViewRow gvRow in ProductsGrid.Rows)
    {
        // Find the ProductsRow instance in products that maps to gvRow
        int productID = Convert.ToInt32(ProductsGrid.DataKeys[gvRow.RowIndex].Value);
        Northwind.ProductsRow product = products.FindByProductID(productID);
        if (product != null)
        {
            // Programmatically access the form field elements in the 
            // current GridViewRow
            TextBox productName = (TextBox)gvRow.FindControl("ProductName");
            DropDownList categories = 
                (DropDownList)gvRow.FindControl("Categories");
            TextBox unitPrice = (TextBox)gvRow.FindControl("UnitPrice");
            CheckBox discontinued = 
                (CheckBox)gvRow.FindControl("Discontinued");
            // Assign the user-entered values to the current ProductRow
            product.ProductName = productName.Text.Trim();
            if (categories.SelectedIndex == 0) 
                product.SetCategoryIDNull(); 
            else 
                product.CategoryID = Convert.ToInt32(categories.SelectedValue);
            if (unitPrice.Text.Trim().Length == 0) 
                product.SetUnitPriceNull(); 
            else 
                product.UnitPrice = Convert.ToDecimal(unitPrice.Text);
            product.Discontinued = discontinued.Checked;
        }
    }
    // Now have the BLL update the products data using a transaction
    productsAPI.UpdateWithTransaction(products);
}

이 메서드는 BLL GetProducts 메서드에 대한 호출을 통해 모든 제품을 에서 다시 ProductsDataTable 가져오는 것으로 시작합니다. 그런 다음 GridView의 Rows 컬렉션을 열거합니다ProductGrid. 컬렉션에는 Rows GridView에 GridViewRow 표시되는 각 행에 대한 instance 포함되어 있습니다. 페이지당 최대 10개의 행을 표시하므로 GridView의 컬렉션에는 10개 이하의 Rows 항목이 있습니다.

각 행에 대해 는 ProductID 컬렉션에서 DataKeys 가져오고 적절한 ProductsRow 는 에서 ProductsDataTable선택됩니다. 네 개의 TemplateField 입력 컨트롤은 프로그래밍 방식으로 참조되며 해당 값은 instance 속성에 ProductsRow 할당됩니다. 각 GridView 행의 값이 를 업데이트 ProductsDataTable하는 데 사용된 후에는 이전 자습서에서 보았듯이 단순히 DAL의 UpdateWithTransaction 메서드를 호출하는 BLL 메서드 UpdateWithTransaction 로 전달됩니다.

이 자습서에 사용되는 일괄 업데이트 알고리즘은 제품의 정보가 변경되었는지 여부에 관계없이 GridView의 행에 해당하는 의 각 행 ProductsDataTable 을 업데이트합니다. 이러한 블라인드 업데이트는 일반적으로 성능 문제가 아니지만 데이터베이스 테이블의 변경 내용을 감사하는 경우 불필요한 레코드로 이어질 수 있습니다. Batch 업데이트 수행 자습서에서 DataList를 사용하여 일괄 업데이트 인터페이스를 살펴보고 사용자가 실제로 수정한 레코드만 업데이트하는 코드를 추가했습니다. 원하는 경우 Batch 업데이트 수행의 기술을 사용하여 이 자습서의 코드를 업데이트할 수 있습니다.

참고

스마트 태그를 통해 데이터 원본을 GridView에 바인딩하는 경우 Visual Studio는 자동으로 데이터 원본의 기본 키 값을 GridView의 DataKeyNames 속성에 할당합니다. 1단계에 설명된 대로 GridView의 스마트 태그를 통해 GridView에 ObjectDataSource를 바인딩하지 않은 경우 컬렉션을 통해 DataKeys 각 행의 값에 액세스 ProductID 하려면 GridView의 DataKeyNames 속성을 ProductID로 수동으로 설정해야 합니다.

에서 BatchUpdate 사용되는 코드는 BLL의 UpdateProduct 메서드에서 사용되는 코드와 비슷하며, 기본 차이점은 메서드에서 아키텍처에서 UpdateProduct 단일 ProductRow instance 검색된다는 점입니다. 의 ProductRow 속성을 할당하는 코드는 전체 패턴과 마찬가지로 의 루프 BatchUpdate내에서 foreach 메서드와 코드 간에 UpdateProducts 동일합니다.

이 자습서를 완료하려면 제품 업데이트 단추 중 하나를 클릭할 BatchUpdate 때 메서드를 호출해야 합니다. 이러한 두 Button 컨트롤의 Click 이벤트에 대한 이벤트 처리기를 만들고 이벤트 처리기에 다음 코드를 추가합니다.

BatchUpdate();
ClientScript.RegisterStartupScript(this.GetType(), "message", 
    "alert('The products have been updated.');", true);

먼저 에 대한 호출이 BatchUpdate이루어집니다. 다음으로, ClientScript property 는 제품이 업데이트되었음을 읽는 메시지 상자를 표시하는 JavaScript를 삽입하는 데 사용됩니다.

잠시 시간을 내어 이 코드를 테스트합니다. 브라우저를 통해 방문하여 BatchUpdate.aspx 여러 행을 편집하고 제품 업데이트 단추 중 하나를 클릭합니다. 입력 유효성 검사 오류가 없다고 가정하면 제품이 업데이트되었습니다라는 메시지 상자가 표시됩니다. 업데이트의 원자성을 확인하려면 1234.56 값을 허용하지 UnitPrice 않는 제약 조건과 같은 임 CHECK 의 제약 조건을 추가하는 것이 좋습니다. 그런 다음 에서 BatchUpdate.aspx여러 레코드를 편집하여 제품 UnitPrice 값 중 하나를 사용할 수 없는 값( 1234.56 )으로 설정해야 합니다. 이렇게 하면 일괄 처리 작업 중에 다른 변경 내용으로 제품 업데이트를 클릭하면 원래 값으로 롤백될 때 오류가 발생합니다.

대체BatchUpdate메서드

방금 검사한 메서드는 BatchUpdate BLL의 GetProducts 메서드에서 모든 제품을 검색한 다음 GridView에 표시되는 레코드만 업데이트합니다. 이 방법은 GridView가 페이징을 사용하지 않는 경우에 이상적이지만, 페이징을 사용하는 경우 수백, 수천 또는 수만 개의 제품이 있을 수 있지만 GridView에는 10개의 행만 있을 수 있습니다. 이러한 경우 데이터베이스에서 모든 제품을 10개만 수정하는 것은 이상적이지 않습니다.

이러한 유형의 경우 다음 BatchUpdateAlternate 메서드를 대신 사용하는 것이 좋습니다.

private void BatchUpdateAlternate()
{
    // Enumerate the GridView's Rows collection and create a ProductRow
    ProductsBLL productsAPI = new ProductsBLL();
    Northwind.ProductsDataTable products = new Northwind.ProductsDataTable();
    foreach (GridViewRow gvRow in ProductsGrid.Rows)
    {
        // Create a new ProductRow instance
        int productID = Convert.ToInt32(ProductsGrid.DataKeys[gvRow.RowIndex].Value);
        
        Northwind.ProductsDataTable currentProductDataTable = 
            productsAPI.GetProductByProductID(productID);
        if (currentProductDataTable.Rows.Count > 0)
        {
            Northwind.ProductsRow product = currentProductDataTable[0];
            // Programmatically access the form field elements in the 
            // current GridViewRow
            TextBox productName = (TextBox)gvRow.FindControl("ProductName");
            DropDownList categories = 
                (DropDownList)gvRow.FindControl("Categories");
            TextBox unitPrice = (TextBox)gvRow.FindControl("UnitPrice");
            CheckBox discontinued = 
                (CheckBox)gvRow.FindControl("Discontinued");
            // Assign the user-entered values to the current ProductRow
            product.ProductName = productName.Text.Trim();
            if (categories.SelectedIndex == 0) 
                product.SetCategoryIDNull(); 
            else 
                product.CategoryID = Convert.ToInt32(categories.SelectedValue);
            if (unitPrice.Text.Trim().Length == 0) 
                product.SetUnitPriceNull(); 
            else 
                product.UnitPrice = Convert.ToDecimal(unitPrice.Text);
            product.Discontinued = discontinued.Checked;
            // Import the ProductRow into the products DataTable
            products.ImportRow(product);
        }
    }
    // Now have the BLL update the products data using a transaction
    productsAPI.UpdateProductsWithTransaction(products);
}

BatchMethodAlternate는 라는 productsProductsDataTable 새 를 만들어 시작합니다. 그런 다음 GridView 컬렉션을 Rows 단계별로 실행하고 각 행에 대해 BLL의 GetProductByProductID(productID) 메서드를 사용하여 특정 제품 정보를 가져옵니다. 검색된 ProductsRow instance 속성이 와 동일한 방식으로 BatchUpdate업데이트되지만 행을 업데이트한 후 DataTable의 ImportRow(DataRow) 메서드를 통해 로 products``ProductsDataTable 가져옵니다.

루프가 완료되면 products GridView의 foreach 각 행에 대해 하나의 ProductsRow instance 포함합니다. 각 인스턴스가 ProductsRow 업데이트되지 않고 에 추가 products 되었으므로 메서드 ProductsTableAdapter 에 맹목적으로 전달 UpdateWithTransaction 하면 는 각 레코드를 데이터베이스에 삽입하려고 합니다. 대신 이러한 각 행이 수정(추가되지 않음)되도록 지정해야 합니다.

이 작업은 라는 UpdateProductsWithTransactionBLL에 새 메서드를 추가하여 수행할 수 있습니다. UpdateProductsWithTransaction아래와 같이 에 있는 각 인스턴스의 ProductsRow 를 로 ProductsDataTableModified 설정한 RowState 다음 을 DAL의 UpdateWithTransaction 메서드에 전달합니다ProductsDataTable.

public int UpdateProductsWithTransaction(Northwind.ProductsDataTable products)
{
    // Mark each product as Modified
    products.AcceptChanges();
    foreach (Northwind.ProductsRow product in products)
        product.SetModified();
    // Update the data via a transaction
    return UpdateWithTransaction(products);
}

요약

GridView는 행당 기본 제공 편집 기능을 제공하지만 완전히 편집 가능한 인터페이스를 만들기 위한 지원이 부족합니다. 이 자습서에서 보았듯이 이러한 인터페이스는 가능하지만 약간의 작업이 필요합니다. 모든 행을 편집할 수 있는 GridView를 만들려면 GridView의 필드를 TemplateFields로 변환하고 에서 편집 인터페이스를 ItemTemplate 정의해야 합니다. 또한 모두 업데이트 -type 단추 웹 컨트롤을 GridView와 별도로 페이지에 추가해야 합니다. 이러한 Buttons Click 이벤트 처리기는 GridView의 Rows 컬렉션을 열거하고, 변경 내용을 에 ProductsDataTable저장하고, 업데이트된 정보를 적절한 BLL 메서드에 전달해야 합니다.

다음 자습서에서는 일괄 삭제를 위한 인터페이스를 만드는 방법을 알아보세요. 특히 각 GridView 행에는 확인란이 포함되며 모든 형식 업데이트 단추 대신 선택한 행 삭제 단추가 있습니다.

행복한 프로그래밍!

저자 정보

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.