다음을 통해 공유


연습: 저장 프로시저에 엔터티 매핑

이 항목에서는 ADO.NET 엔터티 데이터 모델 디자이너(Entity Designer)를 사용하여 엔터티 형식의 삽입, 업데이트 및 삭제 작업을 저장 프로시저에 매핑하는 방법을 보여 줍니다. 엔터티 형식의 삽입, 업데이트 및 삭제 작업은 시스템에서 자동으로 생성된 SQL 문을 사용하거나(기본값) 개발자가 지정한 저장 프로시저를 사용할 수 있습니다. 데이터베이스를 업데이트하는 데 저장 프로시저를 사용하는지 여부에 관계없이 엔터티를 만들고, 업데이트하고, 삭제하는 데 사용되는 응용 프로그램 코드는 동일합니다.

이 연습에서는 CourseManager 응용 프로그램에서 사용된 EDM(엔터티 데이터 모델)을 수정하여 두 개의 엔터티 형식을 저장 프로시저에 매핑합니다. 자세한 내용은 이 항목의 뒷부분에 있는 필수 구성 요소 단원을 참조하십시오. 또한 엔터티 형식을 삽입, 업데이트 및 삭제할 코드를 작성합니다.

필수 구성 요소

이 연습을 수행하려면 CourseManager 응용 프로그램을 빌드해야 합니다. 자세한 내용과 지침은 Entity Framework 퀵 스타트를 참조하십시오. 이 응용 프로그램을 빌드한 후 두 개의 엔터티 형식을 저장 프로시저에 매핑하여 해당 EDM을 수정합니다.

Note참고

이 설명서에 있는 대부분의 연습 항목에서는 CourseManager 응용 프로그램을 시작 지점으로 사용하므로 원래 CourseManager 코드를 편집하지 않고 연습용으로 CourseManager 응용 프로그램을 복사하여 사용하는 것이 좋습니다.

이 연습에서는 독자가 Visual Studio, .NET Framework 및 Visual C# 또는 Visual Basic 프로그래밍에 대한 기본적인 지식을 가지고 있다고 가정합니다.

저장 프로시저에 Person 엔터티 매핑

엔터티의 삽입 작업을 저장 프로시저에 매핑할 때 서버에서 삽입된 행에 대해 기본 키를 만드는 경우 이 값을 다시 엔터티의 키 속성에 매핑해야 합니다. 이 예제에서 InsertPerson 저장 프로시저는 새로 만든 기본 키를 저장 프로시저 결과 집합의 일부로 반환합니다. 기본 키는 Entity Designer의 <Result Binding 추가> 기능을 사용하여 엔터티 키(PersonID)에 매핑됩니다.

Person 엔터티를 저장 프로시저에 매핑하려면

  1. Visual Studio에서 CourseManager 솔루션을 엽니다.

  2. 솔루션 탐색기에서 School.edmx 파일을 두 번 클릭합니다.

    School.edmx 파일이 ADO.NET 엔터티 데이터 모델 디자이너(Entity Designer)에서 열립니다.

  3. Person 엔터티 형식을 마우스 오른쪽 단추로 클릭하고 저장 프로시저 매핑을 선택합니다.

    저장 프로시저 매핑이 매핑 정보 창에 나타납니다.

  4. **<Insert Function 선택>**을 클릭합니다.

    이 필드는 EDM에 포함된 저장 프로시저의 드롭다운 목록이 됩니다.

  5. 드롭다운 목록에서 InsertPerson을 선택합니다.

    저장 프로시저 매개 변수와 엔터티 속성 간의 기본 매핑이 나타납니다. 화살표는 매핑 방향을 나타냅니다. 속성 값이 저장 프로시저 매개 변수에 제공됩니다.

  6. **<Result Binding 추가>**를 클릭합니다.

    필드를 편집할 수 있는 상태가 됩니다.

  7. **<Result Binding 추가>**를 InsertPerson 저장 프로시저에서 반환된 매개 변수 이름인 NewPersonID로 바꿉니다. Enter 키를 누릅니다.

    기본적으로 NewPersonID는 엔터티 키 PersonID에 매핑됩니다. 화살표는 매핑 방향을 나타냅니다. 결과 열의 값이 속성에 제공됩니다.

  8. **<Update Function 선택>**을 클릭하고 결과 드롭다운 목록에서 UpdatePerson을 선택합니다.

    저장 프로시저 매개 변수와 엔터티 속성 간의 기본 매핑이 나타납니다.

  9. **<Delete Function 선택>**을 클릭하고 결과 드롭다운 목록에서 DeletePerson을 선택합니다.

    저장 프로시저 매개 변수와 엔터티 속성 간의 기본 매핑이 나타납니다.

이제 Person 엔터티 형식의 삽입, 업데이트, 삭제 작업이 저장 프로시저에 매핑되었습니다.

저장 프로시저에 OfficeAssignment 엔터티 매핑

일 대 일 연결의 한쪽 End에 있는 엔터티 형식이 저장 프로시저에 매핑된 경우 연결의 반대쪽 End에 있는 엔터티도 저장 프로시저에 매핑되어야 합니다. 이 예제에서 OfficeAssignment 엔터티 형식은 Person 엔터티 형식과 일 대 일 연결 관계에 있으므로 저장 프로시저에 매핑됩니다. 이 매핑에서는 응용 프로그램 코드의 동시성을 편리하게 확인할 수 있도록 업데이트 작업에 Original Value 사용 옵션을 사용합니다.

