작성자: 스콧 미첼
단일 작업에서 여러 데이터베이스 레코드를 삭제하는 방법을 알아봅니다. 사용자 인터페이스 계층에서는 이전 자습서에서 만든 향상된 GridView를 기반으로 합니다. 데이터 액세스 계층에서 트랜잭션 내에서 여러 삭제 작업을 래핑하여 모든 삭제가 성공하거나 모든 삭제가 롤백되도록 합니다.
소개
이전 자습서에서는 완전히 편집 가능한 GridView를 사용하여 일괄 처리 편집 인터페이스를 만드는 방법을 살펴보했습니다. 사용자가 일반적으로 많은 레코드를 한 번에 편집하는 경우 일괄 처리 편집 인터페이스에는 훨씬 적은 포스트백 및 키보드-마우스 컨텍스트 스위치가 필요하므로 최종 사용자의 효율성이 향상됩니다. 이 기술은 사용자가 한 번의 이동으로 많은 레코드를 삭제하는 것이 일반적인 페이지에도 마찬가지로 유용합니다.
온라인 전자 메일 클라이언트를 사용한 사람은 이미 가장 일반적인 일괄 처리 삭제 인터페이스 중 하나에 익숙합니다. 그리드의 각 행에 있는 확인란에 해당하는 모든 선택 항목 삭제 단추가 있습니다(그림 1 참조). 이 자습서는 웹 기반 인터페이스와 일련의 레코드를 단일 원자성 작업으로 삭제하는 방법을 만드는 이전 자습서의 모든 작업을 이미 수행했기 때문에 다소 짧습니다.
GridView에 확인란 열 추가 자습서에서는 확인란 열이 있는 GridView를 만들었으며, 트랜잭션으로 데이터베이스 수정 감싸기 자습서에서는 BLL에서 트랜잭션을 사용하여 List<T>
ProductID
값을 삭제하는 메서드를 만들었습니다. 이 자습서에서는 이전 경험을 토대로 통합하여 잘 작동하는 일괄 삭제 예제를 만듭니다.
그림 1: 각 행에 확인란이 포함됨(전체 크기 이미지를 보려면 클릭)
1단계: 일괄 처리 삭제 인터페이스 만들기
확인란의 GridView 열 추가 자습서에서 일괄 삭제 인터페이스를 이미 만들었으므로, 처음부터 만들기보다는 BatchDelete.aspx
로 간단히 복사하면 됩니다.
BatchDelete.aspx
페이지를 BatchData
폴더에서 열고, CheckBoxField.aspx
페이지를 EnhancedGridView
폴더에서 여는 것으로 시작합니다.
CheckBoxField.aspx
페이지에서 소스 보기로 이동하여 <asp:Content>
태그 사이의 마크업을 그림 2에 표시된 대로 복사합니다.
그림 2: 클립보드에 선언적 태그 CheckBoxField.aspx
복사(전체 크기 이미지를 보려면 클릭)
그런 다음 원본 보기 BatchDelete.aspx
로 이동하여 태그 내에 <asp:Content>
클립보드의 내용을 붙여넣습니다. 코드 숨김 클래스 CheckBoxField.aspx.cs
내에서 코드 숨김 클래스 BatchDelete.aspx.cs
안으로, 코드 숨김 클래스의 코드를 복사하여 붙여넣기 하세요. (DeleteSelectedProducts
버튼 이벤트 처리기, Click
메서드 및 ToggleCheckState
이벤트 처리기, Click
및 CheckAll
버튼을 포함합니다.) 이 콘텐츠를 BatchDelete.aspx
복사한 후 페이지의 코드 숨김 클래스에는 다음 코드가 포함되어야 합니다.
using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
public partial class BatchData_BatchDelete : System.Web.UI.Page
{
protected void DeleteSelectedProducts_Click(object sender, EventArgs e)
{
bool atLeastOneRowDeleted = false;
// Iterate through the Products.Rows property
foreach (GridViewRow row in Products.Rows)
{
// Access the CheckBox
CheckBox cb = (CheckBox)row.FindControl("ProductSelector");
if (cb != null && cb.Checked)
{
// Delete row! (Well, not really...)
atLeastOneRowDeleted = true;
// First, get the ProductID for the selected row
int productID = Convert.ToInt32(Products.DataKeys[row.RowIndex].Value);
// "Delete" the row
DeleteResults.Text += string.Format
("This would have deleted ProductID {0}<br />", productID);
//... To actually delete the product, use ...
//ProductsBLL productAPI = new ProductsBLL();
//productAPI.DeleteProduct(productID);
//............................................
}
}
// Show the Label if at least one row was deleted...
DeleteResults.Visible = atLeastOneRowDeleted;
}
private void ToggleCheckState(bool checkState)
{
// Iterate through the Products.Rows property
foreach (GridViewRow row in Products.Rows)
{
// Access the CheckBox
CheckBox cb = (CheckBox)row.FindControl("ProductSelector");
if (cb != null)
cb.Checked = checkState;
}
}
protected void CheckAll_Click(object sender, EventArgs e)
{
ToggleCheckState(true);
}
protected void UncheckAll_Click(object sender, EventArgs e)
{
ToggleCheckState(false);
}
}
선언적 태그와 소스 코드를 복사한 후, 브라우저를 통해 BatchDelete.aspx
을(를) 확인하면서 잠시 테스트하세요. GridView에서 처음 10개 제품이 나열되며, 각 행에는 제품 이름, 범주, 가격 및 확인란이 포함됩니다. 모두 확인, 모두 선택 취소 및 선택한 제품 삭제의 세 가지 단추가 있어야 합니다. [모두 확인] 단추를 클릭하면 모든 확인란이 선택되며[ 모두 선택 취소]는 모든 확인란의 선택을 취소합니다. 선택한 제품 삭제를 클릭하면 선택한 제품의 값이 나열 ProductID
되지만 실제로는 제품을 삭제하지 않는 메시지가 표시됩니다.
그림 3: 인터페이스 CheckBoxField.aspx
가 이동 BatchDeleting.aspx
되었습니다(전체 크기 이미지를 보려면 클릭).
2단계: 트랜잭션을 사용하여 확인된 제품 삭제
일괄 삭제 인터페이스가 성공적으로 BatchDeleting.aspx
에 복사됐으니, 코드 업데이트를 통해 클래스 DeleteProductsWithTransaction
의 메서드 ProductsBLL
을 사용하여 선택한 제품 삭제 버튼이 확인된 제품을 삭제할 수 있도록 해야 합니다.
트랜잭션 내 데이터베이스 수정 래핑 자습서에서 추가된 이 메서드는 List<T>
값들의 ProductID
를 입력으로 받아들이며, 트랜잭션 범위 내에서 각 해당하는 ProductID
를 삭제합니다.
DeleteSelectedProducts
Button의 Click
이벤트 처리기는 현재 다음 foreach
루프를 사용하여 각 GridView 행을 반복합니다.
// Iterate through the Products.Rows property
foreach (GridViewRow row in Products.Rows)
{
// Access the CheckBox
CheckBox cb = (CheckBox)row.FindControl("ProductSelector");
if (cb != null && cb.Checked)
{
// Delete row! (Well, not really...)
atLeastOneRowDeleted = true;
// First, get the ProductID for the selected row
int productID = Convert.ToInt32(Products.DataKeys[row.RowIndex].Value);
// "Delete" the row
DeleteResults.Text += string.Format
("This would have deleted ProductID {0}<br />", productID);
//... To actually delete the product, use ...
//ProductsBLL productAPI = new ProductsBLL();
//productAPI.DeleteProduct(productID);
//............................................
}
}
각 행에서 ProductSelector
CheckBox 웹 컨트롤이 프로그래밍 방식으로 참조됩니다. 이 옵션을 선택하면 ProductID
에 있는 행이 DataKeys
컬렉션에서 검색되고, DeleteResults
레이블의 Text
속성이 업데이트되어 해당 행이 삭제를 위해 선택되었음을 알리는 메시지가 포함됩니다.
위의 코드는 ProductsBLL
클래스의 Delete
메서드 호출이 주석 처리되어 있으므로 실제로 어떤 상품 레코드도 삭제하지 않습니다. 이 삭제 논리를 적용할 경우, 코드는 상품을 삭제하지만 원자적 작업 내에서 실행되지 않습니다. 즉, 시퀀스의 처음 몇 개의 삭제가 성공했지만 나중에 실패한 경우(아마도 외래 키 제약 조건 위반으로 인해) 예외가 throw되지만 이미 삭제된 제품은 삭제된 상태로 유지됩니다.
원자성을 보장하기 위해 ProductsBLL
클래스의 DeleteProductsWithTransaction
메서드를 사용해야 합니다. 이 메서드는 값 목록을 ProductID
허용하므로 먼저 표에서 이 목록을 컴파일한 다음 매개 변수로 전달해야 합니다. 먼저 List<T>
유형의 int
인스턴스를 만듭니다.
foreach
루프 내에서 선택한 제품의 ProductID
값을 이 List<T>
에 추가해야 합니다. 루프 후에 클래스 List<T>
의 ProductsBLL
메서드에 DeleteProductsWithTransaction
전달되어야 합니다.
DeleteSelectedProducts
다음 코드로 Button의 Click
이벤트 처리기를 업데이트합니다.
protected void DeleteSelectedProducts_Click(object sender, EventArgs e)
{
// Create a List to hold the ProductID values to delete
System.Collections.Generic.List<int> productIDsToDelete =
new System.Collections.Generic.List<int>();
// Iterate through the Products.Rows property
foreach (GridViewRow row in Products.Rows)
{
// Access the CheckBox
CheckBox cb = (CheckBox)row.FindControl("ProductSelector");
if (cb != null && cb.Checked)
{
// Save the ProductID value for deletion
// First, get the ProductID for the selected row
int productID = Convert.ToInt32(Products.DataKeys[row.RowIndex].Value);
// Add it to the List...
productIDsToDelete.Add(productID);
// Add a confirmation message
DeleteResults.Text += string.Format
("ProductID {0} has been deleted<br />", productID);
}
}
// Call the DeleteProductsWithTransaction method and show the Label
// if at least one row was deleted...
if (productIDsToDelete.Count > 0)
{
ProductsBLL productAPI = new ProductsBLL();
productAPI.DeleteProductsWithTransaction(productIDsToDelete);
DeleteResults.Visible = true;
// Rebind the data to the GridView
Products.DataBind();
}
}
업데이트된 코드는 List<T>
(int
) 형식의 productIDsToDelete
을(를) 생성하고 삭제할 ProductID
값들로 채웁니다. 루프 후에 foreach
하나 이상의 제품이 선택된 ProductsBLL
경우 클래스의 DeleteProductsWithTransaction
메서드가 호출되고 이 목록을 전달합니다.
DeleteResults
레이블도 표시되고 데이터가 GridView로 다시 조정됩니다(새로 삭제된 레코드가 더 이상 표에 행으로 표시되지 않도록).
그림 4는 삭제를 위해 여러 행이 선택된 후의 GridView를 보여 줍니다. 그림 5는 선택한 제품 삭제 단추를 클릭한 직후의 화면을 보여 줍니다. 그림 5 ProductID
에서는 삭제된 레코드의 값이 GridView 아래의 레이블에 표시되고 해당 행은 더 이상 GridView에 없습니다.
그림 4: 선택한 제품이 삭제됩니다(전체 크기 이미지를 보려면 클릭).
그림 5: 삭제된 제품 ProductID
값이 GridView 아래에 나열됩니다(전체 크기 이미지를 보려면 클릭).
비고
메서드의 DeleteProductsWithTransaction
원자성을 테스트하려면 테이블에 제품에 Order Details
대한 항목을 수동으로 추가한 다음 해당 제품(다른 제품과 함께)을 삭제하려고 시도합니다. 연결된 주문으로 제품을 삭제하려고 할 때 외래 키 제약 조건 위반이 발생하지만 선택한 다른 제품 삭제가 롤백되는 방법을 확인합니다.
요약
일괄 삭제 인터페이스를 생성하려면, 확인란 열이 있는 GridView를 추가하고 버튼 웹 컨트롤을 추가하세요. 이를 클릭하면 선택한 모든 행이 단일 원자 작업으로 삭제됩니다. 이 자습서에서는 이전 두 자습서에서 수행한 작업을 함께 압정하여 이러한 인터페이스를 빌드했습니다. 즉, 확인란의 GridView 열을 추가하고트랜잭션 내에서 데이터베이스 수정 사항을 래핑합니다. 첫 번째 튜토리얼에서는 확인란 열이 있는 GridView를 만들었고, 다음 튜토리얼에서는 값의 List<T>
를 ProductID
로 전달할 때 트랜잭션 범위 내에서 모두 삭제하는 메서드를 BLL에서 구현했습니다.
다음 자습서에서는 일괄 처리 삽입을 수행하기 위한 인터페이스를 만듭니다.
행복한 프로그래밍!
작성자 정보
7개의 ASP/ASP.NET 책의 저자이자 4GuysFromRolla.com 창립자인 Scott Mitchell은 1998년부터 Microsoft 웹 기술을 연구해 왔습니다. Scott은 독립 컨설턴트, 트레이너 및 작가로 일합니다. 그의 최신 책은 Sams Teach Yourself ASP.NET 2.0 in 24 Hours입니다. 그에게 mitchell@4GuysFromRolla.com으로 연락할 수 있습니다.
특별히 감사드립니다.
이 자습서 시리즈는 많은 유용한 검토자가 검토했습니다. 이 자습서의 수석 검토자는 힐튼 기제나우와 테레사 머피였습니다. 예정된 MSDN 문서를 검토하는 데 관심이 있으신가요? 그렇다면 mitchell@4GuysFromRolla.com으로 메시지를 보내 주세요.