일괄 삽입(C#)

작성자 : Scott Mitchell

PDF 다운로드

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

소개

Batch 업데이트 자습서에서는 여러 레코드를 편집할 수 있는 인터페이스를 표시하도록 GridView 컨트롤을 사용자 지정하는 방법을 살펴보았습니다. 페이지를 방문하는 사용자는 일련의 변경을 수행한 다음, 한 번의 단추 클릭으로 일괄 업데이트를 수행할 수 있습니다. 사용자가 일반적으로 한 곳에서 많은 레코드를 업데이트하는 경우 이러한 인터페이스는 데이터 삽입, 업데이트 및 삭제 개요 자습서에서 처음 살펴보던 기본 행별 편집 기능과 비교할 때 수많은 클릭 및 키보드-마우스 컨텍스트 스위치를 저장할 수 있습니다.

레코드를 추가할 때도 이 개념을 적용할 수 있습니다. Northwind Traders에서 일반적으로 특정 범주에 대한 여러 제품을 포함하는 공급업체로부터 배송을 받는다고 상상해 보십시오. 예를 들어 도쿄 트레이더로부터 6가지 차와 커피 제품을 배송받을 수 있습니다. 사용자가 DetailsView 컨트롤을 통해 한 번에 하나씩 6개의 제품을 입력하는 경우 동일한 범주(음료), 동일한 공급자(도쿄 트레이더), 동일한 중단된 값(False) 및 주문 값에 대한 동일한 단위(0)를 선택해야 합니다. 이 반복적인 데이터 항목은 시간이 오래 걸릴 뿐만 아니라 오류가 발생하기 쉽습니다.

약간의 작업을 통해 사용자가 공급자 및 범주를 한 번 선택하고, 일련의 제품 이름과 단가를 입력한 다음, 단추를 클릭하여 데이터베이스에 새 제품을 추가할 수 있는 일괄 처리 삽입 인터페이스를 만들 수 있습니다(그림 1 참조). 각 제품이 추가되면 해당 ProductNameUnitPrice 데이터 필드에는 TextBoxes에 입력된 값이 할당되고, 해당 CategoryID 값과 SupplierID 값에는 폼의 위쪽에 있는 DropDownLists의 값이 할당됩니다. 및 UnitsOnOrder 값은 Discontinued 각각 및 0의 false 하드 코딩된 값으로 설정됩니다.

Batch 삽입 인터페이스

그림 1: 일괄 삽입 인터페이스(전체 크기 이미지를 보려면 클릭)

이 자습서에서는 그림 1에 표시된 일괄 삽입 인터페이스를 구현하는 페이지를 만듭니다. 이전 두 자습서와 마찬가지로 원자성을 보장하기 위해 트랜잭션의 scope 내에 삽입을 래핑합니다. 시작해 보겠습니다!

1단계: 디스플레이 인터페이스 만들기

이 자습서는 디스플레이 영역과 삽입 영역의 두 영역으로 나뉘어 있는 단일 페이지로 구성됩니다. 이 단계에서 만들 디스플레이 인터페이스는 GridView의 제품을 표시하고 제품 배송 처리라는 단추가 포함되어 있습니다. 이 단추를 클릭하면 표시 인터페이스가 그림 1에 표시된 삽입 인터페이스로 바뀝니다. 표시 인터페이스는 배송 또는 취소에서 제품 추가 단추를 클릭한 후 반환됩니다. 2단계에서 삽입 인터페이스를 만듭니다.

한 번에 하나의 인터페이스만 표시되는 두 개의 인터페이스가 있는 페이지를 만들 때 각 인터페이스는 일반적으로 다른 컨트롤의 컨테이너 역할을 하는 패널 웹 컨트롤 내에 배치됩니다. 따라서 페이지에는 각 인터페이스에 대해 각각 하나씩 두 개의 패널 컨트롤이 있습니다.

먼저 폴더에서 BatchInsert.aspxBatchData 페이지를 열고 도구 상자에서 Designer 패널을 끕니다(그림 2 참조). Panel의 ID 속성을 로 DisplayInterface설정합니다. Designer 패널을 추가할 때 해당 HeightWidth 속성은 각각 50px 및 125px로 설정됩니다. 속성 창 이러한 속성 값을 지웁 수 있습니다.

도구 상자에서 패널을 Designer 끌어다 놓습니다.

그림 2: 도구 상자에서 Designer 패널 끌기(전체 크기 이미지를 보려면 클릭)

그런 다음 Button 및 GridView 컨트롤을 패널로 끕니다. Button의 ID 속성을 로 ProcessShipment 설정하고 해당 Text 속성을 제품 배송 처리로 설정합니다. GridView 속성을 IDProductsGrid 로 설정하고 스마트 태그에서 라는 ProductsDataSource새 ObjectDataSource에 바인딩합니다. 클래스의 GetProducts 메서드에서 해당 데이터를 가져오도록 ObjectDataSource를 ProductsBLL 구성합니다. 이 GridView는 데이터를 표시하는 데만 사용되므로 UPDATE, INSERT 및 DELETE 탭의 드롭다운 목록을 (없음)으로 설정합니다. 마침을 클릭하여 데이터 원본 구성 마법사를 완료합니다.

ProductsBLL 클래스의 GetProducts 메서드에서 반환된 데이터 표시

그림 3: 클래스의 GetProducts 메서드에서 ProductsBLL 반환된 데이터 표시(전체 크기 이미지를 보려면 클릭)

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

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

ObjectDataSource 마법사를 완료하면 Visual Studio에서 제품 데이터 필드에 대한 BoundFields 및 CheckBoxField를 추가합니다. , , CategoryName, SupplierNameUnitPriceDiscontinued 필드를 제외한 ProductName모든 필드를 제거합니다. 모든 미적 사용자 지정을 자유롭게 할 수 있습니다. 필드의 형식을 UnitPrice 통화 값으로 지정하고, 필드의 순서를 변경하고, 여러 필드 HeaderText 값의 이름을 변경하기로 결정했습니다. 또한 GridView의 스마트 태그에서 페이징 사용 및 정렬 사용 확인란을 선택하여 페이징 및 정렬 지원을 포함하도록 GridView를 구성합니다.

Panel, Button, GridView 및 ObjectDataSource 컨트롤을 추가하고 GridView의 필드를 사용자 지정한 후 페이지의 선언적 태그는 다음과 유사하게 표시됩니다.

<asp:Panel ID="DisplayInterface" runat="server">
    <p>
        <asp:Button ID="ProcessShipment" runat="server" 
            Text="Process Product Shipment" /> 
    </p>
    <asp:GridView ID="ProductsGrid" runat="server" AllowPaging="True" 
        AllowSorting="True" AutoGenerateColumns="False" 
        DataKeyNames="ProductID" DataSourceID="ProductsDataSource">
        <Columns>
            <asp:BoundField DataField="ProductName" HeaderText="Product" 
                SortExpression="ProductName" />
            <asp:BoundField DataField="CategoryName" HeaderText="Category" 
                ReadOnly="True" SortExpression="CategoryName" />
            <asp:BoundField DataField="SupplierName" HeaderText="Supplier" 
                ReadOnly="True" SortExpression="SupplierName" />
            <asp:BoundField DataField="UnitPrice" DataFormatString="{0:c}" 
                HeaderText="Price" HtmlEncode="False" 
                SortExpression="UnitPrice">
                <ItemStyle HorizontalAlign="Right" />
            </asp:BoundField>
            <asp:CheckBoxField DataField="Discontinued" HeaderText="Discontinued" 
                SortExpression="Discontinued">
                <ItemStyle HorizontalAlign="Center" />
            </asp:CheckBoxField>
        </Columns>
    </asp:GridView>
    <asp:ObjectDataSource ID="ProductsDataSource" runat="server" 
        OldValuesParameterFormatString="original_{0}"
        SelectMethod="GetProducts" TypeName="ProductsBLL">
    </asp:ObjectDataSource>
</asp:Panel>

Button 및 GridView에 대한 태그는 여는 태그와 닫는 <asp:Panel> 태그 내에 표시됩니다. 이러한 컨트롤은 패널 내에 DisplayInterface 있으므로 패널 속성을 Visible 로 설정하기만 하면 해당 컨트롤 false을 숨길 수 있습니다. 3단계에서는 단추 클릭에 대한 응답으로 패널 속성을 Visible 프로그래밍 방식으로 변경하여 다른 인터페이스를 숨기는 동안 한 인터페이스를 표시하는 방법을 살펴봅니다.

잠시 시간을 내어 브라우저를 통해 진행 상황을 확인합니다. 그림 5에서 볼 수 있듯이 한 번에 10개의 제품을 나열하는 GridView 위에 제품 배송 처리 단추가 표시됩니다.

GridView는 제품 및 제품 정렬 및 페이징 기능을 Lists.

그림 5: 제품 및 제품 정렬 및 페이징 기능을 Lists GridView(전체 크기 이미지를 보려면 클릭)

2단계: 삽입 인터페이스 만들기

표시 인터페이스가 완료되면 삽입 인터페이스를 만들 준비가 된 것입니다. 이 자습서에서는 단일 공급자 및 범주 값을 묻는 메시지를 표시한 다음 사용자가 최대 5개의 제품 이름과 단가 값을 입력할 수 있도록 하는 삽입 인터페이스를 만들어 보겠습니다. 이 인터페이스를 사용하면 사용자는 모두 동일한 범주와 공급자를 공유하지만 고유한 제품 이름과 가격을 갖는 5개의 새 제품을 추가할 수 있습니다.

먼저 도구 상자에서 Designer 패널을 끌어 기존 DisplayInterface 패널 아래에 배치합니다. 새로 추가된 ID 이 Panel의 속성을 로 InsertingInterface 설정하고 해당 Visible 속성을 로 false설정합니다. 3단계에서 Panel의 Visible 속성을 true 로 설정하는 InsertingInterface 코드를 추가합니다. 또한 Panel HeightWidth 속성 값을 지웁니다.

다음으로 그림 1에 다시 표시된 삽입 인터페이스를 만들어야 합니다. 이 인터페이스는 다양한 HTML 기술을 통해 만들 수 있지만 4열, 7행 테이블이라는 매우 간단한 인터페이스를 사용합니다.

참고

HTML <table> 요소에 대한 태그를 입력할 때 원본 뷰를 사용하는 것이 좋습니다. Visual Studio에는 Designer 통해 요소를 추가 <table> 하기 위한 도구가 있지만, Designer 태그에 설정에 대한 style 특화되지 않은 를 삽입하려고 하는 것 같습니다. 태그를 <table> 만든 후에는 일반적으로 웹 컨트롤을 추가하고 해당 속성을 설정하기 위해 Designer 돌아갑니다. 미리 결정된 열과 행이 있는 테이블을 만들 때 Table Web 컨트롤에 배치된 웹 컨트롤은 패턴을 사용하여서만 액세스할 수 있으므로 Table Web 컨트롤 보다는 정적 HTML을 사용하는 FindControl("controlID") 것이 좋습니다. 그러나 Table Web 컨트롤은 프로그래밍 방식으로 생성할 수 있으므로 동적으로 크기가 지정된 테이블(행 또는 열이 일부 데이터베이스 또는 사용자 지정 조건을 기반으로 하는 테이블)에 대해 Table Web 컨트롤을 사용합니다.

패널의 태그 내에 <asp:Panel> 다음 태그를 InsertingInterface 입력합니다.

<table class="DataWebControlStyle" cellspacing="0">
    <tr class="BatchInsertHeaderRow">
        <td class="BatchInsertLabel">Supplier:</td>
        <td></td>
        <td class="BatchInsertLabel">Category:</td>
        <td></td>
    </tr>
    <tr class="BatchInsertRow">
        <td class="BatchInsertLabel">Product:</td>
        <td></td>
        <td class="BatchInsertLabel">Price:</td>
        <td></td>
    </tr>
    <tr class="BatchInsertAlternatingRow">
        <td class="BatchInsertLabel">Product:</td>
        <td></td>
        <td class="BatchInsertLabel">Price:</td>
        <td></td>
    </tr>
    <tr class="BatchInsertRow">
        <td class="BatchInsertLabel">Product:</td>
        <td></td>
        <td class="BatchInsertLabel">Price:</td>
        <td></td>
    </tr>
    <tr class="BatchInsertAlternatingRow">
        <td class="BatchInsertLabel">Product:</td>
        <td></td>
        <td class="BatchInsertLabel">Price:</td>
        <td></td>
    </tr>
    <tr class="BatchInsertRow">
        <td class="BatchInsertLabel">Product:</td>
        <td></td>
        <td class="BatchInsertLabel">Price:</td>
        <td></td>
    </tr>
    <tr class="BatchInsertFooterRow">
        <td colspan="4">
        </td>
    </tr>
</table>

<table> 태그에는 아직 웹 컨트롤이 포함되지 않습니다. 잠시 후에 추가하겠습니다. 각 <tr> 요소에는 특정 CSS 클래스 설정이 BatchInsertHeaderRow 포함됩니다. 공급자 및 범주 DropDownLists가 이동하는 머리글 행의 경우, BatchInsertFooterRow 배송 및 취소 단추에서 제품 추가가 이동하는 바닥글 행의 경우, 제품 및 단가 TextBox 컨트롤을 포함할 행에 대한 값과 BatchInsertAlternatingRow 번갈아 BatchInsertRow 가며 값이 포함됩니다. 이 자습서 전체에서 Styles.css 사용한 GridView 및 DetailsView 컨트롤과 유사한 모양을 삽입 인터페이스에 제공하기 위해 파일에 해당 CSS 클래스를 만들었습니다. 이러한 CSS 클래스는 다음과 같습니다.

/*** Styles for ~/BatchData/BatchInsert.aspx tutorial ***/
.BatchInsertLabel
{
    font-weight: bold;
    text-align: right;
}
.BatchInsertHeaderRow td
{
    color: White;
    background-color: #900;
    padding: 11px;
}
.BatchInsertFooterRow td
{
    text-align: center;
    padding-top: 5px;
}
.BatchInsertRow
{
}
.BatchInsertAlternatingRow
{
    background-color: #fcc;
}

이 태그를 입력한 후 디자인 보기로 돌아갑니다. 그림 <table> 6과 같이 Designer 4열 7행 테이블로 표시됩니다.

삽입 인터페이스는 4열 Seven-Row 테이블로 구성됩니다.

그림 6: 삽입 인터페이스는 4열 Seven-Row 테이블로 구성됩니다(전체 크기 이미지를 보려면 클릭).

이제 삽입 인터페이스에 웹 컨트롤을 추가할 준비가 되었습니다. 도구 상자에서 두 개의 DropDownLists를 공급자용이고 다른 하나는 범주에 해당하는 테이블의 적절한 셀로 끌어옵니다.

공급자 DropDownList의 ID 속성을 로 Suppliers 설정하고 라는 SuppliersDataSource새 ObjectDataSource에 바인딩합니다. 클래스의 메서드에서 SuppliersBLL 해당 데이터를 검색하고 UPDATE 탭의 GetSuppliers 드롭다운 목록을 (없음)으로 설정하도록 새 ObjectDataSource를 구성합니다. 마침을 클릭하여 마법사를 완료합니다.

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

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

DropDownList에서 SuppliersCompanyName 데이터 필드를 표시하고 데이터 필드를 해당 값으로 사용하도록 SupplierID 합니다 ListItem .

CompanyName 데이터 필드를 표시하고 SupplierID를 값으로 사용

그림 8: 데이터 필드 표시 CompanyName 및 값으로 사용 SupplierID (전체 크기 이미지를 보려면 클릭)

두 번째 DropDownList Categories 의 이름을 로 지정하고 라는 CategoriesDataSource새 ObjectDataSource에 바인딩합니다. CategoriesDataSource 클래스의 메서드를 사용하도록 CategoriesBLL ObjectDataSource를 구성하고, UPDATE 및 DELETE 탭의 GetCategories 드롭다운 목록을 (없음)으로 설정하고 마침을 클릭하여 마법사를 완료합니다. 마지막으로 DropDownList에 데이터 필드가 CategoryName 표시되고 를 CategoryID 값으로 사용합니다.

이러한 두 DropDownLists가 추가되고 적절하게 구성된 ObjectDataSources에 바인딩된 후 화면은 그림 9와 유사하게 표시됩니다.

이제 헤더 행에 공급자 및 범주 드롭다운 목록이 포함됩니다.

그림 9: 이제 머리글 행에 SuppliersCategories DropDownLists가 포함되어 있습니다(전체 크기 이미지를 보려면 클릭).

이제 각 새 제품의 이름과 가격을 수집하기 위해 TextBoxes를 만들어야 합니다. 도구 상자에서 5개의 제품 이름 및 가격 행 각각에 대한 Designer TextBox 컨트롤을 끌어옵니다. ID TextBoxesProductName1의 속성을 , , UnitPrice1, ProductName2UnitPrice2, ProductName3, UnitPrice3등으로 설정합니다.

각 단가 TextBoxes 다음에 CompareValidator를 추가하고 속성을 적절한 ID로 설정합니다ControlToValidate. 또한 속성을 로 Operator 설정하고 ValueToCompare 를 0으로 설정하고 를 TypeCurrency설정합니다.GreaterThanEqual 이러한 설정은 CompareValidator에 입력된 가격이 0보다 크거나 같은 유효한 통화 값인지 확인하도록 지시합니다. Text 속성을 *로 설정하고 가격은 ErrorMessage 0보다 크거나 같아야 합니다. 또한 통화 기호를 생략하세요.

참고

데이터베이스 테이블의 필드에 값이 허용되지 NULL 않더라도 ProductName 삽입 인터페이스에는 RequiredFieldValidator 컨트롤이 Products 포함되지 않습니다. 사용자가 최대 5개의 제품을 입력하도록 허용하려고 하기 때문입니다. 예를 들어 사용자가 처음 세 행의 제품 이름과 단가를 입력하고 마지막 두 행을 비워 두면 시스템에 세 개의 새 제품만 추가합니다. ProductName 그러나 가 필요하므로 단가가 입력되면 해당 제품 이름 값이 제공되는지 확인하기 위해 프로그래밍 방식으로 검사 합니다. 4단계에서 이 검사 해결하겠습니다.

사용자 입력의 유효성을 검사할 때 값에 통화 기호가 포함된 경우 CompareValidator는 잘못된 데이터를 보고합니다. 가격을 입력할 때 통화 기호를 생략하도록 사용자에게 지시하는 시각적 신호 역할을 하려면 각 단가 TextBoxes 앞에 $를 추가합니다.

마지막으로 Panel 내에 ValidationSummary 컨트롤을 InsertingInterface 추가하고 해당 속성을 로true, 속성을 ShowMessageBoxShowSummaryfalse로 설정합니다. 이러한 설정을 사용하면 사용자가 잘못된 단가 값을 입력하면 잘못된 TextBox 컨트롤 옆에 별표가 표시되고 ValidationSummary는 앞에서 지정한 오류 메시지를 표시하는 클라이언트 쪽 메시지 상자를 표시합니다.

이 시점에서 화면은 그림 10과 유사하게 표시됩니다.

이제 삽입 인터페이스에는 제품 이름 및 가격에 대한 TextBox가 포함됩니다.

그림 10: 이제 삽입 인터페이스에는 제품 이름 및 가격에 대한 TextBox가 포함됩니다(전체 크기 이미지를 보려면 클릭).

다음으로 발송물에서 제품 추가 및 취소 단추를 바닥글 행에 추가해야 합니다. 도구 상자에서 두 개의 단추 컨트롤을 삽입 인터페이스의 바닥글로 끌어 단추 ID 속성을 AddProducts 로 설정하고 속성을 각각 배송 및 CancelButtonText 취소에서 제품 추가로 설정합니다. 또한 컨트롤의 CausesValidation 속성을 falseCancelButton 설정합니다.

마지막으로 두 인터페이스에 대한 상태 메시지를 표시하는 레이블 웹 컨트롤을 추가해야 합니다. 예를 들어 사용자가 새 제품 배송을 성공적으로 추가하는 경우 디스플레이 인터페이스로 돌아가서 확인 메시지를 표시하려고 합니다. 그러나 사용자가 새 제품에 대한 가격을 제공하지만 제품 이름을 벗어난 경우 필드가 필요하므로 경고 메시지를 ProductName 표시해야 합니다. 두 인터페이스 모두에 대해 이 메시지를 표시해야 하므로 패널 외부의 페이지 맨 위에 배치합니다.

도구 상자에서 레이블 웹 컨트롤을 Designer 페이지 맨 위로 끌어옵니다. 속성을 로 IDStatusLabel설정하고 속성을 지우 Text 고 및 EnableViewState 속성을 로 Visiblefalse설정합니다. 이전 자습서에서 볼 수 있듯이 속성을 로 false 설정 EnableViewState 하면 Label의 속성 값을 프로그래밍 방식으로 변경하고 후속 포스트백에서 자동으로 기본값으로 되돌리기 수 있습니다. 이렇게 하면 후속 포스트백에서 사라지는 일부 사용자 작업에 대한 응답으로 상태 메시지를 표시하기 위한 코드가 간소화됩니다. 마지막으로 컨트롤의 CssClass 속성을 경고로 설정합니다StatusLabel. 이 속성은 텍스트를 크고 기울임꼴, 굵게, 빨간색 글꼴로 표시하는 에 Styles.css 정의된 CSS 클래스의 이름입니다.

그림 11은 레이블이 추가되고 구성된 후 Visual Studio Designer 보여줍니다.

StatusLabel 컨트롤을 두 패널 컨트롤 위에 놓습니다.

그림 11: 컨트롤을 StatusLabel 두 개의 패널 컨트롤 위에 놓습니다(전체 크기 이미지를 보려면 클릭).

3단계: 표시와 삽입 인터페이스 간 전환

이 시점에서 표시 및 삽입 인터페이스에 대한 태그를 완료했지만 여전히 두 가지 작업이 남아 있습니다.

  • 디스플레이와 삽입 인터페이스 간 전환
  • 배송 중인 제품을 데이터베이스에 추가

현재 표시 인터페이스는 표시되지만 삽입 인터페이스는 숨겨집니다. DisplayInterface 이는 Panel의 Visible 속성이 (기본값)으로 true 설정된 반면 InsertingInterface Panel의 Visible 속성은 로 설정되었기 때문false입니다. 두 인터페이스 간에 전환하려면 각 컨트롤의 Visible 속성 값을 토글하기만 하면 됩니다.

제품 배송 처리 단추를 클릭할 때 표시 인터페이스에서 삽입 인터페이스로 이동하려고 합니다. 따라서 다음 코드를 포함하는 이 Button 이벤트에 Click 대한 이벤트 처리기를 만듭니다.

protected void ProcessShipment_Click(object sender, EventArgs e)
{
    DisplayInterface.Visible = false;
    InsertingInterface.Visible = true;
}

이 코드는 단순히 패널을 DisplayInterface 숨기고 패널을 표시합니다 InsertingInterface .

다음으로, 삽입 인터페이스에서 배송에서 제품 추가 및 취소 단추 컨트롤에 대한 이벤트 처리기를 만듭니다. 이러한 단추 중 하나를 클릭하면 디스플레이 인터페이스로 다시 되돌리기 합니다. 두 단추 컨트롤에 대한 이벤트 처리기를 만들어 Click 일시적으로 추가할 메서드인 를 호출 ReturnToDisplayInterface합니다. 패널을 InsertingInterface 숨기고 패널을 DisplayInterface 표시하는 것 외에도 메서드는 ReturnToDisplayInterface 웹 컨트롤을 편집 전 상태로 반환해야 합니다. 여기에는 DropDownLists SelectedIndex 속성을 0으로 설정하고 TextBox 컨트롤의 속성을 지우 Text 는 작업이 포함됩니다.

참고

디스플레이 인터페이스로 돌아가기 전에 컨트롤을 편집 전 상태로 되돌리지 않은 경우 발생할 수 있는 작업을 고려합니다. 사용자가 제품 배송 처리 단추를 클릭하고 배송에서 제품을 입력한 다음 배송에서 제품 추가를 클릭할 수 있습니다. 이렇게 하면 제품이 추가되고 사용자가 디스플레이 인터페이스로 반환됩니다. 이 시점에서 사용자는 다른 배송을 추가할 수 있습니다. 제품 배송 처리 단추를 클릭하면 삽입 인터페이스로 돌아가지만 DropDownList 선택 항목 및 TextBox 값은 여전히 이전 값으로 채워집니다.

protected void AddProducts_Click(object sender, EventArgs e)
{
    // TODO: Save the products
    // Revert to the display interface
    ReturnToDisplayInterface();
}
protected void CancelButton_Click(object sender, EventArgs e)
{
    // Revert to the display interface
    ReturnToDisplayInterface();
}
const int firstControlID = 1;
const int lastControlID = 5;
private void ReturnToDisplayInterface()
{
    // Reset the control values in the inserting interface
    Suppliers.SelectedIndex = 0;
    Categories.SelectedIndex = 0;
    for (int i = firstControlID; i <= lastControlID; i++)
    {
        ((TextBox)InsertingInterface.FindControl("ProductName" + i.ToString())).Text =
            string.Empty;
        ((TextBox)InsertingInterface.FindControl("UnitPrice" + i.ToString())).Text = 
            string.Empty;
    }
    DisplayInterface.Visible = true;
    InsertingInterface.Visible = false;
}

Click 이벤트 처리기는 단순히 메서드를 ReturnToDisplayInterface 호출하지만 4단계의 배송 Click 에서 제품 추가 이벤트 처리기로 돌아가 제품을 저장하는 코드를 추가합니다. ReturnToDisplayInterface는 및 Categories DropDownLists를 첫 번째 옵션으로 반환하여 Suppliers 시작합니다. 두 상 firstControlID 수와 lastControlID 삽입 인터페이스에서 제품 이름 및 단가 TextBox의 이름을 지정하는 데 사용되는 시작 및 끝 컨트롤 인덱스 값을 표시하고 TextBox 컨트롤의 속성을 빈 문자열로 다시 설정하는 Text 루프의 for 범위에서 사용됩니다. 마지막으로 삽입 인터페이스가 숨겨지고 표시 인터페이스가 표시되도록 Panels Visible 속성이 다시 설정됩니다.

잠시 시간을 내어 브라우저에서 이 페이지를 테스트합니다. 페이지를 처음 방문할 때 그림 5와 같이 표시 인터페이스가 표시됩니다. 제품 배송 처리 단추를 클릭합니다. 페이지가 포스트백되고 그림 12와 같이 삽입 인터페이스가 표시됩니다. 배송에서 제품 추가 또는 취소 단추를 클릭하면 디스플레이 인터페이스로 돌아갑니다.

참고

삽입 인터페이스를 보는 동안 잠시 시간을 내어 단가 TextBoxes에서 CompareValidators를 테스트합니다. 잘못된 통화 값 또는 값이 0보다 작은 가격으로 배송에서 제품 추가 단추를 클릭하면 클라이언트 쪽 메시지 상자 경고가 표시됩니다.

제품 배송 처리 단추를 클릭하면 삽입 인터페이스가 표시됩니다.

그림 12: 제품 배송 처리 단추를 클릭한 후 삽입 인터페이스가 표시됩니다(전체 크기 이미지를 보려면 클릭).

4단계: 제품 추가

이 자습서에 남아 있는 것은 배송 단추 Click 에서 제품 추가 이벤트 처리기에서 데이터베이스에 제품을 저장하는 것입니다. 이 작업은 를 만들고 ProductsDataTable 제공된 각 제품 이름에 대한 instance 추가하여 ProductsRow 수행할 수 있습니다. 이러한 ProductsRow 메서드가 추가되면 를 전달하는 ProductsDataTable클래스의 UpdateWithTransaction 메서드를 ProductsBLL 호출합니다. UpdateWithTransaction트랜잭션 내 데이터베이스 수정 래핑 자습서에서 다시 만든 메서드는 를 s UpdateWithTransaction 메서드에 ProductsTableAdapter 전달합니다ProductsDataTable. 여기에서 ADO.NET 트랜잭션이 시작되고 TableAdapter는 DataTable에 추가 ProductsRow 된 각 데이터베이스에 대한 문을 발급 INSERT 합니다. 모든 제품이 오류 없이 추가되었다고 가정하면 트랜잭션이 커밋되고, 그렇지 않으면 롤백됩니다.

배송 단추에서 Click 제품 추가 이벤트 처리기의 코드도 약간의 오류 검사를 수행해야 합니다. 삽입 인터페이스에 RequiredFieldValidators가 사용되지 않으므로 사용자는 이름을 생략하는 동안 제품 가격을 입력할 수 있습니다. 제품 이름이 필요하므로 이러한 조건이 전개되면 사용자에게 경고하고 삽입을 진행하지 않도록 해야 합니다. 전체 Click 이벤트 처리기 코드는 다음과 같습니다.

protected void AddProducts_Click(object sender, EventArgs e)
{
    // Make sure that the UnitPrice CompareValidators report valid data...
    if (!Page.IsValid)
        return;
    // Add new ProductsRows to a ProductsDataTable...
    Northwind.ProductsDataTable products = new Northwind.ProductsDataTable();
    for (int i = firstControlID; i <= lastControlID; i++)
    {
        // Read in the values for the product name and unit price
        string productName = ((TextBox)InsertingInterface.FindControl
            ("ProductName" + i.ToString())).Text.Trim();
        string unitPrice = ((TextBox)InsertingInterface.FindControl
            ("UnitPrice" + i.ToString())).Text.Trim();
        // Ensure that if unitPrice has a value, so does productName
        if (unitPrice.Length > 0 && productName.Length == 0)
        {
            // Display a warning and exit this event handler
            StatusLabel.Text = "If you provide a unit price you must also " +
                "include the name of the product.";
            StatusLabel.Visible = true;
            return;
        }
        // Only add the product if a product name value is provided
        if (productName.Length > 0)
        {
            // Add a new ProductsRow to the ProductsDataTable
            Northwind.ProductsRow newProduct = products.NewProductsRow();
            // Assign the values from the web page
            newProduct.ProductName = productName;
            newProduct.SupplierID = Convert.ToInt32(Suppliers.SelectedValue);
            newProduct.CategoryID = Convert.ToInt32(Categories.SelectedValue);
            if (unitPrice.Length > 0)
                newProduct.UnitPrice = Convert.ToDecimal(unitPrice);
            // Add any "default" values
            newProduct.Discontinued = false;
            newProduct.UnitsOnOrder = 0;
            products.AddProductsRow(newProduct);
        }
    }
    // If we reach here, see if there were any products added
    if (products.Count > 0)
    {
        // Add the new products to the database using a transaction
        ProductsBLL productsAPI = new ProductsBLL();
        productsAPI.UpdateWithTransaction(products);
        // Rebind the data to the grid so that the products just added are displayed
        ProductsGrid.DataBind();
        // Display a confirmation (don't use the Warning CSS class, though)
        StatusLabel.CssClass = string.Empty;
        StatusLabel.Text = string.Format(
            "{0} products from supplier {1} have been added and filed under " + 
            "category {2}.", products.Count, Suppliers.SelectedItem.Text, 
            Categories.SelectedItem.Text);
        StatusLabel.Visible = true;
        // Revert to the display interface
        ReturnToDisplayInterface();
    }
    else
    {
        // No products supplied!
        StatusLabel.Text = "No products were added. Please enter the product " + 
            "names and unit prices in the textboxes.";
        StatusLabel.Visible = true;
    }
}

이벤트 처리기는 속성이 값을 true반환하도록 하여 Page.IsValid 시작합니다. 가 를 반환 false하는 경우 이는 CompareValidators 중 하나 이상이 잘못된 데이터를 보고하고 있음을 의미합니다. 이러한 경우 입력한 제품을 삽입하려고 하지 않거나 사용자가 입력한 단가 값을 ProductsRow s UnitPrice 속성에 할당하려고 할 때 예외가 발생합니다.

다음으로 새 ProductsDataTable instance 만들어집니다(products). for 루프는 제품 이름 및 단가 TextBoxes를 반복하는 데 사용되며 Text 속성은 지역 변수 productNameunitPrice로 읽습니다. 사용자가 단가 값을 입력했지만 해당 제품 이름 StatusLabel 에는 입력하지 않은 경우 단가를 제공하는 경우 제품 이름도 포함해야 하며 이벤트 처리기가 종료됩니다.

제품 이름이 제공된 경우 s NewProductsRow 메서드를 사용하여 ProductsDataTableProductsRow instance 만들어집니다. 이 새 ProductsRow instance ProductName 속성은 현재 제품 이름 TextBox SupplierID 로 설정되고 및 CategoryID 속성은 삽입 인터페이스 헤더의 DropDownLists 속성에 할당 SelectedValue 됩니다. 사용자가 제품 가격에 대한 값을 입력한 경우 instance 속성에 ProductsRow 할당됩니다. 그렇지 않으면 속성이 할당되지 않은 상태로 유지되어 데이터베이스에 값 UnitPrice 이 생성됩니다NULL.UnitPrice 마지막으로 DiscontinuedUnitsOnOrder 속성은 각각 하드 코딩된 값 false 과 0에 할당됩니다.

속성이 instance 할당 ProductsRow 된 후 에 추가ProductsDataTable됩니다.

루프가 for 완료되면 제품이 추가되었는지 여부를 검사. 사용자는 제품 이름 또는 가격을 입력하기 전에 배송에서 제품 추가를 클릭했을 수 있습니다. 에 제품이 하나 이상 있는 ProductsDataTableProductsBLL 경우 클래스의 UpdateWithTransaction 메서드가 호출됩니다. 다음으로, 새로 추가된 제품이 디스플레이 인터페이스에 ProductsGrid 표시되도록 데이터가 GridView로 다시 연결됩니다. 는 StatusLabel 확인 메시지를 표시하도록 업데이트되고 ReturnToDisplayInterface 가 호출되어 삽입 인터페이스를 숨기고 표시 인터페이스를 표시합니다.

입력한 제품이 없으면 삽입 인터페이스가 계속 표시되지만 제품이 추가되지 않았습니다. 표시되는 텍스트 상자에 제품 이름 및 단가를 입력하세요.

그림 13, 14 및 15는 실행 중인 삽입 및 표시 인터페이스를 보여 줍니다. 그림 13에서 사용자는 해당 제품 이름 없이 단가 값을 입력했습니다. 그림 14는 3개의 새 제품이 성공적으로 추가된 후의 디스플레이 인터페이스를 보여 주며, 그림 15에서는 GridView에 새로 추가된 제품 중 두 개(세 번째 제품은 이전 페이지에 있습니다)를 보여 줍니다.

단가를 입력할 때 제품 이름이 필요합니다.

그림 13: 단가를 입력할 때 제품 이름이 필요합니다(전체 크기 이미지를 보려면 클릭).

공급 업체 마유미에 대한 세 가지 새로운 채소가 추가되었습니다

그림 14: 공급업체 Mayumi의 새 채소 3개가 추가되었습니다(전체 크기 이미지를 보려면 클릭).

새 제품은 GridView의 마지막 페이지에서 찾을 수 있습니다.

그림 15: GridView의 마지막 페이지에서 새 제품을 찾을 수 있습니다(전체 크기 이미지를 보려면 클릭).

참고

이 자습서에서 사용되는 일괄 삽입 논리는 트랜잭션의 scope 내의 삽입을 래핑합니다. 이를 확인하려면 의도적으로 데이터베이스 수준 오류를 발생합니다. 예를 들어 DropDownList에서 선택한 값 Categories 에 새 ProductsRow instance CategoryID 속성을 할당하는 대신 와 같은 i * 5값에 할당합니다. 다음은 i 루프 인덱서이며 1에서 5 사이의 값이 있습니다. 따라서 일괄 삽입에 둘 이상의 제품을 추가할 때 첫 번째 제품에는 유효한 CategoryID 값(5)이 있지만 후속 제품에는 CategoryID 테이블의 값과 CategoryID 일치하지 않는 값 Categories 이 있습니다. 순 효과는 첫 번째 INSERT 가 성공하는 동안 후속 키가 외래 키 제약 조건 위반으로 실패한다는 것입니다. 일괄 삽입은 원자성이므로 첫 번째 INSERT 삽입은 롤백되어 일괄 삽입 프로세스가 시작되기 전에 데이터베이스를 해당 상태로 되돌려 옵니다.

요약

이 자습서와 이전 두 자습서에서는 데이터 일괄 처리를 업데이트, 삭제 및 삽입할 수 있는 인터페이스를 만들었으며, 모두 트랜잭션 자습서 내의 데이터베이스 수정 래핑 에서 데이터 액세스 계층에 추가한 트랜잭션 지원을 사용했습니다. 특정 시나리오의 경우 이러한 일괄 처리 사용자 인터페이스는 기본 데이터의 무결성을 유지하면서 클릭, 포스트백 및 키보드-마우스 컨텍스트 스위치 수를 줄임으로써 최종 사용자 효율성을 크게 향상시킵니다.

이 자습서에서는 일괄 처리된 데이터 작업을 완료합니다. 다음 자습서 집합에서는 TableAdapter 메서드의 저장 프로시저 사용, DAL에서 연결 및 명령 수준 설정 구성, 연결 문자열 암호화 등 다양한 고급 데이터 액세스 계층 시나리오를 살펴봅니다.

행복한 프로그래밍!

저자 정보

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

특별 감사

이 자습서 시리즈는 많은 유용한 검토자가 검토했습니다. 이 자습서의 수석 검토자는 힐튼 기세나우와 S ren Jacob Lauritsen이었습니다. 예정된 MSDN 문서를 검토하는 데 관심이 있으신가요? 그렇다면 에 줄을 놓습니다 mitchell@4GuysFromRolla.com.