OfficeAssignment 엔터티를 저장 프로시저에 매핑하려면

  1. OfficeAssignment 엔터티 형식을 마우스 오른쪽 단추로 클릭하고 Stored Procedure Mapping을 선택합니다.

    저장 프로시저 매핑이 매핑 정보 창에 나타납니다.

  2. **<Insert Function 선택>**을 클릭하고 결과 드롭다운 목록에서 InsertOfficeAssignment를 선택합니다.

    저장 프로시저 매개 변수와 엔터티 속성 간의 기본 매핑이 나타납니다.

  3. **<Result Binding 추가>**를 클릭합니다.

    필드를 편집할 수 있는 상태가 됩니다.

  4. <Result Binding 추가> 대신 Timestamp를 입력합니다.

  5. Propery/Value 열에서 Timestamp 옆에 있는 빈 필드를 클릭합니다.

    이 필드는 InsertOfficeAssignment 저장 프로시저에서 반환된 값을 매핑할 수 있는 속성의 드롭다운 목록이 됩니다.

  6. 드롭다운 목록에서 Timestamp를 선택합니다.

  7. **<Update Function 선택>**을 클릭하고 결과 드롭다운 목록에서 UpdateOfficeAssignment를 선택합니다.

    저장 프로시저 매개 변수와 엔터티 속성 간의 기본 매핑이 나타납니다. Original Value 사용 열에서 매핑된 각 속성 옆에 확인란이 나타납니다.

  8. 속성 열에서 OrigTimestamp 매개 변수에 해당하는 빈 필드를 클릭하고 결과 드롭다운 목록에서 Timestamp를 선택합니다.

    매개 변수 이름이 속성 이름과 정확하게 일치하지 않았으므로 Entity Designer에서 이 매핑을 기본 매핑으로 설정하지 않았습니다.

  9. Timestamp 속성에 해당하는 Original Value 사용 열의 상자를 선택합니다.

    업데이트를 시도하면 데이터를 다시 데이터베이스에 쓸 때 원래 데이터베이스에서 읽은 Timestamp 속성의 값이 사용됩니다. 값이 데이터베이스의 값과 일치하지 않으면 OptimisticConcurrencyException이 throw됩니다.

  10. **<Result Binding 추가>**를 클릭합니다.

    필드를 편집할 수 있는 상태가 됩니다.

  11. **<Result Binding 추가>**를 **Timestamp.**로 바꿉니다.

  12. Propery/Value 열에서 Timestamp 옆에 있는 빈 필드를 클릭합니다.

    이 필드는 UpdateOfficeAssignment 저장 프로시저에서 반환된 결과 열을 매핑할 수 있는 속성의 드롭다운 목록이 됩니다.

  13. 드롭다운 목록에서 Timestamp를 선택합니다.

  14. **<Delete Function 선택>**을 클릭하고 결과 드롭다운 목록에서 DeleteOfficeAssignment를 선택합니다.

    저장 프로시저 매개 변수와 엔터티 속성 간의 기본 매핑이 나타납니다.

이제 OfficeAssignment 엔터티 형식의 삽입, 업데이트, 삭제 작업이 저장 프로시저에 매핑되었습니다.

사용자 인터페이스 생성

다음에는 CourseManager 응용 프로그램에 두 개의 폼을 추가합니다. 한 폼에서는 강사 정보를 보고 업데이트하기 위한 인터페이스를 제공하고, 다른 폼에서는 사무실 할당을 보고 업데이트하기 위한 인터페이스를 제공합니다.

사용자 인터페이스를 생성하려면

  1. 솔루션 탐색기에서 CourseManager 프로젝트를 마우스 오른쪽 단추로 클릭하고 추가를 가리킨 다음 새 항목을 선택합니다.

    새 항목 추가 대화 상자가 나타납니다.

  2. Windows Form을 선택하고 폼의 이름을 InstructorViewer.vb 또는 InstructorViewer.cs로 설정한 다음 추가를 클릭합니다.

    새 폼이 프로젝트에 추가되고 폼 디자이너에서 열립니다. 폼의 이름은 InstructorViewer로 설정되고 텍스트는 InstructorViewer로 설정됩니다.

  3. 도구 상자에서 DataGridView 컨트롤을 폼으로 끌어다 놓고 속성 창에서 해당 이름instructorGridView로 설정합니다.

  4. 도구 상자에서 Button 컨트롤을 폼으로 끌어다 놓습니다. 해당 이름updateInstructor로 설정하고 텍스트Update Instructor로 설정합니다.

  5. 도구 상자에서 다른 Button 컨트롤을 폼으로 끌어다 놓고 해당 이름viewOffices로 설정하고 텍스트View Offices로 설정합니다.

  6. 솔루션 탐색기에서 CourseManager 프로젝트를 마우스 오른쪽 단추로 클릭하고 추가를 가리킨 다음 새 항목을 선택합니다.

    새 항목 추가 대화 상자가 나타납니다.

  7. Windows Form을 선택하고 폼의 이름을 OfficeViewer.vb 또는 OfficeViewer.cs로 설정한 다음 추가를 클릭합니다.

    새 폼이 프로젝트에 추가되고 폼 디자이너에서 열립니다. 폼의 이름은 OfficeViewer로 설정되고 텍스트는 OfficeViewer로 설정됩니다.

  8. 도구 상자에서 ComboBox 컨트롤을 폼으로 끌어다 놓고 해당 이름instructorList로 설정합니다.

  9. 도구 상자에서 TextBox 컨트롤을 폼으로 끌어다 놓고 해당 이름officeLocation으로 설정합니다.

  10. 도구 상자에서 Button 컨트롤을 폼으로 끌어다 놓습니다. 해당 이름updateOffice로 설정하고 텍스트Update Office로 설정합니다.

  11. 솔루션 탐색기에서 CourseViewer.vb 또는 CourseViewer.cs를 두 번 클릭합니다.

    CourseViewer 폼의 디자인 뷰가 나타납니다.

  12. 도구 상자에서 Button 컨트롤을 폼으로 끌어다 놓습니다.

  13. 속성 창에서 Button이름 속성을 viewInstructors로 설정하고 텍스트 속성을 View Instructors로 설정합니다.

  14. viewInstructorsButton 컨트롤을 두 번 클릭합니다.

    CourseViewer 폼의 코드 숨김 파일이 열립니다.

  15. viewInstructors_Click 이벤트 처리기에 다음 코드를 추가합니다.

    Dim instructorViewer As New InstructorViewer()
    instructorViewer.Visible = True
    
    InstructorViewer instructorViewer = new InstructorViewer();
    instructorViewer.Visible = true;
    
  16. InstructorViewer 폼의 디자인 뷰로 돌아갑니다.

  17. viewOfficesButton 컨트롤을 두 번 클릭합니다.

    Form2의 코드 숨김 파일이 열립니다.

  18. viewOffices_Click 이벤트 처리기에 다음 코드를 추가합니다.

    Dim officeViewer As New OfficeViewer()
    officeViewer.Visible = True
    
    OfficeViewer officeViewer = new OfficeViewer();
    officeViewer.Visible = true;
    

