다음을 통해 공유


.NET Framework 데이터베이스 애플리케이션에서 동시성 예외 처리

참고 항목

데이터 집합 및 관련 클래스는 2000년대 초반에 적용된 레거시 .NET Framework 기술로, 응용 프로그램이 데이터베이스에서 연결이 끊어진 동안 응용 프로그램이 메모리의 데이터로 작업할 수 있도록 합니다. 사용자가 데이터를 수정하고 변경 내용을 다시 데이터베이스에 유지할 수 있도록 하는 애플리케이션에 특히 유용합니다. 데이터 세트는 매우 성공적인 기술로 입증되었지만 새 .NET 애플리케이션은 Entity Framework Core를 사용하는 것이 좋습니다. Entity Framework는 표 형식 데이터를 개체 모델로 사용하는 더 자연스러운 방법을 제공하며 더 단순한 프로그래밍 인터페이스를 제공합니다.

동시성 예외(System.Data.DBConcurrencyException)는 두 사용자가 동시에 데이터베이스의 동일한 데이터를 변경하려고 할 때 발생합니다. 이 연습에서는 DBConcurrencyException를 catch하고, 오류가 발생한 행을 찾고, 오류를 처리하기 위한 전략을 파악하는 방법을 보여 주는 Windows 애플리케이션을 만듭니다.

이 연습에서는 다음 프로세스를 수행합니다.

  1. Windows Forms 앱(.NET Framework) 프로젝트 만들기

  2. Northwind Customers 테이블을 기반으로 새 데이터 세트를 만듭니다.

  3. DataGridView를 사용하여 양식을 만들고 데이터를 표시합니다.

  4. Northwind 데이터베이스 내 Customers 테이블의 데이터를 사용하여 데이터 세트를 채웁니다.

  5. 서버 탐색기테이블 데이터 표시 기능을 사용하여 Customers 테이블의 데이터에 액세스하고 레코드를 변경합니다.

  6. 동일한 레코드를 다른 값으로 변경하고, 데이터 세트를 업데이트하고, 변경 내용을 데이터베이스에 쓰기를 시도합니다. 그러면 동시성 오류가 발생합니다.

  7. 오류를 catch한 다음 서로 다른 버전의 레코드를 표시하여 사용자가 계속해서 데이터베이스를 업데이트할지 또는 업데이트를 취소할지 결정할 수 있도록 합니다.

필수 조건

이 연습에서는 SQL Server Express LocalDB 및 Northwind 샘플 데이터베이스를 사용합니다.

  1. SQL Server Express LocalDB가 없는 경우 SQL Server Express 다운로드 페이지 또는 Visual Studio 설치 관리자를 통해 설치합니다. Visual Studio 설치 관리자에서 데이터 스토리지 및 처리 워크로드의 일부로 또는 개별 구성 요소로 SQL Server Express LocalDB를 설치할 수 있습니다.

  2. 다음 단계에 따라 Northwind 샘플 데이터베이스를 설치합니다.

    1. Visual Studio에서 SQL Server 개체 탐색기 창을 엽니다. (SQL Server 개체 탐색기는 Visual Studio 설치 관리자에서 데이터 스토리지 및 처리 워크로드의 일부로 설치됩니다.) SQL Server 노드를 확장합니다. LocalDB 인스턴스를 마우스 오른쪽 단추로 클릭하고 새 쿼리를 선택합니다.

      쿼리 편집기 창이 열립니다.

    2. Northwind Transact-SQL 스크립트를 클립보드에 복사합니다. 이 T-SQL 스크립트는 Northwind 데이터베이스를 처음부터 만들고 데이터를 채웁니다.

    3. T-SQL 스크립트를 쿼리 편집기에 붙여넣은 다음, 실행 단추를 선택합니다.

      잠시 후 쿼리 실행이 완료되고 Northwind 데이터베이스가 만들어집니다.

새 프로젝트 만들기

먼저 새 Windows Forms 애플리케이션을 만듭니다.

  1. Visual Studio 파일 메뉴에서 >프로젝트를 선택합니다.

  2. 왼쪽 창에서 Visual C# 또는 Visual Basic을 확장하고 Windows 바탕 화면을 선택합니다.

  3. 가운데 창에서 Windows Forms 앱 프로젝트 형식을 선택합니다.

  4. 프로젝트 이름을 ConcurrencyWalkthrough로 지정한 다음 확인을 선택합니다.

    ConcurrencyWalkthrough 프로젝트가 만들어지고, 솔루션 탐색기에 추가되고, 디자이너에서 새 양식이 열립니다.

Northwind 데이터 세트 만들기

