Пошаговое руководство по сопоставлению сущностей с хранимыми процедурами (средства модели EDM)

В этом разделе показано, как использовать конструктор моделей EDM ADO.NET (конструктор сущностей) для сопоставления операций вставки, обновления и удаления типа сущности с хранимыми процедурами. В операциях вставки, обновления и удаления типа сущности могут использоваться инструкции SQL, автоматически формируемые системой (по умолчанию), или хранимые процедуры, которые определены разработчиком. Код приложения, используемый для создания, обновления и удаления сущностей, остается неизменным независимо от того, используются ли для обновления базы данных хранимые процедуры.

В этом пошаговом руководстве показано, как сопоставить два типа сущности (в концептуальной модели) с хранимыми процедурами (в модели хранения) путем изменения EDMX-файла в приложении CourseManager (дополнительные сведения см. в подразделе «Предварительные условия» далее в этом разделе). Кроме того, показано, как написать код, с помощью которого осуществляется вставка, обновление и удаление типов сущности.

Cc716679.note(ru-ru,VS.100).gifПримечание
Если не выполнено сопоставление с хранимыми процедурами всех трех операций (вставки, обновления и удаления типов сущностей), то несопоставленные во время выполнения будут вызывать исключение UpdateException.

Предварительные требования

Для работы с этим пошаговым руководством необходимо построить приложение CourseManager. Дополнительные сведения и инструкции см. в разделе Краткое руководство по Entity Framework. После сборки этого приложения необходимо внести изменения в его EDMX-файл, сопоставив два типа сущностей с хранимыми процедурами.

Cc716679.note(ru-ru,VS.100).gifПримечание
Приложение CourseManager используется в качестве отправной точки во многих разделах пошагового руководства в данной документации, поэтому рекомендуется использовать для данного пошагового руководства копию приложения CourseManager, а не вносить изменения в первоначальный код CourseManager.

В этом пошаговом руководстве предполагается, что читатель обладает основными навыками работы со средой Visual Studio и платформой .NET Framework, а также навыками программирования на языке Visual C# или Visual Basic.

Сопоставление сущности Person с хранимыми процедурами

При сопоставлении операции вставки сущности с хранимой процедурой необходимо учитывать, что, если сервер создает значение первичного ключа для вставленной строки, это значение должно быть снова сопоставлено со свойством ключа сущности. В этом примере хранимая процедура InsertPerson возвращает вновь созданный первичный ключ как часть результирующего набора хранимой процедуры. Первичный ключ сопоставляется с ключом сущности (PersonID) при помощи средства <Добавление привязок результатов> конструктора сущностей.

Cc716679.note(ru-ru,VS.100).gifПримечание
При сопоставлении операций вставки, обновления или удаления с хранимой процедурой, возвращающей выходной параметр с целочисленным значением, устанавливается флажок Параметр числа затронутых строк.Если флажок выбран для параметра и в результате вызова операции возвращено нулевое значение, то будет вызвано исключение OptimisticConcurrencyException.

Сопоставление сущности Person с хранимыми процедурами

  1. Откройте решение CourseManager в среде Visual Studio.

  2. В обозревателе решений дважды щелкните файл School.edmx.

    Файл School.edmx открывается в конструкторе моделей EDM ADO.NET (в конструкторе сущностей).

  3. Щелкните правой кнопкой мыши тип сущности Person и выберите пункт Сопоставление хранимых процедур.

    Сопоставления хранимых процедур появятся в окне Сведения о сопоставлении.

  4. Щелкните элемент <Выбор функции вставки>.

    Поле становится раскрывающимся списком хранимых процедур в модели хранения, которые могут быть сопоставлены с типами сущностей в концептуальной модели.

  5. Выберите пункт InsertPerson из раскрывающегося списка.

    Появятся применяемые по умолчанию сопоставления параметров хранимой процедуры со свойствами сущности. Обратите внимание, что стрелки указывают направление сопоставления: «Значения свойств передаются параметрам хранимой процедуры».

  6. Щелкните элемент <Добавление привязки к результату>.

    Поле становится изменяемым.

  7. Введите NewPersonID — имя параметра, возвращаемого хранимой процедурой InsertPerson. Нажмите клавишу ВВОД.

    По умолчанию значение NewPersonID сопоставляется с ключом сущности PersonID. Обратите внимание, что стрелка указывает направление сопоставления — значение столбца результата передается свойству.

  8. Щелкните элемент <Выбор функции обновления> и выберите команду UpdatePerson из результирующего раскрывающегося списка.

    Появятся применяемые по умолчанию сопоставления параметров хранимой процедуры со свойствами сущности.

  9. Щелкните элемент <Выбор функции удаления> и выберите команду DeletePerson из результирующего раскрывающегося списка.

    Появятся применяемые по умолчанию сопоставления параметров хранимой процедуры со свойствами сущности.

