Пошаговое руководство по сопоставлению сущностей с хранимыми процедурами (средства модели EDM)
В этом разделе показано, как использовать конструктор моделей EDM ADO.NET (конструктор сущностей) для сопоставления операций вставки, обновления и удаления типа сущности с хранимыми процедурами. В операциях вставки, обновления и удаления типа сущности могут использоваться инструкции SQL, автоматически формируемые системой (по умолчанию), или хранимые процедуры, которые определены разработчиком. Код приложения, используемый для создания, обновления и удаления сущностей, остается неизменным независимо от того, используются ли для обновления базы данных хранимые процедуры.
В этом пошаговом руководстве показано, как сопоставить два типа сущности (в концептуальной модели) с хранимыми процедурами (в модели хранения) путем изменения EDMX-файла в приложении CourseManager (дополнительные сведения см. в подразделе «Предварительные условия» далее в этом разделе). Кроме того, показано, как написать код, с помощью которого осуществляется вставка, обновление и удаление типов сущности.
Примечание |
---|
Если не выполнено сопоставление с хранимыми процедурами всех трех операций (вставки, обновления и удаления типов сущностей), то несопоставленные во время выполнения будут вызывать исключение UpdateException. |
Предварительные требования
Для работы с этим пошаговым руководством необходимо построить приложение CourseManager. Дополнительные сведения и инструкции см. в разделе Краткое руководство по Entity Framework. После сборки этого приложения необходимо внести изменения в его EDMX-файл, сопоставив два типа сущностей с хранимыми процедурами.
Примечание |
---|
Приложение CourseManager используется в качестве отправной точки во многих разделах пошагового руководства в данной документации, поэтому рекомендуется использовать для данного пошагового руководства копию приложения CourseManager, а не вносить изменения в первоначальный код CourseManager. |
В этом пошаговом руководстве предполагается, что читатель обладает основными навыками работы со средой Visual Studio и платформой .NET Framework, а также навыками программирования на языке Visual C# или Visual Basic.
Сопоставление сущности Person с хранимыми процедурами
При сопоставлении операции вставки сущности с хранимой процедурой необходимо учитывать, что, если сервер создает значение первичного ключа для вставленной строки, это значение должно быть снова сопоставлено со свойством ключа сущности. В этом примере хранимая процедура InsertPerson возвращает вновь созданный первичный ключ как часть результирующего набора хранимой процедуры. Первичный ключ сопоставляется с ключом сущности (PersonID) при помощи средства <Добавление привязок результатов> конструктора сущностей.
Примечание |
---|
При сопоставлении операций вставки, обновления или удаления с хранимой процедурой, возвращающей выходной параметр с целочисленным значением, устанавливается флажок Параметр числа затронутых строк.Если флажок выбран для параметра и в результате вызова операции возвращено нулевое значение, то будет вызвано исключение OptimisticConcurrencyException. |
Сопоставление сущности Person с хранимыми процедурами
Откройте решение CourseManager в среде Visual Studio.
В обозревателе решений дважды щелкните файл School.edmx.
Файл School.edmx открывается в конструкторе моделей EDM ADO.NET (в конструкторе сущностей).
Щелкните правой кнопкой мыши тип сущности Person и выберите пункт Сопоставление хранимых процедур.
Сопоставления хранимых процедур появятся в окне Сведения о сопоставлении.
Щелкните элемент <Выбор функции вставки>.
Поле становится раскрывающимся списком хранимых процедур в модели хранения, которые могут быть сопоставлены с типами сущностей в концептуальной модели.
Выберите пункт InsertPerson из раскрывающегося списка.
Появятся применяемые по умолчанию сопоставления параметров хранимой процедуры со свойствами сущности. Обратите внимание, что стрелки указывают направление сопоставления: «Значения свойств передаются параметрам хранимой процедуры».
Щелкните элемент <Добавление привязки к результату>.
Поле становится изменяемым.
Введите NewPersonID — имя параметра, возвращаемого хранимой процедурой InsertPerson. Нажмите клавишу ВВОД.
По умолчанию значение NewPersonID сопоставляется с ключом сущности PersonID. Обратите внимание, что стрелка указывает направление сопоставления — значение столбца результата передается свойству.
Щелкните элемент <Выбор функции обновления> и выберите команду UpdatePerson из результирующего раскрывающегося списка.
Появятся применяемые по умолчанию сопоставления параметров хранимой процедуры со свойствами сущности.
Щелкните элемент <Выбор функции удаления> и выберите команду DeletePerson из результирующего раскрывающегося списка.
Появятся применяемые по умолчанию сопоставления параметров хранимой процедуры со свойствами сущности.
Теперь операции вставки, обновления и удаления типа сущности Person сопоставлены с хранимыми процедурами.
Сопоставление сущности OfficeAssignment с хранимыми процедурами
В данном примере выполняется сопоставление типа сущности OfficeAssignment с хранимыми процедурами. В этом сопоставлении используется параметр Использовать исходное значение операции обновления для обеспечения удобного способа проверки параллелизма в прикладном коде.
Сопоставление сущности OfficeAssignment с хранимыми процедурами
Щелкните правой кнопкой мыши тип сущности OfficeAssignment и выберите команду Stored Procedure Mapping.
Сопоставления хранимых процедур появятся в окне Сведения о сопоставлении.
Щелкните элемент <Выбор функции вставки> и выберите команду InsertOfficeAssignment из появившегося раскрывающегося списка.
Появятся применяемые по умолчанию сопоставления параметров хранимой процедуры со свойствами сущности.
Щелкните элемент <Добавление привязки к результату>.
Поле становится изменяемым.
Введите команду Timestamp.
Щелкните пустое поле в столбце Property/Value рядом с Timestamp.
Это поле становится раскрывающимся списком свойств, с которым можно сопоставить значение, возвращаемое хранимой процедурой InsertOfficeAssignment.
Выберите пункт Timestamp из раскрывающегося списка.
Щелкните элемент <Выбор функции обновления> и выберите команду UpdateOfficeAssignment из результирующего раскрывающегося списка.
Появятся применяемые по умолчанию сопоставления параметров хранимой процедуры со свойствами сущности. В столбце Использовать исходное значение рядом с каждым сопоставленным свойством появляются флажки.
Щелкните пустое поле в столбце Свойство, которое соответствует параметру OrigTimestamp, и выберите Timestamp из результирующего раскрывающегося списка.
Конструктор сущностей не преобразует это сопоставление в применяемое по умолчанию, поскольку имя параметра точно не совпало с именем свойства.
Отметьте в столбце Использовать исходное значение флажок, который соответствует свойству Timestamp.
При попытке обновления значение свойства Timestamp, которое было первоначально считано из базы данных, будет использоваться при записи данных обратно в базу данных. Если это значение не согласуется со значением в базе данных, то активизируется исключение OptimisticConcurrencyException.
Щелкните элемент <Добавление привязки к результату>.
Поле становится изменяемым.
Замените значения <Добавление привязки к результату> значением Timestamp.
Щелкните пустое поле в столбце Property/Value рядом с Timestamp.
Поле становится раскрывающимся списком свойств, с которыми можно сопоставить столбец результата, возвращаемый хранимой процедурой UpdateOfficeAssignment.
Выберите пункт Timestamp из раскрывающегося списка.
Щелкните элемент <Выбор функции удаления> и выберите команду DeleteOfficeAssignment из результирующего раскрывающегося списка.
Появятся применяемые по умолчанию сопоставления параметров хранимой процедуры со свойствами сущности.
Теперь операции вставки, обновления и удаления типа сущности OfficeAssignment сопоставлены с хранимыми процедурами.
Создание пользовательского интерфейса
Затем нужно добавить две формы к приложению CourseManager. Одна из этих форм предоставляет интерфейс для просмотра и обновления сведений о преподавателе. Другая форма предоставляет интерфейс для просмотра и обновления распределения аудиторий.
Создание пользовательского интерфейса
Щелкните правой кнопкой мыши имя проекта CourseManager в окне Обозреватель решений, укажите пункт Добавить и выберите пункт Новый элемент.
Появится диалоговое окно Добавление нового элемента.
Выберите элемент Windows Form, задайте в качестве значения формы InstructorViewer.vb или InstructorViewer.cs и щелкните элемент Добавить.
Новая форма будет добавлена к проекту и открыта в конструкторе форм. Форме назначается имя InstructorViewer и текст InstructorViewer.
Перетащите элемент управления DataGridView из области элементов в форму и задайте для свойства Имя значение instructorGridView в окне Свойства.
Перетащите элемент управления Button из области элементов в форму. Задайте для его свойства Имя значение updateInstructor, а для свойства Текст значение Update Instructor.
Перетащите еще один элемент управления Button из области элементов в форму. Задайте для его свойства Имя значение viewOffices, а для свойства Текст значение View Offices.
Щелкните правой кнопкой мыши имя проекта CourseManager в окне Обозреватель решений, укажите пункт Добавить и выберите пункт Новый элемент.
Появится диалоговое окно Добавление нового элемента.
Выберите элемент Windows Form, задайте для имени формы значение OfficeViewer.vb или OfficeViewer.cs и щелкните элемент Добавить.
Новая форма будет добавлена к проекту и открыта в конструкторе форм. Форме назначается имя OfficeViewer и текст OfficeViewer.
Перетащите элемент управления ComboBox из области элементов в форму и задайте для его свойства Имя значение instructorList.
Перетащите элемент управления TextBox из области элементов в форму и задайте для его свойства Имя значение officeLocation.
Перетащите элемент управления Button из области элементов в форму. Задайте для его свойства Имя значение updateOffice, а для свойства Текст значение Update Office.
В окне Обозреватель решений дважды щелкните элемент CourseViewer.vb или CourseViewer.cs.
Форма CourseViewer откроется в конструкторе.
Перетащите элемент управления Button из области элементов в форму.
В окне Свойства задайте для свойства Имя элемента Button значение viewInstructors, а для свойства Текст значение View Instructors.
Дважды щелкните элемент управления viewInstructors Button.
Откроется файл с фоновым кодом для формы CourseViewer.
Добавьте следующий код в обработчик события viewInstructors_Click:
Dim instructorViewer As New InstructorViewer() instructorViewer.Visible = True
InstructorViewer instructorViewer = new InstructorViewer(); instructorViewer.Visible = true;
Возвратитесь к конструктору формы InstructorViewer.
Дважды щелкните элемент управления viewOffices Button.
Откроется файл с фоновым кодом для Form2.
Добавьте следующий код в обработчик события 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.
Просмотр и обновление сведений о преподавателях
При открытой форме InstructorViewer в конструкторе форм дважды щелкните форму InstructorViewer.
Откроется файл с фоновым кодом для формы InstructorViewer.
Добавьте следующие инструкции using (C#) или Imports (Visual Basic):
Imports System.Data.Objects Imports System.Data.Objects.DataClasses
using System.Data.Objects; using System.Data.Objects.DataClasses;
Добавьте свойство к классу InstructorViewer, который представляет контекст объекта:
' Create an ObjectContext instance based on SchoolEntity. Private schoolContext As SchoolEntities
// Create an ObjectContext instance based on SchoolEntity. private SchoolEntities schoolContext;
В обработчике события 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;
Возвратитесь к конструктору формы InstructorViewer и дважды щелкните элемент управления updateInstructor Button.
Обработчик события updateInstructor_Click добавляется к файлу с фоновым кодом.
Добавьте код к обработчику события 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.
Просмотр и обновление сведений об аудитории
При открытой форме OfficeViewer в конструкторе форм дважды щелкните форму OfficeViewer.
Откроется файл с фоновым кодом для формы OfficeViewer.
Добавьте следующие инструкции using (C#) или Imports (Visual Basic):
Imports System.Data.Objects Imports System.Data.Objects.DataClasses
using System.Data.Objects; using System.Data.Objects.DataClasses;
Добавьте свойство к классу OfficeViewer, который представляет контекст объекта:
' Create an ObjectContext instance based on SchoolEntity. Private schoolContext As SchoolEntities
// Create an ObjectContext instance based on SchoolEntity. private SchoolEntities schoolContext;
Добавьте следующий метод к форме:
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.
В обработчике события OfficeViewer_Load добавьте код инициализации контекста объекта и вызовите метод, который привязывает элемент управления ComboBox к запросу, возвращающему все типы Person, не имеющие свойства null HireDate.
schoolContext = New SchoolEntities() ExecuteInstructorQuery()
schoolContext = new SchoolEntities(); ExecuteInstructorQuery();
Возвратитесь к конструктору формы OfficeViewer и дважды щелкните элемент управления instructorList ComboBox.
Обработчик события instructorList_SelectedIndexChanged добавляется к файлу с фоновым кодом.
Добавьте к обработчику события код, который отображает местоположение аудитории выбранного преподавателя в элементе управления 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;
Возвратитесь к конструктору формы OfficeViewer и дважды щелкните элемент управления updateOffice Button.
Обработчик события updateOffice_Click добавляется к файлу с фоновым кодом.
Добавьте код, который сохраняет все изменения, внесенные в сведения об аудитории, в элементе управления 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; }
Возвратитесь к конструктору формы OfficeViewer и дважды щелкните элемент управления officeLocation TextBox.
Обработчик события officeLocation_TextChanged добавляется к файлу с фоновым кодом.
Добавьте код для включения элемента управления 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 код, который принудительно вносит клиентские изменения в базу данных после возникновения конфликта параллелизма.
Обработка конфликтов параллелизма
Дважды щелкните элемент InstructorViewer.vb или InstructorViewer.cs в окне Обозреватель решений.
Форма откроется в конструкторе форм.
Дважды нажмите кнопку View Offices.
Откроется файл с фоновым кодом для формы InstructorViewer.
Добавьте следующий код к обработчику события 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;
Дважды щелкните элемент OfficeViewer.vb или OfficeViewer.cs в окне Обозреватель решений.
Форма откроется в конструкторе форм.
Перетащите элемент управления Button из области элементов в форму. Задайте для его свойства Имя значение forceChanges, а для свойства Текст, значение Force Changes.
Дважды нажмите кнопку Force Changes.
Откроется файл с фоновым кодом для формы Office Viewer.
Добавьте следующий код к обработчику события 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(); }
Уберите комментарий из строки кода
forceChanges = False
(Visual Basic) илиforceChanges = false;
(C#) в обработчике события instructorList_SelectedIndexChanged, чтобы кнопка Force Changes была отключена при выборе нового преподавателя.Уберите комментарий из строки кода
forceChanges = True
(Visual Basic) илиforceChanges = true;
(C#) в обработчике события updateOffice_Click, чтобы кнопка Force Changes была включена при возникновении конфликта параллелизма.Уберите комментарий из строки кода
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