다음으로, NorthwindDataSet라는 데이터 세트를 만듭니다.

  1. 데이터 메뉴에서 새 데이터 원본 추가를 선택합니다.

    데이터 원본 구성 마법사가 열립니다.

  2. 데이터 원본 형식 선택 화면에서 데이터베이스를 선택합니다.

    Visual Studio의 데이터 원본 구성 마법사

  3. 사용 가능한 연결 목록에서 Northwind 샘플 데이터베이스에 대한 연결을 선택합니다. 이 연결이 연결 목록에 없는 경우 새 연결을 선택합니다.

    참고 항목

    로컬 데이터베이스 파일에 연결하는 경우 프로젝트에 파일을 추가할지 묻는 메시지가 표시되면 아니요를 선택합니다.

  4. 애플리케이션 구성 파일에 연결 문자열 저장 화면에서 다음을 선택합니다.

  5. 테이블 노드를 확장하고 Customers 테이블을 선택합니다. 데이터 세트의 기본 이름은 NorthwindDataSet입니다.

  6. 마침을 선택하여 데이터 세트를 프로젝트에 추가합니다.

데이터 바인딩된 DataGridView 컨트롤 만들기

이 섹션에서는 데이터 원본 창에서 Customers 항목을 Windows Form으로 끌어 System.Windows.Forms.DataGridView을 만듭니다.

  1. 데이터 메뉴에서 데이터 원본 창을 열려면 데이터 원본 표시를 선택합니다.

  2. 데이터 원본 창에서 NorthwindDataSet 노드를 확장 한 다음 Customers 테이블을 선택합니다.

  3. 테이블 노드에서 아래쪽 화살표를 선택하고 드롭다운 목록에서 DataGridView를 선택합니다.

  4. 테이블을 양식의 빈 영역으로 끕니다.

    CustomersDataGridView라는 DataGridView 컨트롤과 CustomersBindingNavigator라는 BindingNavigatorBindingSource에 바인딩된 양식에 추가됩니다. 이 양식은 NorthwindDataSet의 Customers 테이블에 바인딩됩니다.

양식 테스트

이제 양식을 테스트하여 이 시점까지 예상대로 동작하는지 확인할 수 있습니다.

  1. F5 키를 선택하여 애플리케이션을 실행합니다.

    양식이 Customers 테이블의 데이터로 채워진 DataGridView 컨트롤과 함께 표시됩니다.

  2. 디버그 메뉴에서 디버깅 중지를 선택합니다.

동시성 오류 처리

오류를 처리하는 방법은 애플리케이션을 제어하는 특정 비즈니스 규칙에 따라 달라집니다. 이 연습에서는 동시성 오류를 처리하는 방법의 예로 다음 전략을 사용합니다.

애플리케이션이 사용자에게 세 가지 버전의 레코드를 제공합니다.

  • 데이터베이스의 현재 레코드

  • 데이터 세트에 로드된 원본 레코드

  • 데이터 세트에서 제안된 변경 내용

그러면 사용자가 데이터베이스를 제안된 버전으로 덮어쓰거나 업데이트를 취소하고 데이터베이스의 새 값을 사용하여 데이터 세트를 새로 고칠 수 있습니다.

동시성 오류 처리를 사용하도록 설정하려면

  1. 사용자 지정 오류 처리기를 만듭니다.

  2. 사용자에게 선택 항목을 표시합니다.

  3. 사용자의 응답을 처리합니다.

  4. 업데이트를 다시 보내거나 데이터 세트에서 데이터를 다시 설정합니다.

동시성 예외를 처리하는 코드 추가

업데이트를 수행하려고 할 때 예외가 발생하면 일반적으로 발생한 예외에서 제공한 정보를 사용하여 작업을 수행하려고 합니다. 이 섹션에서는 데이터베이스 업데이트를 시도하는 코드를 추가합니다. 또한 발생할 수 있는 모든 DBConcurrencyException 및 기타 예외를 처리합니다.

참고 항목

이 연습의 뒷부분에서 CreateMessageProcessDialogResults 메서드가 추가됩니다.

  1. Form1_Load 메서드 아래에 다음 코드를 추가합니다.

    private void UpdateDatabase()
    {
        try
        {
            this.customersTableAdapter.Update(this.northwindDataSet.Customers);
            MessageBox.Show("Update successful");
        }
        catch (DBConcurrencyException dbcx)
        {
            DialogResult response = MessageBox.Show(CreateMessage((NorthwindDataSet.CustomersRow)
                (dbcx.Row)), "Concurrency Exception", MessageBoxButtons.YesNo);
    
            ProcessDialogResult(response);
        }
        catch (Exception ex)
        {
            MessageBox.Show("An error was thrown while attempting to update the database.");
        }
    }
    

  1. 다음과 같이 CustomersBindingNavigatorSaveItem_Click 메서드를 바꿔 UpdateDatabase 메서드를 호출합니다.

    private void customersBindingNavigatorSaveItem_Click(object sender, EventArgs e)
    {
        UpdateDatabase();
    }
    

사용자에게 선택 항목 표시