Теперь операции вставки, обновления и удаления типа сущности Person сопоставлены с хранимыми процедурами.

Сопоставление сущности OfficeAssignment с хранимыми процедурами

В данном примере выполняется сопоставление типа сущности OfficeAssignment с хранимыми процедурами. В этом сопоставлении используется параметр Использовать исходное значение операции обновления для обеспечения удобного способа проверки параллелизма в прикладном коде.

Сопоставление сущности OfficeAssignment с хранимыми процедурами

  1. Щелкните правой кнопкой мыши тип сущности OfficeAssignment и выберите команду Stored Procedure Mapping.

    Сопоставления хранимых процедур появятся в окне Сведения о сопоставлении.

  2. Щелкните элемент <Выбор функции вставки> и выберите команду InsertOfficeAssignment из появившегося раскрывающегося списка.

    Появятся применяемые по умолчанию сопоставления параметров хранимой процедуры со свойствами сущности.

  3. Щелкните элемент <Добавление привязки к результату>.

    Поле становится изменяемым.

  4. Введите команду Timestamp.

  5. Щелкните пустое поле в столбце Property/Value рядом с Timestamp.

    Это поле становится раскрывающимся списком свойств, с которым можно сопоставить значение, возвращаемое хранимой процедурой InsertOfficeAssignment.

  6. Выберите пункт Timestamp из раскрывающегося списка.

  7. Щелкните элемент <Выбор функции обновления> и выберите команду UpdateOfficeAssignment из результирующего раскрывающегося списка.

    Появятся применяемые по умолчанию сопоставления параметров хранимой процедуры со свойствами сущности. В столбце Использовать исходное значение рядом с каждым сопоставленным свойством появляются флажки.

  8. Щелкните пустое поле в столбце Свойство, которое соответствует параметру OrigTimestamp, и выберите Timestamp из результирующего раскрывающегося списка.

    Конструктор сущностей не преобразует это сопоставление в применяемое по умолчанию, поскольку имя параметра точно не совпало с именем свойства.

  9. Отметьте в столбце Использовать исходное значение флажок, который соответствует свойству Timestamp.

    При попытке обновления значение свойства Timestamp, которое было первоначально считано из базы данных, будет использоваться при записи данных обратно в базу данных. Если это значение не согласуется со значением в базе данных, то активизируется исключение OptimisticConcurrencyException.

  10. Щелкните элемент <Добавление привязки к результату>.

    Поле становится изменяемым.

  11. Замените значения <Добавление привязки к результату> значением Timestamp.

  12. Щелкните пустое поле в столбце Property/Value рядом с Timestamp.

    Поле становится раскрывающимся списком свойств, с которыми можно сопоставить столбец результата, возвращаемый хранимой процедурой UpdateOfficeAssignment.

  13. Выберите пункт Timestamp из раскрывающегося списка.

  14. Щелкните элемент <Выбор функции удаления> и выберите команду 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. Дважды щелкните элемент управления viewInstructors Button.

    Откроется файл с фоновым кодом для формы CourseViewer.

  15. Добавьте следующий код в обработчик события viewInstructors_Click:

    Dim instructorViewer As New InstructorViewer()
    instructorViewer.Visible = True
    
    InstructorViewer instructorViewer = new InstructorViewer();
    instructorViewer.Visible = true;
    
  16. Возвратитесь к конструктору формы InstructorViewer.

  17. Дважды щелкните элемент управления viewOffices Button.

    Откроется файл с фоновым кодом для Form2.

  18. Добавьте следующий код в обработчик события viewOffices_Click:

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

Создание пользовательского интерфейса завершено.

Просмотр и обновление сведений о преподавателях

