Обновление связанных данных с помощью Entity Framework в приложении MVC ASP.NET (6 из 10)
Пример веб-приложения Contoso University демонстрирует создание ASP.NET приложений MVC 4 с помощью Entity Framework 5 Code First и Visual Studio 2012. Сведения о серии руководств см. в первом руководстве серии.
Примечание.
Если вы не сможете устранить проблему, скачайте завершенную главу и попытайтесь воспроизвести проблему. Как правило, решение проблемы можно найти, сравнивая код с завершенным кодом. Некоторые распространенные ошибки и способы их устранения см. в статье об ошибках и обходных решениях.
В предыдущем руководстве вы отображали связанные данные; В этом руководстве вы обновите связанные данные. Для большинства связей это можно сделать, обновив соответствующие поля внешнего ключа. Для связей "многие ко многим" платформа Entity Framework не предоставляет таблицу соединения напрямую, поэтому необходимо явно добавлять и удалять сущности в соответствующие свойства навигации и из него.
На следующих рисунках изображены страницы, с которыми вы будете работать.
Настройка страниц создания и редактирования для курсов
Создаваемая сущность курса должна иметь связь с существующей кафедрой. Чтобы упростить эту задачу, шаблонный код включает методы контроллеров, а также представления "Create" (Создание) и "Edit" (Редактирование) с раскрывающимся списком для выбора кафедры. Раскрывающийся список задает свойство внешнего ключа Course.DepartmentID
, и это все, что нужно Entity Framework для загрузки свойства навигации Department
с соответствующей сущностью Department
. Вы будете использовать этот шаблонный код, немного его изменив, чтобы добавить обработку ошибок и сортировку раскрывающегося списка.
В CourseController.cs удалите четыре Edit
и методы и Create
замените их следующим кодом:
public ActionResult Create()
{
PopulateDepartmentsDropDownList();
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(
[Bind(Include = "CourseID,Title,Credits,DepartmentID")]
Course course)
{
try
{
if (ModelState.IsValid)
{
db.Courses.Add(course);
db.SaveChanges();
return RedirectToAction("Index");
}
}
catch (DataException /* dex */)
{
//Log the error (uncomment dex variable name after DataException and add a line here to write a log.)
ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator.");
}
PopulateDepartmentsDropDownList(course.DepartmentID);
return View(course);
}
public ActionResult Edit(int id)
{
Course course = db.Courses.Find(id);
PopulateDepartmentsDropDownList(course.DepartmentID);
return View(course);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(
[Bind(Include = "CourseID,Title,Credits,DepartmentID")]
Course course)
{
try
{
if (ModelState.IsValid)
{
db.Entry(course).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
}
catch (DataException /* dex */)
{
//Log the error (uncomment dex variable name after DataException and add a line here to write a log.)
ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator.");
}
PopulateDepartmentsDropDownList(course.DepartmentID);
return View(course);
}
private void PopulateDepartmentsDropDownList(object selectedDepartment = null)
{
var departmentsQuery = from d in db.Departments
orderby d.Name
select d;
ViewBag.DepartmentID = new SelectList(departmentsQuery, "DepartmentID", "Name", selectedDepartment);
}
Метод PopulateDepartmentsDropDownList
получает список всех отделов, отсортированных по имени, создает SelectList
коллекцию для раскрывающегося списка и передает коллекцию в представление в свойстве ViewBag
. Этот метод принимает необязательный параметр selectedDepartment
, позволяющий вызывающему коду указать элемент, который будет выбран при отрисовке раскрывающегося списка. Представление передает имя DepartmentID
DropDownList
вспомогательному элементу, а помощник затем знает, как выглядеть в объекте ViewBag
для именованного.SelectList
DepartmentID
Метод HttpGet
Create
вызывает PopulateDepartmentsDropDownList
метод без настройки выбранного элемента, так как для нового курса отдел еще не установлен:
public ActionResult Create()
{
PopulateDepartmentsDropDownList();
return View();
}
Метод HttpGet
Edit
задает выбранный элемент на основе идентификатора отдела, который уже назначен курсу для редактирования:
public ActionResult Edit(int id)
{
Course course = db.Courses.Find(id);
PopulateDepartmentsDropDownList(course.DepartmentID);
return View(course);
}
Методы HttpPost
для обоих Create
элементов, Edit
а также код, который задает выбранный элемент при повторном воспроизведении страницы после ошибки:
catch (DataException /* dex */)
{
//Log the error (uncomment dex variable name after DataException and add a line here to write a log.)
ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator.");
}
PopulateDepartmentsDropDownList(course.DepartmentID);
return View(course);
Этот код гарантирует, что при повторном воспроизведении страницы для отображения сообщения об ошибке любой отдел остается выбранным.
В Views\Course\Create.cshtml добавьте выделенный код, чтобы создать новое поле номера курса перед полем Title . Как описано в предыдущем руководстве, поля первичного ключа по умолчанию не являются шаблонами, но этот первичный ключ имеет смысл, поэтому вы хотите, чтобы пользователь мог ввести значение ключа.
@model ContosoUniversity.Models.Course
@{
ViewBag.Title = "Create";
}
<h2>Create</h2>
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
@Html.ValidationSummary(true)
<fieldset>
<legend>Course</legend>
<div class="editor-label">
@Html.LabelFor(model => model.CourseID)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.CourseID)
@Html.ValidationMessageFor(model => model.CourseID)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Title)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Title)
@Html.ValidationMessageFor(model => model.Title)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Credits)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Credits)
@Html.ValidationMessageFor(model => model.Credits)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.DepartmentID, "Department")
</div>
<div class="editor-field">
@Html.DropDownList("DepartmentID", String.Empty)
@Html.ValidationMessageFor(model => model.DepartmentID)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
@Html.ActionLink("Back to List", "Index")
</div>
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}
В представлениях\Course\Edit.cshtml, Views\Course\Delete.cshtml и Views\Course\Details.cshtml добавьте поле номера курса перед полем Title . Так как это первичный ключ, он отображается, но его нельзя изменить.
<div class="editor-label">
@Html.LabelFor(model => model.CourseID)
</div>
<div class="editor-field">
@Html.DisplayFor(model => model.CourseID)
</div>
Запустите страницу "Создать" (отобразите страницу индекса курса и нажмите кнопку "Создать") и введите данные для нового курса:
Нажмите кнопку Создать. Страница индекса курса отображается с новым курсом, добавленным в список. Название кафедры в списке страницы индекса поступает из свойства навигации, показывая, что связь установлена правильно.
Запустите страницу "Изменить" (отобразите страницу "Индекс курса" и нажмите кнопку "Изменить" для курса).
Измените данные на странице и нажмите кнопку Save (Сохранить). Страница индекса курса отображается с обновленными данными курса.
Добавление страницы редактирования для преподавателей
При редактировании записи преподавателя может потребоваться обновить назначенный преподавателю кабинет. Сущность Instructor
имеет связь "один к нулю" или "один" с сущностью OfficeAssignment
, что означает, что необходимо обрабатывать следующие ситуации:
- Если пользователь очищает назначение office и изначально имеет значение, необходимо удалить и удалить
OfficeAssignment
сущность. - Если пользователь вводит значение назначения office и изначально был пустым, необходимо создать новую
OfficeAssignment
сущность. - Если пользователь изменяет значение назначения office, необходимо изменить значение существующей
OfficeAssignment
сущности.
Откройте InstructorController.cs и просмотрите HttpGet
Edit
метод:
public ActionResult Edit(int id = 0)
{
Instructor instructor = db.Instructors.Find(id);
if (instructor == null)
{
return HttpNotFound();
}
ViewBag.InstructorID = new SelectList(db.OfficeAssignments, "InstructorID", "Location", instructor.InstructorID);
return View(instructor);
}
Шаблонный код здесь не является нужным. Он настраивает данные для раскрывающегося списка, но вам нужно текстовое поле. Замените этот метод следующим кодом:
public ActionResult Edit(int id)
{
Instructor instructor = db.Instructors
.Include(i => i.OfficeAssignment)
.Where(i => i.InstructorID == id)
.Single();
return View(instructor);
}
Этот код удаляет инструкцию ViewBag
и добавляет страстную загрузку связанной OfficeAssignment
сущности. Вы не можете выполнять страстную загрузку с Find
помощью метода, поэтому Single
Where
вместо этого используются методы для выбора инструктора.
Замените HttpPost
Edit
метод следующим кодом. который обрабатывает обновления назначения office:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(int id, FormCollection formCollection)
{
var instructorToUpdate = db.Instructors
.Include(i => i.OfficeAssignment)
.Where(i => i.InstructorID == id)
.Single();
if (TryUpdateModel(instructorToUpdate, "",
new string[] { "LastName", "FirstMidName", "HireDate", "OfficeAssignment" }))
{
try
{
if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment.Location))
{
instructorToUpdate.OfficeAssignment = null;
}
db.Entry(instructorToUpdate).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
catch (DataException /* dex */)
{
//Log the error (uncomment dex variable name after DataException and add a line here to write a log.
ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator.");
}
}
ViewBag.InstructorID = new SelectList(db.OfficeAssignments, "InstructorID", "Location", id);
return View(instructorToUpdate);
}
Код делает следующее:
Получает текущую сущность
Instructor
из базы данных, используя безотложную загрузку для свойства навигацииOfficeAssignment
. Это то же самое, что и в методеHttpGet
Edit
.Обновляет извлеченную сущность
Instructor
, используя значения из связывателя модели. Используемая перегрузка TryUpdateModel позволяет безопасно указать свойства, которые необходимо включить. Это защищает от чрезмерной передачи данных, как описано во втором руководстве.if (TryUpdateModel(instructorToUpdate, "", new string[] { "LastName", "FirstMidName", "HireDate", "OfficeAssignment" }))
Если расположение кабинета отсутствует, задает для свойства
Instructor.OfficeAssignment
значение NULL, что приводит к удалению связанной строки в таблицеOfficeAssignment
.if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment.Location)) { instructorToUpdate.OfficeAssignment = null; }
Сохраняет изменения в базу данных.
В Views\Instructor\Edit.cshtml после div
элементов поля "Дата найма" добавьте новое поле для редактирования расположения офиса:
<div class="editor-label">
@Html.LabelFor(model => model.OfficeAssignment.Location)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.OfficeAssignment.Location)
@Html.ValidationMessageFor(model => model.OfficeAssignment.Location)
</div>
Запустите страницу (перейдите на вкладку "Инструкторы" и нажмите кнопку "Изменить " для инструктора). Измените значение Office Location (Расположение кабинета) и нажмите кнопку Save (Сохранить).
Добавление заданий курса на страницу редактирования инструктора
Преподаватели могут вести любое число курсов. Теперь вы улучшите страницу редактирования преподавателя, добавив возможность изменять назначения курсов с помощью группы флажков, как показано на следующем снимке экрана:
Связь между Course
сущностями — Instructor
"многие ко многим", что означает, что у вас нет прямого доступа к таблице соединения. Вместо этого вы добавите и удалите сущности в свойство навигации и из него Instructor.Courses
.
Пользовательский интерфейс, позволяющий изменить назначенные для преподавателя курсы, представляет собой группу флажков. Отображается флажок для каждого курса в базе данных, а флажки для курсов, назначенных данному преподавателю, установлены. Пользователь может устанавливать и снимать флажки, изменяя назначения курсов. Если число курсов было гораздо больше, вы, вероятно, хотите использовать другой метод представления данных в представлении, но вы будете использовать тот же метод управления свойствами навигации для создания или удаления связей.
Чтобы предоставить данные в представлении для списка флажков, нужно использовать класс модели представления. Создайте AssignedCourseData.cs в папке ViewModels и замените существующий код следующим кодом:
namespace ContosoUniversity.ViewModels
{
public class AssignedCourseData
{
public int CourseID { get; set; }
public string Title { get; set; }
public bool Assigned { get; set; }
}
}
В InstructorController.cs замените HttpGet
Edit
метод следующим кодом. Изменения выделены.
public ActionResult Edit(int id)
{
Instructor instructor = db.Instructors
.Include(i => i.OfficeAssignment)
.Include(i => i.Courses)
.Where(i => i.InstructorID == id)
.Single();
PopulateAssignedCourseData(instructor);
return View(instructor);
}
private void PopulateAssignedCourseData(Instructor instructor)
{
var allCourses = db.Courses;
var instructorCourses = new HashSet<int>(instructor.Courses.Select(c => c.CourseID));
var viewModel = new List<AssignedCourseData>();
foreach (var course in allCourses)
{
viewModel.Add(new AssignedCourseData
{
CourseID = course.CourseID,
Title = course.Title,
Assigned = instructorCourses.Contains(course.CourseID)
});
}
ViewBag.Courses = viewModel;
}
Код добавляет безотложную загрузку для свойства навигации Courses
и вызывает новый метод PopulateAssignedCourseData
для предоставления сведений массиву флажков с помощью класса модели представления AssignedCourseData
.
Код в методе PopulateAssignedCourseData
считывает все сущности Course
, чтобы загрузить список курсов, используя класс модели представления. Для каждого курса код проверяет, существует ли этот курс в свойстве навигации Courses
преподавателя. Чтобы создать эффективный поиск при проверке того, назначен ли курс инструктору, курсы, назначенные инструктору, помещаются в коллекцию HashSet . Свойство Assigned
имеет значение true
для курсов, которым назначен инструктор. Представление будет использовать это свойство, чтобы определить, какие флажки нужно отображать как выбранные. Наконец, список передается в представление в свойстве ViewBag
.
Добавьте код, выполняемый, когда пользователь нажимает кнопку Save (Сохранить). Замените HttpPost
Edit
метод следующим кодом, который вызывает новый метод, который обновляет Courses
свойство навигации сущности Instructor
. Изменения выделены.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(int id, FormCollection formCollection, string[] selectedCourses)
{
var instructorToUpdate = db.Instructors
.Include(i => i.OfficeAssignment)
.Include(i => i.Courses)
.Where(i => i.InstructorID == id)
.Single();
if (TryUpdateModel(instructorToUpdate, "",
new string[] { "LastName", "FirstMidName", "HireDate", "OfficeAssignment" }))
{
try
{
if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment.Location))
{
instructorToUpdate.OfficeAssignment = null;
}
UpdateInstructorCourses(selectedCourses, instructorToUpdate);
db.Entry(instructorToUpdate).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
catch (DataException /* dex */)
{
//Log the error (uncomment dex variable name after DataException and add a line here to write a log.
ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists see your system administrator.");
}
}
PopulateAssignedCourseData(instructorToUpdate);
return View(instructorToUpdate);
}
private void UpdateInstructorCourses(string[] selectedCourses, Instructor instructorToUpdate)
{
if (selectedCourses == null)
{
instructorToUpdate.Courses = new List<Course>();
return;
}
var selectedCoursesHS = new HashSet<string>(selectedCourses);
var instructorCourses = new HashSet<int>
(instructorToUpdate.Courses.Select(c => c.CourseID));
foreach (var course in db.Courses)
{
if (selectedCoursesHS.Contains(course.CourseID.ToString()))
{
if (!instructorCourses.Contains(course.CourseID))
{
instructorToUpdate.Courses.Add(course);
}
}
else
{
if (instructorCourses.Contains(course.CourseID))
{
instructorToUpdate.Courses.Remove(course);
}
}
}
}
Так как представление не содержит коллекцию сущностей Course
, привязка модели не может автоматически обновлять Courses
свойство навигации. Вместо использования привязки модели для обновления свойства навигации Courses вы будете делать это в новом UpdateInstructorCourses
методе. Поэтому нужно исключить свойство Courses
из привязки модели. Для этого не требуется никаких изменений в коде, который вызывает TryUpdateModel , так как вы используете перегрузку безопасного списка и Courses
не входит в список включения.
Если флажки не были выбраны, код в UpdateInstructorCourses
инициализирует Courses
свойство навигации с пустой коллекцией:
if (selectedCourses == null)
{
instructorToUpdate.Courses = new List<Course>();
return;
}
После этого код в цикле проходит по всем курсам в базе данных и сравнивает каждый из них с теми, которые сейчас назначены преподавателю, в противоположность тем, которые были выбраны в представлении. Чтобы упростить эффективную подстановку, последние две коллекции хранятся в объектах HashSet
.
Если флажок для курса был установлен, но курс отсутствует в свойстве навигации Instructor.Courses
, этот курс добавляется в коллекцию в свойстве навигации.
if (selectedCoursesHS.Contains(course.CourseID.ToString()))
{
if (!instructorCourses.Contains(course.CourseID))
{
instructorToUpdate.Courses.Add(course);
}
}
Если флажок для курса не был установлен, но курс присутствует в свойстве навигации Instructor.Courses
, этот курс удаляется из свойства навигации.
else
{
if (instructorCourses.Contains(course.CourseID))
{
instructorToUpdate.Courses.Remove(course);
}
}
В Views\Instructor\Edit.cshtml добавьте поле Courses с массивом флажков, добавив следующий выделенный код сразу после div
элементов поля OfficeAssignment
:
@model ContosoUniversity.Models.Instructor
@{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
@Html.ValidationSummary(true)
<fieldset>
<legend>Instructor</legend>
@Html.HiddenFor(model => model.InstructorID)
<div class="editor-label">
@Html.LabelFor(model => model.LastName)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.LastName)
@Html.ValidationMessageFor(model => model.LastName)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.FirstMidName)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.FirstMidName)
@Html.ValidationMessageFor(model => model.FirstMidName)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.HireDate)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.HireDate)
@Html.ValidationMessageFor(model => model.HireDate)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.OfficeAssignment.Location)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.OfficeAssignment.Location)
@Html.ValidationMessageFor(model => model.OfficeAssignment.Location)
</div>
<div class="editor-field">
<table>
<tr>
@{
int cnt = 0;
List<ContosoUniversity.ViewModels.AssignedCourseData> courses = ViewBag.Courses;
foreach (var course in courses) {
if (cnt++ % 3 == 0) {
@: </tr> <tr>
}
@: <td>
<input type="checkbox"
name="selectedCourses"
value="@course.CourseID"
@(Html.Raw(course.Assigned ? "checked=\"checked\"" : "")) />
@course.CourseID @: @course.Title
@:</td>
}
@: </tr>
}
</table>
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
<div>
@Html.ActionLink("Back to List", "Index")
</div>
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}
Этот код создает таблицу HTML с тремя столбцами. Каждый столбец содержит флажок, за которым идет заголовок с номером и названием курса. Все флажки имеют то же имя ("selectedCourses"), которое сообщает привязщику модели, что они должны рассматриваться как группа. Атрибут value
каждого флажка имеет значение CourseID.
"Когда страница размещена", привязка модели передает массив контроллеру, состоящему из CourseID
значений только выбранных флажков.
Когда флажки изначально отображаются, те, которые предназначены для курсов, назначенных инструктору, имеют checked
атрибуты, которые выбирают их (отображает их).
После изменения заданий курса вы захотите проверить изменения, когда сайт возвращается на страницу Index
. Поэтому необходимо добавить столбец в таблицу на этой странице. В этом случае не нужно использовать ViewBag
объект, так как сведения, которые вы хотите отобразить, уже находится в Courses
свойстве Instructor
навигации сущности, которую вы передаете на страницу в качестве модели.
В Views\Instructor\Index.cshtml добавьте заголовок Courses сразу после заголовка Office , как показано в следующем примере:
<tr>
<th></th>
<th>Last Name</th>
<th>First Name</th>
<th>Hire Date</th>
<th>Office</th>
<th>Courses</th>
</tr>
Затем добавьте новую ячейку сведений сразу после ячейки сведений о расположении офиса:
@model ContosoUniversity.ViewModels.InstructorIndexData
@{
ViewBag.Title = "Instructors";
}
<h2>Instructors</h2>
<p>
@Html.ActionLink("Create New", "Create")
</p>
<table>
<tr>
<th></th>
<th>Last Name</th>
<th>First Name</th>
<th>Hire Date</th>
<th>Office</th>
<th>Courses</th>
</tr>
@foreach (var item in Model.Instructors)
{
string selectedRow = "";
if (item.InstructorID == ViewBag.InstructorID)
{
selectedRow = "selectedrow";
}
<tr class="@selectedRow" valign="top">
<td>
@Html.ActionLink("Select", "Index", new { id = item.InstructorID }) |
@Html.ActionLink("Edit", "Edit", new { id = item.InstructorID }) |
@Html.ActionLink("Details", "Details", new { id = item.InstructorID }) |
@Html.ActionLink("Delete", "Delete", new { id = item.InstructorID })
</td>
<td>
@item.LastName
</td>
<td>
@item.FirstMidName
</td>
<td>
@String.Format("{0:d}", item.HireDate)
</td>
<td>
@if (item.OfficeAssignment != null)
{
@item.OfficeAssignment.Location
}
</td>
<td>
@{
foreach (var course in item.Courses)
{
@course.CourseID @: @course.Title <br />
}
}
</td>
</tr>
}
</table>
@if (Model.Courses != null)
{
<h3>Courses Taught by Selected Instructor</h3>
<table>
<tr>
<th></th>
<th>ID</th>
<th>Title</th>
<th>Department</th>
</tr>
@foreach (var item in Model.Courses)
{
string selectedRow = "";
if (item.CourseID == ViewBag.CourseID)
{
selectedRow = "selectedrow";
}
<tr class="@selectedRow">
<td>
@Html.ActionLink("Select", "Index", new { courseID = item.CourseID })
</td>
<td>
@item.CourseID
</td>
<td>
@item.Title
</td>
<td>
@item.Department.Name
</td>
</tr>
}
</table>
}
@if (Model.Enrollments != null)
{
<h3>Students Enrolled in Selected Course</h3>
<table>
<tr>
<th>Name</th>
<th>Grade</th>
</tr>
@foreach (var item in Model.Enrollments)
{
<tr>
<td>
@item.Student.FullName
</td>
<td>
@Html.DisplayFor(modelItem => item.Grade)
</td>
</tr>
}
</table>
}
Запустите страницу "Индекс инструктора", чтобы просмотреть курсы, назначенные каждому инструктору:
Щелкните "Изменить" преподавателя, чтобы просмотреть страницу "Изменить ".
Измените некоторые задания курса и нажмите кнопку "Сохранить". Вносимые вами изменения отражаются на странице индекса.
Примечание. Подход, принятый для редактирования данных курса инструктора, хорошо работает, если существует ограниченное количество курсов. Для коллекций большего размера следовало бы применять другой пользовательский интерфейс и другой метод обновления.
Обновление метода Delete
Измените код в методе HttpPost Delete, чтобы запись назначения office (при наличии) удаляется при удалении инструктора:
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public ActionResult DeleteConfirmed(int id)
{
Instructor instructor = db.Instructors
.Include(i => i.OfficeAssignment)
.Where(i => i.InstructorID == id)
.Single();
instructor.OfficeAssignment = null;
db.Instructors.Remove(instructor);
db.SaveChanges();
return RedirectToAction("Index");
}
Если вы попытаетесь удалить инструктора, которому назначен отдел от имени администратора, вы получите ошибку целостности ссылок. В текущей версии этого руководства приведен дополнительный код, который автоматически удаляет инструктора из любого отдела, где инструктор назначается администратором.
Итоги
Теперь вы выполнили это введение в работу с связанными данными. До сих пор в этих руководствах вы выполнили полный спектр операций CRUD, но вы не рассмотрели проблемы параллелизма. В следующем руководстве представлена тема параллелизма, описание вариантов ее обработки и добавление обработки параллелизма в код CRUD, который вы уже написали для одного типа сущности.
Ссылки на другие ресурсы Entity Framework можно найти в конце последнего учебника в этой серии.