방금 작성한 코드는 CreateMessage 프로시저를 호출하여 사용자에게 오류 정보를 표시합니다. 이 연습에서는 메시지 상자를 사용하여 서로 다른 버전의 레코드를 사용자에게 표시합니다. 그러면 사용자가 레코드를 변경 내용으로 덮어쓸지 아니면 편집을 취소할지 선택할 수 있습니다. 사용자가 메시지 상자에서 옵션을 선택(단추를 클릭)하면 응답이 ProcessDialogResult 메서드에 전달됩니다.

코드 편집기에 다음 코드를 추가하여 메시지를 만듭니다. UpdateDatabase 메서드 아래에 다음 코드를 입력합니다.

private string CreateMessage(NorthwindDataSet.CustomersRow cr)
{
    return
        "Database: " + GetRowData(GetCurrentRowInDB(cr), DataRowVersion.Default) + "\n" +
        "Original: " + GetRowData(cr, DataRowVersion.Original) + "\n" +
        "Proposed: " + GetRowData(cr, DataRowVersion.Current) + "\n" +
        "Do you still want to update the database with the proposed value?";
}


//--------------------------------------------------------------------------
// This method loads a temporary table with current records from the database
// and returns the current values from the row that caused the exception.
//--------------------------------------------------------------------------
private NorthwindDataSet.CustomersDataTable tempCustomersDataTable = 
    new NorthwindDataSet.CustomersDataTable();

private NorthwindDataSet.CustomersRow GetCurrentRowInDB(NorthwindDataSet.CustomersRow RowWithError)
{
    this.customersTableAdapter.Fill(tempCustomersDataTable);

    NorthwindDataSet.CustomersRow currentRowInDb = 
        tempCustomersDataTable.FindByCustomerID(RowWithError.CustomerID);

    return currentRowInDb;
}


//--------------------------------------------------------------------------
// This method takes a CustomersRow and RowVersion 
// and returns a string of column values to display to the user.
//--------------------------------------------------------------------------
private string GetRowData(NorthwindDataSet.CustomersRow custRow, DataRowVersion RowVersion)
{
    string rowData = "";

    for (int i = 0; i < custRow.ItemArray.Length ; i++ )
    {
        rowData = rowData + custRow[i, RowVersion].ToString() + " ";
    }
    return rowData;
}

사용자 응답 처리

또한 메시지 상자에 대한 사용자의 응답을 처리하는 코드도 필요합니다. 옵션은 데이터베이스의 현재 레코드를 제안된 변경 내용으로 덮어쓰거나 로컬 변경 내용을 취소하고 현재 데이터베이스에 있는 레코드를 사용하여 데이터 테이블을 새로 고치는 것입니다. 사용자가 를 선택하면 preserveChanges 인수를 true로 설정하여 Merge 메서드가 호출됩니다. 그러면 레코드의 원래 버전이 데이터베이스의 레코드와 일치하기 때문에 업데이트가 성공하게 됩니다.

이전 섹션에서 추가한 코드 아래에 다음 코드를 추가합니다.

// This method takes the DialogResult selected by the user and updates the database 
// with the new values or cancels the update and resets the Customers table 
// (in the dataset) with the values currently in the database.

private void ProcessDialogResult(DialogResult response)
{
    switch (response)
    {
        case DialogResult.Yes:
            northwindDataSet.Merge(tempCustomersDataTable, true, MissingSchemaAction.Ignore);
            UpdateDatabase();
            break;

        case DialogResult.No:
            northwindDataSet.Merge(tempCustomersDataTable);
            MessageBox.Show("Update cancelled");
            break;
    }
}

양식 동작 테스트

이제 양식을 테스트하여 예상대로 동작하는지 확인할 수 있습니다. 동시성 위반을 시뮬레이트하려면 NorthwindDataSet를 채운 후 데이터베이스에서 데이터를 변경합니다.

  1. F5 키를 선택하여 애플리케이션을 실행합니다.

  2. 양식이 표시되면 실행 중인 상태로 두고 Visual Studio IDE로 전환합니다.

  3. 보기 메뉴에서 서버 탐색기를 선택합니다.

  4. 서버 탐색기에서 애플리케이션이 사용하는 연결을 확장한 다음 테이블 노드를 확장합니다.

  5. Customers 테이블을 마우스 오른쪽 단추로 클릭한 다음 테이블 데이터 표시를 선택합니다.

  6. 첫 번째 레코드(ALFKI)에서 ContactNameMaria Anders2로 변경합니다.

    참고 항목

    다른 행으로 이동하여 변경 내용을 커밋합니다.

  7. ConcurrencyWalkthrough의 실행 중인 양식으로 전환합니다.

  8. 양식의 첫 번째 레코드(ALFKI)에서 ContactNameMaria Anders1로 변경합니다.

  9. 저장 단추를 선택합니다.

    동시성 오류가 발생하고 메시지 상자가 나타납니다.

    아니요를 선택하면 업데이트를 취소하고 현재 데이터베이스에 있는 값으로 데이터 세트를 업데이트합니다. 를 선택하면 제안된 값이 데이터베이스에 기록됩니다.