В этой процедуре показано, как добавить к форме InstructorViewer код, который позволяет просматривать и обновлять сведения о преподавателе. Точнее, в этом коде выполняются следующие действия.

  • Привязка объекта DataGridView к запросу, который возвращает информацию о типах Person, представляющих преподавателей. Дополнительные сведения о привязке объектов к элементам управления см. в разделе Binding Objects to Controls (Entity Framework).

  • Сохранение любых изменений (вставки, обновления или удаления) элемента управления DataGridView в базе данных.

  • Использование сопоставленных ранее хранимых процедур для записи данных в базу данных при вызове метода SaveChanges() в обработчике события updateInstructor_Click.

Просмотр и обновление сведений о преподавателях

  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 запрос, который возвращает все типы Person, не имеющие свойств null HireDate.

    ' Initialize the ObjectContext.
    schoolContext = New SchoolEntities()
    Dim instructorQuery As ObjectQuery(Of Person) = _
        schoolContext.People.Include("OfficeAssignment") _
        .Where("it.HireDate is not null") _
        .OrderBy("it.LastName")
    instructorGridView.DataSource = instructorQuery _
        .Execute(MergeOption.OverwriteChanges)
    instructorGridView.Columns("EnrollmentDate").Visible = False
    instructorGridView.Columns("EnrollmentDate").Visible = False
    instructorGridView.Columns("OfficeAssignment").Visible = False
    instructorGridView.Columns("StudentGrades").Visible = False
    instructorGridView.Columns("Courses").Visible = False
    
    // Initialize schoolContext.
    schoolContext = new SchoolEntities();
    
    // Define the query to retrieve instructors.
    ObjectQuery<Person> instructorQuery = schoolContext.People
        .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;
    instructorGridView.Columns["OfficeAssignment"].Visible = false;
    instructorGridView.Columns["StudentGrades"].Visible = false;
    instructorGridView.Columns["Courses"].Visible = false;
    
  5. Возвратитесь к конструктору формы InstructorViewer и дважды щелкните элемент управления updateInstructor Button.

    Обработчик события updateInstructor_Click добавляется к файлу с фоновым кодом.

  6. Добавьте код к обработчику события updateInstructor_Click, который сохраняет любые изменения, внесенные в сведения о преподавателе, в элементе управления instructorGridView DataGridView.

    ' Save object changes to the database, display a 
    ' message, and refresh the form.
    schoolContext.SaveChanges()
    MessageBox.Show("Change(s) saved to the database.")
    Me.Refresh()
    
    // Save object changes to the database, display a 
    // message, and refresh the form.
    schoolContext.SaveChanges();
    MessageBox.Show("Change(s) saved to the database.");
    this.Refresh();
    

Нажмите клавиши Ctrl + F5, чтобы запустить приложение. Теперь сведения о преподавателе можно просматривать и обновлять, щелкая элемент View Instructors, внося изменения в открывшуюся таблицу и щелкая элемент Update Instructor.

Просмотр и обновление сведений об аудитории

В этой процедуре показано, как добавить код к форме OfficeViewer, который позволит просматривать и обновлять сведения о распределениях аудиторий. Точнее, в этом коде выполняются следующие действия.

  • Привязка свойства ComboBox к запросу, который возвращает сведения о преподавателе.

  • Отображение сведений о местоположении аудитории для выбранного преподавателя в TextBox.

  • Использование сопоставленных ранее хранимых процедур для записи данных в базу данных при вызове метода SaveChanges() в обработчике события updateOffice_Click.