이제 사용자 인터페이스가 완료되었습니다.

강사 정보 보기 및 업데이트

이 절차에서는 강사 정보를 보고 업데이트할 수 있도록 하는 코드를 InstructorViewer 폼에 추가합니다. 보다 구체적으로, 코드는 다음을 수행합니다.

  • 강사인 Person 형식에 대한 정보를 반환하는 쿼리에 DataGridView를 바인딩합니다. 개체를 컨트롤에 바인딩하는 방법에 대한 자세한 내용은 컨트롤에 개체 바인딩(Entity Framework)을 참조하십시오.

  • DataGridView의 변경 내용(삽입, 업데이트 또는 삭제)을 데이터베이스에 저장합니다.

  • updateInstructor_Click 이벤트 처리기에서 **SaveChanges()**가 호출될 때 앞에서 매핑한 저장 프로시저를 사용하여 데이터를 데이터베이스에 씁니다.

강사 정보를 보고 업데이트하려면

  1. 폼 디자이너에 InstructorViewer 폼이 열려 있는 상태로 InstructorViewer 폼을 두 번 클릭합니다.

    InstructorViewer 폼의 코드 숨김 파일이 열립니다.

  2. 다음 using(C#) 또는 Imports(Visual Basic) 문을 추가합니다.

    Imports System.Data.Objects
    Imports System.Data.Objects.DataClasses
    
    using System.Data.Objects;
    using System.Data.Objects.DataClasses;
    
  3. 개체 컨텍스트를 나타내는 속성을 InstructorViewer 클래스에 추가합니다.

    ' Create an ObjectContext instance based on SchoolEntity.
    Private schoolContext As SchoolEntities
    
    // Create an ObjectContext instance based on SchoolEntity.
    private SchoolEntities schoolContext;
    
  4. InstructorViewer_Load 이벤트 처리기에서 개체 컨텍스트를 초기화하고 DataGridView 컨트롤의 데이터 소스를 nullHireDate가 없는 모든 Person 형식을 반환하는 쿼리로 설정하는 코드를 추가합니다.

    ' Initialize the ObjectContext.
    schoolContext = New SchoolEntities()
    Dim instructorQuery As ObjectQuery(Of Person) = _
        schoolContext.Person.Include("OfficeAssignment") _
        .Where("it.HireDate is not null") _
        .OrderBy("it.LastName")
    instructorGridView.DataSource = instructorQuery _
        .Execute(MergeOption.OverwriteChanges)
    instructorGridView.Columns("EnrollmentDate").Visible = False
    
    // Initialize schoolContext.
    schoolContext = new SchoolEntities();
    
    // Define the query to retrieve instructors.
    ObjectQuery<Person> instructorQuery = schoolContext.Person
        .Include("OfficeAssignment")
        .Where("it.HireDate is not null")
        .OrderBy("it.LastName");
    
    // Execute and bind the instructorList control to the query.
    instructorGridView.DataSource = instructorQuery.
        Execute(MergeOption.OverwriteChanges);
    instructorGridView.Columns["EnrollmentDate"].Visible = false;
    
  5. InstructorViewer 폼의 디자인 뷰로 돌아가서 updateInstructorButton 컨트롤을 두 번 클릭합니다.

    updateInstructor_Click 이벤트 처리기가 코드 숨김 파일에 추가됩니다.

  6. instructorGridViewDataGridView 컨트롤에서 강사 정보에 대해 변경된 내용을 저장하는 코드를 updateInstructor_Click 이벤트 처리기에 추가합니다.

    Dim numChanges As Integer
    ' Save object changes to the database, display a 
    ' message, and refresh the form.
    numChanges = schoolContext.SaveChanges()
    MessageBox.Show(numChanges.ToString() + _
        " change(s) saved to the database.")
    Me.Refresh()
    
    int numChanges;
    // Save object changes to the database, display a 
    // message, and refresh the form.
    numChanges = schoolContext.SaveChanges();
    MessageBox.Show(numChanges.ToString() +
        " change(s) saved to the database.");
    this.Refresh();
    

응용 프로그램을 실행하려면 Ctrl+F5를 누릅니다. 이제 View Instructors를 클릭하고 나타나는 테이블에서 변경한 다음 Update Instructor를 클릭하여 강사 정보를 보고 업데이트할 수 있습니다.

사무실 정보 보기 및 업데이트

이 절차에서는 사무실 할당 정보를 보고 업데이트할 수 있도록 하는 코드를 OfficeViewer 폼에 추가합니다. 보다 구체적으로, 코드는 다음을 수행합니다.

  • 강사 정보를 반환하는 쿼리에 ComboBox를 바인딩합니다.

  • 선택한 강사의 사무실 위치 정보를 TextBox에 표시합니다.

  • updateOffice_Click 이벤트 처리기에서 **SaveChanges()**가 호출될 때 앞에서 매핑한 저장 프로시저를 사용하여 데이터를 데이터베이스에 씁니다.

사무실 정보를 보고 업데이트하려면

  1. 폼 디자이너에 OfficeViewer 폼이 열려 있는 상태로 OfficeViewer 폼을 두 번 클릭합니다.

    OfficeViewer 폼의 코드 숨김 파일이 열립니다.

  2. 다음 using(C#) 또는 Imports(Visual Basic) 문을 추가합니다.

    Imports System.Data.Objects
    Imports System.Data.Objects.DataClasses
    
    using System.Data.Objects;
    using System.Data.Objects.DataClasses;
    
  3. 개체 컨텍스트를 나타내는 속성을 OfficeViewer 클래스에 추가합니다.

    ' Create an ObjectContext instance based on SchoolEntity.
    Private schoolContext As SchoolEntities
    
    // Create an ObjectContext instance based on SchoolEntity.
    private SchoolEntities schoolContext;
    
  4. 다음 메서드를 폼에 추가합니다.

    Private Sub ExecuteInstructorQuery()
        ' Define the query to retrieve instructors.
        Dim instructorQuery As ObjectQuery(Of Person) = _
            schoolContext.Person.Include("OfficeAssignment"). _
            Where("it.HireDate is not null").OrderBy("it.LastName")
    
        'Execute and bind the instructorList control to the query.
        'Using MergeOption.OverwriteChanges overwrites local data
        'with data from the database.
        instructorList.DataSource = instructorQuery _
            .Execute(MergeOption.OverwriteChanges)
        instructorList.DisplayMember = "LastName"
    End Sub
    
    private void ExecuteInstructorQuery()
    {
        // Define the query to retrieve instructors.
        ObjectQuery<Person> instructorQuery = schoolContext.Person
            .Include("OfficeAssignment")
            .Where("it.HireDate is not null")
            .OrderBy("it.LastName");
    
        //Execute and bind the instructorList control to the query.
        //Using MergeOption.OverwriteChanges overwrites local data
        //with data from the database.
        instructorList.DataSource = instructorQuery
            .Execute(MergeOption.OverwriteChanges);
        instructorList.DisplayMember = "LastName";
    }
    

    이 메서드는 강사 정보를 반환하고 그 결과를 instructorListComboBox 컨트롤에 바인딩하는 쿼리를 실행합니다.

  5. OfficeViewer_Load 이벤트 처리기에서 개체 컨텍스트를 초기화하고 ComboBox 컨트롤을 nullHireDate가 없는 모든 Person 형식을 반환하는 쿼리에 바인딩하는 메서드를 호출하는 코드를 추가합니다.

    schoolContext = New SchoolEntities()
    ExecuteInstructorQuery()
    
    schoolContext = new SchoolEntities();
    ExecuteInstructorQuery();
    
  6. OfficeViewer 폼의 디자인 뷰로 돌아가서 instructorListComboBox 컨트롤을 두 번 클릭합니다.

    instructorList_SelectedIndexChanged 이벤트 처리기가 코드 숨김 파일에 추가됩니다.

  7. 선택한 강사의 사무실 위치를 ListBox 컨트롤에 표시하고 updateOfficeButton 컨트롤을 사용하지 않도록 설정하는 코드를 이벤트 처리기에 추가합니다. 선택한 사무실 위치가 변경되면 이 컨트롤은 사용할 수 없게 됩니다.

    Dim instructor As Person = CType(Me.instructorList _
        .SelectedItem(), Person)
    
    If Not instructor.OfficeAssignment Is Nothing Then
        Me.officeLocation.Text = instructor _
            .OfficeAssignment.Location.ToString()
    Else
        Me.officeLocation.Text = ""
    End If
    
    ' Disable the updateOffice button until a change
    ' has been made to the office location.
    updateOffice.Enabled = False
    
    'forceChanges.Enabled = False
    
    Person instructor = (Person)this.instructorList.
        SelectedItem;
    
    if (instructor.OfficeAssignment != null)
    {
        this.officeLocation.Text = instructor.
            OfficeAssignment.Location.ToString();
    }
    else
    {
        this.officeLocation.Text = "";
    }
    
    // Disable the updateOffice button until a change
    // has been made to the office location.
    updateOffice.Enabled = false;
    
    //forceChanges.Enabled = false;
    
  8. OfficeViewer 폼의 디자인 뷰로 돌아가서 updateOfficeButton 컨트롤을 두 번 클릭합니다.

    updateOffice_Click 이벤트 처리기가 코드 숨김 파일에 추가됩니다.

  9. 사무실 정보의 변경 내용을 officeLocationTextBox 컨트롤에 저장하는 코드를 추가합니다.

    Try
        Dim numChanges As Integer
        Dim currentInstructor As Person = CType(Me.instructorList _
            .SelectedItem(), Person)
        If Me.officeLocation.Text <> String.Empty Then
            If Not currentInstructor.OfficeAssignment Is Nothing Then
                currentInstructor.OfficeAssignment.Location() = _
                    Me.officeLocation.Text
            Else
                Dim temp(8) As Byte
                currentInstructor.OfficeAssignment = _
                    OfficeAssignment.CreateOfficeAssignment( _
                    currentInstructor.PersonID, _
                    Me.officeLocation.Text, temp)
            End If
        Else
            schoolContext.DeleteObject(currentInstructor. _
                                       OfficeAssignment)
        End If
        numChanges = schoolContext.SaveChanges()
        MessageBox.Show(numChanges.ToString() _
                + " change(s) saved to the database.")
    Catch oce As OptimisticConcurrencyException
        MessageBox.Show(oce.Message + " The conflict " & _
                "occurred on " & oce.StateEntries(0).Entity _
                .ToString() & "with key value " & _
                oce.StateEntries(0).EntityKey.EntityKeyValues(0) _
                .Value)
    
        'forceChanges.Enabled = True
    Catch ue As UpdateException
        MessageBox.Show(ue.Message & " Click OK to retrieve " _
                & "the latest data from the database.")
        ExecuteInstructorQuery()
        Me.Refresh()
    Finally
        ' Disable the updateOffice button until another
        ' change has been made to the location.
        updateOffice.Enabled = False
    End Try
    
    try
    {
        int numChanges;
        Person currentInstructor = (Person)this.instructorList.
            SelectedItem;
        if (this.officeLocation.Text != string.Empty)
        {
            if (currentInstructor.OfficeAssignment != null)
            {
                currentInstructor.OfficeAssignment.Location
                    = this.officeLocation.Text;
            }
            else
            {
                currentInstructor.OfficeAssignment
                    = OfficeAssignment.CreateOfficeAssignment(
                    currentInstructor.PersonID, this.officeLocation.Text,
                    new byte[8]);
            }
        }
        else
        {
            schoolContext.DeleteObject(currentInstructor
                .OfficeAssignment);
        }
        numChanges = schoolContext.SaveChanges();
        MessageBox.Show(numChanges.ToString() +
            " change(s) saved to the database.");
    }
    catch (OptimisticConcurrencyException oce)
    {
        MessageBox.Show(oce.Message + " The conflict "
            + "occurred on " + oce.StateEntries[0].Entity
            + " with key value " + oce.StateEntries[0].
            EntityKey.EntityKeyValues[0].Value);
    
        //forceChanges.Enabled = true;
    }
    catch (UpdateException ue)
    {
        MessageBox.Show(ue.Message + " Click OK to retrieve "
            + "the latest data from the database.");
        ExecuteInstructorQuery();
        this.Refresh();
    }
    finally
    {
        // Disable the updateOffice button until another
        // change has been made to the location.
        updateOffice.Enabled = false;
    }
    
  10. OfficeViewer 폼의 디자인 뷰로 돌아가서 officeLocationTextBox 컨트롤을 두 번 클릭합니다.

    officeLocation_TextChanged 이벤트 처리기가 코드 숨김 파일에 추가됩니다.

  11. 선택한 사무실 위치가 변경되었을 경우 updateOfficeButton 컨트롤을 사용할 수 있도록 하는 코드를 추가합니다.

    ' Enable the udateOffice button when there is a change
    ' to write to the database.
    updateOffice.Enabled = True
    
    // Enable the udateOffice button when there is a change
    // to write to the database.
    updateOffice.Enabled = true;
    

이제 응용 프로그램이 완료되었습니다. 응용 프로그램을 실행하려면 Ctrl + F5를 누릅니다. 이제 OfficeViewer 폼에서 사무실 정보를 보고 업데이트할 수 있습니다.

동시성 충돌 처리

이 절차에서는 동시성 충돌이 발생한 후 클라이언트 변경 내용을 데이터베이스에 강제로 저장하는 코드를 Office Viewer 폼에 추가합니다.

동시성 충돌을 처리하려면

  1. 솔루션 탐색기에서 InstructorViewer.vb 또는 InstructorViewer.cs를 두 번 클릭합니다.

    폼이 폼 디자이너에서 열립니다.

  2. View Offices 단추를 두 번 클릭합니다.

    InstructorViewer 폼의 코드 숨김 파일이 열립니다.

  3. View Offices 단추를 클릭할 때 두 개의 OfficeViewer 폼이 로드되도록 다음 코드를 viewOffices_Click 이벤트 처리기에 추가합니다.

    Dim officeViewer2 As New OfficeViewer()
    officeViewer2.Text = "Demonstrate Conflict"
    officeViewer2.Visible = True
    
    OfficeViewer officeViewer2 = new OfficeViewer();
    officeViewer2.Text = "Demonstrate Conflict";
    officeViewer2.Visible = true;
    
  4. 솔루션 탐색기에서 OfficeViewer.vb 또는 OfficeViewer.cs를 두 번 클릭합니다.

    폼이 폼 디자이너에서 열립니다.

  5. 도구 상자에서 Button 컨트롤을 폼으로 끌어다 놓습니다. 해당 이름forceChanges로 설정하고 텍스트Force Changes로 설정합니다.

  6. Force Changes 단추를 두 번 클릭합니다.

    Office Viewer 폼의 코드 숨김 파일이 열립니다.

  7. 클라이언트 변경 내용이 서버에 강제로 저장되거나 instructorListComboBox 컨트롤에 바인딩된 데이터가 데이터베이스에서 새로 고쳐지도록 다음 코드를 forceChanges_Click 이벤트 처리기에 추가합니다.

    Dim numChanges As Integer
    Dim currentInstructor As Person = CType(Me.instructorList _
        .SelectedItem(), Person)
    Try
        currentInstructor.OfficeAssignment.Location = _
            Me.officeLocation.Text
        ' Using RefreshMode.ClientWins disables the
        ' optimistic concurrency check.
        schoolContext.Refresh(RefreshMode.ClientWins, _
                    currentInstructor.OfficeAssignment)
        numChanges = schoolContext.SaveChanges()
        MessageBox.Show(numChanges.ToString() + _
                " change(s) saved to the database.")
    
        'forceChanges.Enabled = False
    Catch ioe As InvalidOperationException
        MessageBox.Show(ioe.Message + " Click OK to retrieve " _
                + "the latest data from the database.")
        ExecuteInstructorQuery()
        Me.Refresh()
    End Try
    
    int numChanges;
    Person currentInstructor = (Person)this.instructorList
        .SelectedItem;
    try
    {
        currentInstructor.OfficeAssignment.Location
                    = this.officeLocation.Text;
    
        // Using RefreshMode.ClientWins disables the
        // optimistic concurrency check.
        schoolContext.Refresh(RefreshMode.ClientWins,
                currentInstructor.OfficeAssignment);
        numChanges = schoolContext.SaveChanges();
        MessageBox.Show(numChanges.ToString() +
            " change(s) saved to the database.");
    
        //forceChanges.Enabled = false;
    }
    catch (InvalidOperationException ioe)
    {
        MessageBox.Show(ioe.Message + " Click OK to retrieve "
            + "the latest data from the database.");
        ExecuteInstructorQuery();
        this.Refresh();
    }
    
  8. 새 강사가 선택되면 Force Changes 단추를 사용할 수 없게 되도록 instructorList_SelectedIndexChanged 이벤트 처리기에서 forceChanges = False(Visual Basic) 또는 forceChanges = false;(C#) 코드 줄의 주석 처리를 제거합니다.

  9. 동시성 충돌이 발생하면 Force Changes 단추를 사용할 수 없게 되도록 updateOffice_Click 이벤트 처리기에서 forceChanges = True(Visual Basic) 또는 forceChanges = true;(C#) 코드 줄의 주석 처리를 제거합니다.

  10. 변경 내용이 데이터베이스에 강제로 저장된 후 Force Changes 단추를 사용할 수 없게 되도록 forceChanges_Click 이벤트 처리기에서 forceChanges = False(Visual Basic) 또는 forceChanges = false;(C#) 코드 줄의 주석 처리를 제거합니다.

응용 프로그램에서 동시성 충돌을 처리하는지 확인하려면 응용 프로그램을 실행하고(Ctrl+F5 누름), View Instructors를 클릭한 다음 View Offices를 클릭합니다. Office Viewer 폼에서 사무실 위치를 업데이트한 다음 다른 Demonstrate Conflict 폼에서 같은 사무실 위치를 업데이트하려고 시도합니다. 동시성 충돌을 알리는 메시지 상자가 나타납니다. Demonstrate Conflict 폼의 변경 내용을 데이터베이스에 강제로 저장하려면 Force Changes를 클릭합니다.

코드 목록

이 단원에는 InstructorViewerOfficeViewer 폼에 대한 코드 숨김 파일의 최종 버전이 포함되어 있습니다.

Imports System.Data.Objects
Imports System.Data.Objects.DataClasses
Public Class InstructorViewer

    ' Create an ObjectContext instance based on SchoolEntity.
    Private schoolContext As SchoolEntities

    Private Sub viewOffices_Click(ByVal sender As System.Object, _
            ByVal e As System.EventArgs) Handles viewOffices.Click
        Dim officeViewer As New OfficeViewer()
        officeViewer.Visible = True

        Dim officeViewer2 As New OfficeViewer()
        officeViewer2.Text = "Demonstrate Conflict"
        officeViewer2.Visible = True
    End Sub

    Private Sub InstructorViewer_Load(ByVal sender As System.Object, _
                    ByVal e As System.EventArgs) Handles MyBase.Load
        ' Initialize the ObjectContext.
        schoolContext = New SchoolEntities()
        Dim instructorQuery As ObjectQuery(Of Person) = _
            schoolContext.Person.Include("OfficeAssignment") _
            .Where("it.HireDate is not null") _
            .OrderBy("it.LastName")
        instructorGridView.DataSource = instructorQuery _
            .Execute(MergeOption.OverwriteChanges)
        instructorGridView.Columns("EnrollmentDate").Visible = False
    End Sub

    Private Sub updateInstructor_Click(ByVal sender As System.Object, _
            ByVal e As System.EventArgs) Handles updateInstructor.Click
        Dim numChanges As Integer
        ' Save object changes to the database, display a 
        ' message, and refresh the form.
        numChanges = schoolContext.SaveChanges()
        MessageBox.Show(numChanges.ToString() + _
            " change(s) saved to the database.")
        Me.Refresh()
    End Sub
End Class
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Data.Objects;
using System.Data.Objects.DataClasses;

namespace CourseManager
{
    public partial class InstructorViewer : Form
    {
        // Create an ObjectContext instance based on SchoolEntity.
        private SchoolEntities schoolContext;

        public InstructorViewer()
        {
            InitializeComponent();
        }

        private void viewOffices_Click(object sender, EventArgs e)
        {
            OfficeViewer officeViewer = new OfficeViewer();
            officeViewer.Visible = true;

            OfficeViewer officeViewer2 = new OfficeViewer();
            officeViewer2.Text = "Demonstrate Conflict";
            officeViewer2.Visible = true;
        }

        private void InstructorViewer_Load(object sender, EventArgs e)
        {
            // Initialize schoolContext.
            schoolContext = new SchoolEntities();

            // Define the query to retrieve instructors.
            ObjectQuery<Person> instructorQuery = schoolContext.Person
                .Include("OfficeAssignment")
                .Where("it.HireDate is not null")
                .OrderBy("it.LastName");

            // Execute and bind the instructorList control to the query.
            instructorGridView.DataSource = instructorQuery.
                Execute(MergeOption.OverwriteChanges);
            instructorGridView.Columns["EnrollmentDate"].Visible = false;
        }

        private void updateInstructor_Click(object sender, EventArgs e)
        {
            int numChanges;
            // Save object changes to the database, display a 
            // message, and refresh the form.
            numChanges = schoolContext.SaveChanges();
            MessageBox.Show(numChanges.ToString() +
                " change(s) saved to the database.");
            this.Refresh();
        }
    }
}
Imports System.Data.Objects
Imports System.Data.Objects.DataClasses
Public Class OfficeViewer

    ' Create an ObjectContext instance based on SchoolEntity.
    Private schoolContext As SchoolEntities

    Private Sub OfficeViewer_Load(ByVal sender As System.Object, _
                ByVal e As System.EventArgs) Handles MyBase.Load
        schoolContext = New SchoolEntities()
        ExecuteInstructorQuery()
    End Sub

    Private Sub instructorList_SelectedIndexChanged(ByVal sender As  _
                System.Object, ByVal e As System.EventArgs) Handles _
                instructorList.SelectedIndexChanged
        Dim instructor As Person = CType(Me.instructorList _
            .SelectedItem(), Person)

        If Not instructor.OfficeAssignment Is Nothing Then
            Me.officeLocation.Text = instructor _
                .OfficeAssignment.Location.ToString()
        Else
            Me.officeLocation.Text = ""
        End If

        ' Disable the updateOffice button until a change
        ' has been made to the office location.
        updateOffice.Enabled = False

        'forceChanges.Enabled = False
    End Sub

    Private Sub updateOffice_Click(ByVal sender As System.Object, _
            ByVal e As System.EventArgs) Handles updateOffice.Click
        Try
            Dim numChanges As Integer
            Dim currentInstructor As Person = CType(Me.instructorList _
                .SelectedItem(), Person)
            If Me.officeLocation.Text <> String.Empty Then
                If Not currentInstructor.OfficeAssignment Is Nothing Then
                    currentInstructor.OfficeAssignment.Location() = _
                        Me.officeLocation.Text
                Else
                    Dim temp(8) As Byte
                    currentInstructor.OfficeAssignment = _
                        OfficeAssignment.CreateOfficeAssignment( _
                        currentInstructor.PersonID, _
                        Me.officeLocation.Text, temp)
                End If
            Else
                schoolContext.DeleteObject(currentInstructor. _
                                           OfficeAssignment)
            End If
            numChanges = schoolContext.SaveChanges()
            MessageBox.Show(numChanges.ToString() _
                    + " change(s) saved to the database.")
        Catch oce As OptimisticConcurrencyException
            MessageBox.Show(oce.Message + " The conflict " & _
                    "occurred on " & oce.StateEntries(0).Entity _
                    .ToString() & "with key value " & _
                    oce.StateEntries(0).EntityKey.EntityKeyValues(0) _
                    .Value)

            'forceChanges.Enabled = True
        Catch ue As UpdateException
            MessageBox.Show(ue.Message & " Click OK to retrieve " _
                    & "the latest data from the database.")
            ExecuteInstructorQuery()
            Me.Refresh()
        Finally
            ' Disable the updateOffice button until another
            ' change has been made to the location.
            updateOffice.Enabled = False
        End Try
    End Sub

    Private Sub officeLocation_TextChanged(ByVal sender As  _
                System.Object, ByVal e As System.EventArgs) _
                Handles officeLocation.TextChanged
        ' Enable the udateOffice button when there is a change
        ' to write to the database.
        updateOffice.Enabled = True
    End Sub

    Private Sub forceChanges_Click(ByVal sender As System.Object, _
            ByVal e As System.EventArgs) Handles forceChanges.Click
        Dim numChanges As Integer
        Dim currentInstructor As Person = CType(Me.instructorList _
            .SelectedItem(), Person)
        Try
            currentInstructor.OfficeAssignment.Location = _
                Me.officeLocation.Text
            ' Using RefreshMode.ClientWins disables the
            ' optimistic concurrency check.
            schoolContext.Refresh(RefreshMode.ClientWins, _
                        currentInstructor.OfficeAssignment)
            numChanges = schoolContext.SaveChanges()
            MessageBox.Show(numChanges.ToString() + _
                    " change(s) saved to the database.")

            'forceChanges.Enabled = False
        Catch ioe As InvalidOperationException
            MessageBox.Show(ioe.Message + " Click OK to retrieve " _
                    + "the latest data from the database.")
            ExecuteInstructorQuery()
            Me.Refresh()
        End Try
    End Sub

    Private Sub ExecuteInstructorQuery()
        ' Define the query to retrieve instructors.
        Dim instructorQuery As ObjectQuery(Of Person) = _
            schoolContext.Person.Include("OfficeAssignment"). _
            Where("it.HireDate is not null").OrderBy("it.LastName")

        'Execute and bind the instructorList control to the query.
        'Using MergeOption.OverwriteChanges overwrites local data
        'with data from the database.
        instructorList.DataSource = instructorQuery _
            .Execute(MergeOption.OverwriteChanges)
        instructorList.DisplayMember = "LastName"
    End Sub
End Class
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Data.Objects;
using System.Data.Objects.DataClasses;

namespace CourseManager
{
    public partial class OfficeViewer : Form
    {
        // Create an ObjectContext instance based on SchoolEntity.
        private SchoolEntities schoolContext;

        public OfficeViewer()
        {
            InitializeComponent();
        }

        private void OfficeViewer_Load(object sender, EventArgs e)
        {
            schoolContext = new SchoolEntities();
            ExecuteInstructorQuery();
        }

        private void instructorList_SelectedIndexChanged(object sender, 
        EventArgs e)
        {
            Person instructor = (Person)this.instructorList.
                SelectedItem;

            if (instructor.OfficeAssignment != null)
            {
                this.officeLocation.Text = instructor.
                    OfficeAssignment.Location.ToString();
            }
            else
            {
                this.officeLocation.Text = "";
            }

            // Disable the updateOffice button until a change
            // has been made to the office location.
            updateOffice.Enabled = false;

            //forceChanges.Enabled = false;
        }

        private void updateOffice_Click(object sender, EventArgs e)
        {
            try
            {
                int numChanges;
                Person currentInstructor = (Person)this.instructorList.
                    SelectedItem;
                if (this.officeLocation.Text != string.Empty)
                {
                    if (currentInstructor.OfficeAssignment != null)
                    {
                        currentInstructor.OfficeAssignment.Location
                            = this.officeLocation.Text;
                    }
                    else
                    {
                        currentInstructor.OfficeAssignment
                            = OfficeAssignment.CreateOfficeAssignment(
                            currentInstructor.PersonID, this.officeLocation.Text,
                            new byte[8]);
                    }
                }
                else
                {
                    schoolContext.DeleteObject(currentInstructor
                        .OfficeAssignment);
                }
                numChanges = schoolContext.SaveChanges();
                MessageBox.Show(numChanges.ToString() +
                    " change(s) saved to the database.");
            }
            catch (OptimisticConcurrencyException oce)
            {
                MessageBox.Show(oce.Message + " The conflict "
                    + "occurred on " + oce.StateEntries[0].Entity
                    + " with key value " + oce.StateEntries[0].
                    EntityKey.EntityKeyValues[0].Value);

                //forceChanges.Enabled = true;
            }
            catch (UpdateException ue)
            {
                MessageBox.Show(ue.Message + " Click OK to retrieve "
                    + "the latest data from the database.");
                ExecuteInstructorQuery();
                this.Refresh();
            }
            finally
            {
                // Disable the updateOffice button until another
                // change has been made to the location.
                updateOffice.Enabled = false;
            }
        }

        private void officeLocation_TextChanged(object sender, EventArgs e)
        {
            // Enable the udateOffice button when there is a change
            // to write to the database.
            updateOffice.Enabled = true;
        }

        private void forceChanges_Click(object sender, EventArgs e)
        {
            int numChanges;
            Person currentInstructor = (Person)this.instructorList
                .SelectedItem;
            try
            {
                currentInstructor.OfficeAssignment.Location
                            = this.officeLocation.Text;

                // Using RefreshMode.ClientWins disables the
                // optimistic concurrency check.
                schoolContext.Refresh(RefreshMode.ClientWins,
                        currentInstructor.OfficeAssignment);
                numChanges = schoolContext.SaveChanges();
                MessageBox.Show(numChanges.ToString() +
                    " change(s) saved to the database.");

                //forceChanges.Enabled = false;
            }
            catch (InvalidOperationException ioe)
            {
                MessageBox.Show(ioe.Message + " Click OK to retrieve "
                    + "the latest data from the database.");
                ExecuteInstructorQuery();
                this.Refresh();
            }
        }

        private void ExecuteInstructorQuery()
        {
            // Define the query to retrieve instructors.
            ObjectQuery<Person> instructorQuery = schoolContext.Person
                .Include("OfficeAssignment")
                .Where("it.HireDate is not null")
                .OrderBy("it.LastName");

            //Execute and bind the instructorList control to the query.
            //Using MergeOption.OverwriteChanges overwrites local data
            //with data from the database.
            instructorList.DataSource = instructorQuery
                .Execute(MergeOption.OverwriteChanges);
            instructorList.DisplayMember = "LastName";
        }
    }
}

다음 단계

엔터티의 삽입, 업데이트 및 삭제 작업을 저장 프로시저에 성공적으로 매핑했습니다. Entity Framework의 저장 프로시저 지원에 대한 자세한 내용은 저장 프로시저 지원(Entity Framework)을 참조하십시오. Entity Framework를 사용하는 응용 프로그램을 빌드하는 방법에 대한 자세한 내용은 프로그래밍 가이드(Entity Framework)를 참조하십시오.

참고 항목

기타 리소스

ADO.NET 엔터티 데이터 모델 디자이너 시나리오
엔터티 데이터 모델 도구 작업