Просмотр и обновление сведений об аудитории

  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.People.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.People
            .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";
    }
    

    Этот метод выполняет запрос, который возвращает сведения о преподавателе, и привязывает результаты к элементу управления instructorList ComboBox.

  5. В обработчике события OfficeViewer_Load добавьте код инициализации контекста объекта и вызовите метод, который привязывает элемент управления ComboBox к запросу, возвращающему все типы Person, не имеющие свойства null HireDate.

    schoolContext = New SchoolEntities()
    ExecuteInstructorQuery()
    
    schoolContext = new SchoolEntities();
    ExecuteInstructorQuery();
    
  6. Возвратитесь к конструктору формы OfficeViewer и дважды щелкните элемент управления instructorList ComboBox.

    Обработчик события instructorList_SelectedIndexChanged добавляется к файлу с фоновым кодом.

  7. Добавьте к обработчику события код, который отображает местоположение аудитории выбранного преподавателя в элементе управления ListBox и отключает элемент управления updateOffice Button. Этот элемент управления будет включен после внесения изменения в выбранное местоположение аудитории.

    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 и дважды щелкните элемент управления updateOffice Button.

    Обработчик события updateOffice_Click добавляется к файлу с фоновым кодом.

  9. Добавьте код, который сохраняет все изменения, внесенные в сведения об аудитории, в элементе управления officeLocation TextBox:

    Try
        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
        schoolContext.SaveChanges()
        MessageBox.Show("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
    {
        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);
        }
        schoolContext.SaveChanges();
        MessageBox.Show("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 и дважды щелкните элемент управления officeLocation TextBox.

    Обработчик события officeLocation_TextChanged добавляется к файлу с фоновым кодом.

  11. Добавьте код для включения элемента управления updateOffice Button после внесения изменения в выбранное местоположение аудитории:

    ' 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. Добавьте следующий код к обработчику события viewOffices_Click, чтобы загружались две формы OfficeViewer при нажатии кнопки View Offices.

    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. Добавьте следующий код к обработчику события forceChanges_Click, чтобы изменения в клиенте были принудительно переданы на сервер или данные, привязанные к элементу управления instructorList ComboBox, были восстановлены из базы данных.

    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)
        schoolContext.SaveChanges()
        MessageBox.Show("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
    
    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);
        schoolContext.SaveChanges();
        MessageBox.Show("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. Уберите комментарий из строки кода forceChanges = False (Visual Basic) или forceChanges = false; (C#) в обработчике события instructorList_SelectedIndexChanged, чтобы кнопка Force Changes была отключена при выборе нового преподавателя.

  9. Уберите комментарий из строки кода forceChanges = True (Visual Basic) или forceChanges = true; (C#) в обработчике события updateOffice_Click, чтобы кнопка Force Changes была включена при возникновении конфликта параллелизма.

  10. Уберите комментарий из строки кода forceChanges = False (Visual Basic) или forceChanges = false; (C#) в обработчике события forceChanges_Click, чтобы кнопка Force Changes была отключена после принудительной передачи изменений в базу данных.

Чтобы видеть, как происходит в приложении обработка конфликта параллелизма, запустите приложение (нажав клавиши Ctrl+F5), щелкните элемент View Instructors, а затем щелкните элемент View Offices. Обновите местоположение аудитории в форме Office Viewer, а затем попытайтесь обновить местоположение той же аудитории в другой форме Demonstrate Conflict. Появится окно сообщения с уведомлением о возникновении конфликта параллелизма. Чтобы принудительно передать изменения из формы Demonstrate Conflict в базу данных, щелкните элемент Force Changes.

Листинг кода

В этом разделе содержатся конечные версии файлов с фоновым кодом для форм InstructorViewer и OfficeViewer.

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.People.Include("OfficeAssignment") _
            .Where("it.HireDate is not null") _
            .OrderBy("it.LastName")
        instructorGridView.DataSource = instructorQuery _
            .Execute(MergeOption.OverwriteChanges)
        instructorGridView.Columns("EnrollmentDate").Visible = False
        instructorGridView.Columns("EnrollmentDate").Visible = False
        instructorGridView.Columns("OfficeAssignment").Visible = False
        instructorGridView.Columns("StudentGrades").Visible = False
        instructorGridView.Columns("Courses").Visible = False
    End Sub

    Private Sub updateInstructor_Click(ByVal sender As System.Object, _
            ByVal e As System.EventArgs) Handles updateInstructor.Click
        ' Save object changes to the database, display a 
        ' message, and refresh the form.
        schoolContext.SaveChanges()
        MessageBox.Show("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.People
                .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;
            instructorGridView.Columns["OfficeAssignment"].Visible = false;
            instructorGridView.Columns["StudentGrades"].Visible = false;
            instructorGridView.Columns["Courses"].Visible = false;
        }

        private void updateInstructor_Click(object sender, EventArgs e)
        {
            // Save object changes to the database, display a 
            // message, and refresh the form.
            schoolContext.SaveChanges();
            MessageBox.Show("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 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
            schoolContext.SaveChanges()
            MessageBox.Show("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 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)
            schoolContext.SaveChanges()
            MessageBox.Show("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.People.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
            {
                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);
                }
                schoolContext.SaveChanges();
                MessageBox.Show("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)
        {
            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);
                schoolContext.SaveChanges();
                MessageBox.Show("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.People
                .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.

См. также

Другие ресурсы

Сценарии средств работы с моделью EDM
Задачи средств модели EDM