"
 
 
 
ASP.NET (snapshot 2017) Microsoft documentation and samples

Updating related data - EF Core with ASP.NET Core MVC tutorial (7 of 10)

By Tom Dykstra and Rick Anderson

The Contoso University sample web application demonstrates how to create ASP.NET Core MVC web applications using Entity Framework Core and Visual Studio. For information about the tutorial series, see the first tutorial in the series.

In the previous tutorial you displayed related data; in this tutorial you’ll update related data by updating foreign key fields and navigation properties.

The following illustrations show some of the pages that you’ll work with.

Course Edit page
Course Edit page
Instructor Edit page
Instructor Edit page

Customize the Create and Edit Pages for Courses

When a new course entity is created, it must have a relationship to an existing department. To facilitate this, the scaffolded code includes controller methods and Create and Edit views that include a drop-down list for selecting the department. The drop-down list sets the Course.DepartmentID foreign key property, and that’s all the Entity Framework needs in order to load the Department navigation property with the appropriate Department entity. You’ll use the scaffolded code, but change it slightly to add error handling and sort the drop-down list.

In CoursesController.cs, delete the four Create and Edit methods and replace them with the following code:

[!code-csharpMain]

   1:  //#define ScaffoldedCode
   2:  #define RevisedIndexMethod
   3:   
   4:  using System;
   5:  using System.Collections.Generic;
   6:  using System.Linq;
   7:  using System.Threading.Tasks;
   8:  using Microsoft.AspNetCore.Mvc;
   9:  using Microsoft.AspNetCore.Mvc.Rendering;
  10:  using Microsoft.EntityFrameworkCore;
  11:  using ContosoUniversity.Data;
  12:  using ContosoUniversity.Models;
  13:   
  14:  namespace ContosoUniversity.Controllers
  15:  {
  16:      public class CoursesController : Controller
  17:      {
  18:          private readonly SchoolContext _context;
  19:   
  20:          public CoursesController(SchoolContext context)
  21:          {
  22:              _context = context;    
  23:          }
  24:   
  25:          // GET: Courses
  26:  #if ScaffoldedCode
  27:          public async Task<IActionResult> Index()
  28:          {
  29:              var schoolContext = _context.Courses
  30:                  .Include(c => c.Department)
  31:                  .AsNoTracking();
  32:              return View(await schoolContext.ToListAsync());
  33:          }
  34:  #elif RevisedIndexMethod
  35:          #region snippet_RevisedIndexMethod
  36:          public async Task<IActionResult> Index()
  37:          {
  38:              var courses = _context.Courses
  39:                  .Include(c => c.Department)
  40:                  .AsNoTracking();
  41:              return View(await courses.ToListAsync());
  42:          }
  43:          #endregion
  44:  #endif
  45:          // GET: Courses/Details/5
  46:          #region snippet_Details
  47:          public async Task<IActionResult> Details(int? id)
  48:          {
  49:              if (id == null)
  50:              {
  51:                  return NotFound();
  52:              }
  53:   
  54:              var course = await _context.Courses
  55:                  .Include(c => c.Department)
  56:                  .AsNoTracking()
  57:                  .SingleOrDefaultAsync(m => m.CourseID == id);
  58:              if (course == null)
  59:              {
  60:                  return NotFound();
  61:              }
  62:   
  63:              return View(course);
  64:          }
  65:          #endregion
  66:   
  67:          // GET: Courses/Create
  68:          #region snippet_CreateGet
  69:          public IActionResult Create()
  70:          {
  71:              PopulateDepartmentsDropDownList();
  72:              return View();
  73:          }
  74:          #endregion
  75:          // POST: Courses/Create
  76:          #region snippet_CreatePost
  77:          [HttpPost]
  78:          [ValidateAntiForgeryToken]
  79:          public async Task<IActionResult> Create([Bind("CourseID,Credits,DepartmentID,Title")] Course course)
  80:          {
  81:              if (ModelState.IsValid)
  82:              {
  83:                  _context.Add(course);
  84:                  await _context.SaveChangesAsync();
  85:                  return RedirectToAction(nameof(Index));
  86:              }
  87:              PopulateDepartmentsDropDownList(course.DepartmentID);
  88:              return View(course);
  89:          }
  90:          #endregion
  91:   
  92:          // GET: Courses/Edit/5
  93:          #region snippet_EditGet
  94:          public async Task<IActionResult> Edit(int? id)
  95:          {
  96:              if (id == null)
  97:              {
  98:                  return NotFound();
  99:              }
 100:   
 101:              var course = await _context.Courses
 102:                  .AsNoTracking()
 103:                  .SingleOrDefaultAsync(m => m.CourseID == id);
 104:              if (course == null)
 105:              {
 106:                  return NotFound();
 107:              }
 108:              PopulateDepartmentsDropDownList(course.DepartmentID);
 109:              return View(course);
 110:          }
 111:          #endregion
 112:   
 113:   
 114:          // POST: Courses/Edit/5
 115:          #region snippet_EditPost
 116:          [HttpPost, ActionName("Edit")]
 117:          [ValidateAntiForgeryToken]
 118:          public async Task<IActionResult> EditPost(int? id)
 119:          {
 120:              if (id == null)
 121:              {
 122:                  return NotFound();
 123:              }
 124:   
 125:              var courseToUpdate = await _context.Courses
 126:                  .SingleOrDefaultAsync(c => c.CourseID == id);
 127:   
 128:              if (await TryUpdateModelAsync<Course>(courseToUpdate,
 129:                  "",
 130:                  c => c.Credits, c => c.DepartmentID, c => c.Title))
 131:              {
 132:                  try
 133:                  {
 134:                      await _context.SaveChangesAsync();
 135:                  }
 136:                  catch (DbUpdateException /* ex */)
 137:                  {
 138:                      //Log the error (uncomment ex variable name and write a log.)
 139:                      ModelState.AddModelError("", "Unable to save changes. " +
 140:                          "Try again, and if the problem persists, " +
 141:                          "see your system administrator.");
 142:                  }
 143:                  return RedirectToAction(nameof(Index));
 144:              }
 145:              PopulateDepartmentsDropDownList(courseToUpdate.DepartmentID);
 146:              return View(courseToUpdate);
 147:          }
 148:          #endregion
 149:   
 150:          #region snippet_Departments
 151:          private void PopulateDepartmentsDropDownList(object selectedDepartment = null)
 152:          {
 153:              var departmentsQuery = from d in _context.Departments
 154:                                     orderby d.Name
 155:                                     select d;
 156:              ViewBag.DepartmentID = new SelectList(departmentsQuery.AsNoTracking(), "DepartmentID", "Name", selectedDepartment);
 157:          }
 158:          #endregion
 159:   
 160:          // GET: Courses/Delete/5
 161:          #region snippet_DeleteGet
 162:          public async Task<IActionResult> Delete(int? id)
 163:          {
 164:              if (id == null)
 165:              {
 166:                  return NotFound();
 167:              }
 168:   
 169:              var course = await _context.Courses
 170:                  .Include(c => c.Department)
 171:                  .AsNoTracking()
 172:                  .SingleOrDefaultAsync(m => m.CourseID == id);
 173:              if (course == null)
 174:              {
 175:                  return NotFound();
 176:              }
 177:   
 178:              return View(course);
 179:          }
 180:          #endregion
 181:   
 182:          // POST: Courses/Delete/5
 183:          [HttpPost, ActionName("Delete")]
 184:          [ValidateAntiForgeryToken]
 185:          public async Task<IActionResult> DeleteConfirmed(int id)
 186:          {
 187:              var course = await _context.Courses
 188:                  .SingleOrDefaultAsync(m => m.CourseID == id);
 189:              if (course != null)
 190:              {
 191:                  _context.Courses.Remove(course);
 192:                  await _context.SaveChangesAsync();
 193:              }
 194:              return RedirectToAction(nameof(Index));
 195:          }
 196:   
 197:          #region snippet_UpdateGet
 198:          public IActionResult UpdateCourseCredits()
 199:          {
 200:              return View();
 201:          }
 202:          #endregion
 203:   
 204:          #region snippet_UpdatePost
 205:          [HttpPost]
 206:          public async Task<IActionResult> UpdateCourseCredits(int? multiplier)
 207:          {
 208:              if (multiplier != null)
 209:              {
 210:                  ViewData["RowsAffected"] = 
 211:                      await _context.Database.ExecuteSqlCommandAsync(
 212:                          "UPDATE Course SET Credits = Credits * {0}",
 213:                          parameters: multiplier);
 214:              }
 215:              return View();
 216:          }
 217:          #endregion
 218:      }
 219:  }

[!code-csharpMain]

   1:  //#define ScaffoldedCode
   2:  #define RevisedIndexMethod
   3:   
   4:  using System;
   5:  using System.Collections.Generic;
   6:  using System.Linq;
   7:  using System.Threading.Tasks;
   8:  using Microsoft.AspNetCore.Mvc;
   9:  using Microsoft.AspNetCore.Mvc.Rendering;
  10:  using Microsoft.EntityFrameworkCore;
  11:  using ContosoUniversity.Data;
  12:  using ContosoUniversity.Models;
  13:   
  14:  namespace ContosoUniversity.Controllers
  15:  {
  16:      public class CoursesController : Controller
  17:      {
  18:          private readonly SchoolContext _context;
  19:   
  20:          public CoursesController(SchoolContext context)
  21:          {
  22:              _context = context;    
  23:          }
  24:   
  25:          // GET: Courses
  26:  #if ScaffoldedCode
  27:          public async Task<IActionResult> Index()
  28:          {
  29:              var schoolContext = _context.Courses
  30:                  .Include(c => c.Department)
  31:                  .AsNoTracking();
  32:              return View(await schoolContext.ToListAsync());
  33:          }
  34:  #elif RevisedIndexMethod
  35:          #region snippet_RevisedIndexMethod
  36:          public async Task<IActionResult> Index()
  37:          {
  38:              var courses = _context.Courses
  39:                  .Include(c => c.Department)
  40:                  .AsNoTracking();
  41:              return View(await courses.ToListAsync());
  42:          }
  43:          #endregion
  44:  #endif
  45:          // GET: Courses/Details/5
  46:          #region snippet_Details
  47:          public async Task<IActionResult> Details(int? id)
  48:          {
  49:              if (id == null)
  50:              {
  51:                  return NotFound();
  52:              }
  53:   
  54:              var course = await _context.Courses
  55:                  .Include(c => c.Department)
  56:                  .AsNoTracking()
  57:                  .SingleOrDefaultAsync(m => m.CourseID == id);
  58:              if (course == null)
  59:              {
  60:                  return NotFound();
  61:              }
  62:   
  63:              return View(course);
  64:          }
  65:          #endregion
  66:   
  67:          // GET: Courses/Create
  68:          #region snippet_CreateGet
  69:          public IActionResult Create()
  70:          {
  71:              PopulateDepartmentsDropDownList();
  72:              return View();
  73:          }
  74:          #endregion
  75:          // POST: Courses/Create
  76:          #region snippet_CreatePost
  77:          [HttpPost]
  78:          [ValidateAntiForgeryToken]
  79:          public async Task<IActionResult> Create([Bind("CourseID,Credits,DepartmentID,Title")] Course course)
  80:          {
  81:              if (ModelState.IsValid)
  82:              {
  83:                  _context.Add(course);
  84:                  await _context.SaveChangesAsync();
  85:                  return RedirectToAction(nameof(Index));
  86:              }
  87:              PopulateDepartmentsDropDownList(course.DepartmentID);
  88:              return View(course);
  89:          }
  90:          #endregion
  91:   
  92:          // GET: Courses/Edit/5
  93:          #region snippet_EditGet
  94:          public async Task<IActionResult> Edit(int? id)
  95:          {
  96:              if (id == null)
  97:              {
  98:                  return NotFound();
  99:              }
 100:   
 101:              var course = await _context.Courses
 102:                  .AsNoTracking()
 103:                  .SingleOrDefaultAsync(m => m.CourseID == id);
 104:              if (course == null)
 105:              {
 106:                  return NotFound();
 107:              }
 108:              PopulateDepartmentsDropDownList(course.DepartmentID);
 109:              return View(course);
 110:          }
 111:          #endregion
 112:   
 113:   
 114:          // POST: Courses/Edit/5
 115:          #region snippet_EditPost
 116:          [HttpPost, ActionName("Edit")]
 117:          [ValidateAntiForgeryToken]
 118:          public async Task<IActionResult> EditPost(int? id)
 119:          {
 120:              if (id == null)
 121:              {
 122:                  return NotFound();
 123:              }
 124:   
 125:              var courseToUpdate = await _context.Courses
 126:                  .SingleOrDefaultAsync(c => c.CourseID == id);
 127:   
 128:              if (await TryUpdateModelAsync<Course>(courseToUpdate,
 129:                  "",
 130:                  c => c.Credits, c => c.DepartmentID, c => c.Title))
 131:              {
 132:                  try
 133:                  {
 134:                      await _context.SaveChangesAsync();
 135:                  }
 136:                  catch (DbUpdateException /* ex */)
 137:                  {
 138:                      //Log the error (uncomment ex variable name and write a log.)
 139:                      ModelState.AddModelError("", "Unable to save changes. " +
 140:                          "Try again, and if the problem persists, " +
 141:                          "see your system administrator.");
 142:                  }
 143:                  return RedirectToAction(nameof(Index));
 144:              }
 145:              PopulateDepartmentsDropDownList(courseToUpdate.DepartmentID);
 146:              return View(courseToUpdate);
 147:          }
 148:          #endregion
 149:   
 150:          #region snippet_Departments
 151:          private void PopulateDepartmentsDropDownList(object selectedDepartment = null)
 152:          {
 153:              var departmentsQuery = from d in _context.Departments
 154:                                     orderby d.Name
 155:                                     select d;
 156:              ViewBag.DepartmentID = new SelectList(departmentsQuery.AsNoTracking(), "DepartmentID", "Name", selectedDepartment);
 157:          }
 158:          #endregion
 159:   
 160:          // GET: Courses/Delete/5
 161:          #region snippet_DeleteGet
 162:          public async Task<IActionResult> Delete(int? id)
 163:          {
 164:              if (id == null)
 165:              {
 166:                  return NotFound();
 167:              }
 168:   
 169:              var course = await _context.Courses
 170:                  .Include(c => c.Department)
 171:                  .AsNoTracking()
 172:                  .SingleOrDefaultAsync(m => m.CourseID == id);
 173:              if (course == null)
 174:              {
 175:                  return NotFound();
 176:              }
 177:   
 178:              return View(course);
 179:          }
 180:          #endregion
 181:   
 182:          // POST: Courses/Delete/5
 183:          [HttpPost, ActionName("Delete")]
 184:          [ValidateAntiForgeryToken]
 185:          public async Task<IActionResult> DeleteConfirmed(int id)
 186:          {
 187:              var course = await _context.Courses
 188:                  .SingleOrDefaultAsync(m => m.CourseID == id);
 189:              if (course != null)
 190:              {
 191:                  _context.Courses.Remove(course);
 192:                  await _context.SaveChangesAsync();
 193:              }
 194:              return RedirectToAction(nameof(Index));
 195:          }
 196:   
 197:          #region snippet_UpdateGet
 198:          public IActionResult UpdateCourseCredits()
 199:          {
 200:              return View();
 201:          }
 202:          #endregion
 203:   
 204:          #region snippet_UpdatePost
 205:          [HttpPost]
 206:          public async Task<IActionResult> UpdateCourseCredits(int? multiplier)
 207:          {
 208:              if (multiplier != null)
 209:              {
 210:                  ViewData["RowsAffected"] = 
 211:                      await _context.Database.ExecuteSqlCommandAsync(
 212:                          "UPDATE Course SET Credits = Credits * {0}",
 213:                          parameters: multiplier);
 214:              }
 215:              return View();
 216:          }
 217:          #endregion
 218:      }
 219:  }

[!code-csharpMain]

   1:  //#define ScaffoldedCode
   2:  #define RevisedIndexMethod
   3:   
   4:  using System;
   5:  using System.Collections.Generic;
   6:  using System.Linq;
   7:  using System.Threading.Tasks;
   8:  using Microsoft.AspNetCore.Mvc;
   9:  using Microsoft.AspNetCore.Mvc.Rendering;
  10:  using Microsoft.EntityFrameworkCore;
  11:  using ContosoUniversity.Data;
  12:  using ContosoUniversity.Models;
  13:   
  14:  namespace ContosoUniversity.Controllers
  15:  {
  16:      public class CoursesController : Controller
  17:      {
  18:          private readonly SchoolContext _context;
  19:   
  20:          public CoursesController(SchoolContext context)
  21:          {
  22:              _context = context;    
  23:          }
  24:   
  25:          // GET: Courses
  26:  #if ScaffoldedCode
  27:          public async Task<IActionResult> Index()
  28:          {
  29:              var schoolContext = _context.Courses
  30:                  .Include(c => c.Department)
  31:                  .AsNoTracking();
  32:              return View(await schoolContext.ToListAsync());
  33:          }
  34:  #elif RevisedIndexMethod
  35:          #region snippet_RevisedIndexMethod
  36:          public async Task<IActionResult> Index()
  37:          {
  38:              var courses = _context.Courses
  39:                  .Include(c => c.Department)
  40:                  .AsNoTracking();
  41:              return View(await courses.ToListAsync());
  42:          }
  43:          #endregion
  44:  #endif
  45:          // GET: Courses/Details/5
  46:          #region snippet_Details
  47:          public async Task<IActionResult> Details(int? id)
  48:          {
  49:              if (id == null)
  50:              {
  51:                  return NotFound();
  52:              }
  53:   
  54:              var course = await _context.Courses
  55:                  .Include(c => c.Department)
  56:                  .AsNoTracking()
  57:                  .SingleOrDefaultAsync(m => m.CourseID == id);
  58:              if (course == null)
  59:              {
  60:                  return NotFound();
  61:              }
  62:   
  63:              return View(course);
  64:          }
  65:          #endregion
  66:   
  67:          // GET: Courses/Create
  68:          #region snippet_CreateGet
  69:          public IActionResult Create()
  70:          {
  71:              PopulateDepartmentsDropDownList();
  72:              return View();
  73:          }
  74:          #endregion
  75:          // POST: Courses/Create
  76:          #region snippet_CreatePost
  77:          [HttpPost]
  78:          [ValidateAntiForgeryToken]
  79:          public async Task<IActionResult> Create([Bind("CourseID,Credits,DepartmentID,Title")] Course course)
  80:          {
  81:              if (ModelState.IsValid)
  82:              {
  83:                  _context.Add(course);
  84:                  await _context.SaveChangesAsync();
  85:                  return RedirectToAction(nameof(Index));
  86:              }
  87:              PopulateDepartmentsDropDownList(course.DepartmentID);
  88:              return View(course);
  89:          }
  90:          #endregion
  91:   
  92:          // GET: Courses/Edit/5
  93:          #region snippet_EditGet
  94:          public async Task<IActionResult> Edit(int? id)
  95:          {
  96:              if (id == null)
  97:              {
  98:                  return NotFound();
  99:              }
 100:   
 101:              var course = await _context.Courses
 102:                  .AsNoTracking()
 103:                  .SingleOrDefaultAsync(m => m.CourseID == id);
 104:              if (course == null)
 105:              {
 106:                  return NotFound();
 107:              }
 108:              PopulateDepartmentsDropDownList(course.DepartmentID);
 109:              return View(course);
 110:          }
 111:          #endregion
 112:   
 113:   
 114:          // POST: Courses/Edit/5
 115:          #region snippet_EditPost
 116:          [HttpPost, ActionName("Edit")]
 117:          [ValidateAntiForgeryToken]
 118:          public async Task<IActionResult> EditPost(int? id)
 119:          {
 120:              if (id == null)
 121:              {
 122:                  return NotFound();
 123:              }
 124:   
 125:              var courseToUpdate = await _context.Courses
 126:                  .SingleOrDefaultAsync(c => c.CourseID == id);
 127:   
 128:              if (await TryUpdateModelAsync<Course>(courseToUpdate,
 129:                  "",
 130:                  c => c.Credits, c => c.DepartmentID, c => c.Title))
 131:              {
 132:                  try
 133:                  {
 134:                      await _context.SaveChangesAsync();
 135:                  }
 136:                  catch (DbUpdateException /* ex */)
 137:                  {
 138:                      //Log the error (uncomment ex variable name and write a log.)
 139:                      ModelState.AddModelError("", "Unable to save changes. " +
 140:                          "Try again, and if the problem persists, " +
 141:                          "see your system administrator.");
 142:                  }
 143:                  return RedirectToAction(nameof(Index));
 144:              }
 145:              PopulateDepartmentsDropDownList(courseToUpdate.DepartmentID);
 146:              return View(courseToUpdate);
 147:          }
 148:          #endregion
 149:   
 150:          #region snippet_Departments
 151:          private void PopulateDepartmentsDropDownList(object selectedDepartment = null)
 152:          {
 153:              var departmentsQuery = from d in _context.Departments
 154:                                     orderby d.Name
 155:                                     select d;
 156:              ViewBag.DepartmentID = new SelectList(departmentsQuery.AsNoTracking(), "DepartmentID", "Name", selectedDepartment);
 157:          }
 158:          #endregion
 159:   
 160:          // GET: Courses/Delete/5
 161:          #region snippet_DeleteGet
 162:          public async Task<IActionResult> Delete(int? id)
 163:          {
 164:              if (id == null)
 165:              {
 166:                  return NotFound();
 167:              }
 168:   
 169:              var course = await _context.Courses
 170:                  .Include(c => c.Department)
 171:                  .AsNoTracking()
 172:                  .SingleOrDefaultAsync(m => m.CourseID == id);
 173:              if (course == null)
 174:              {
 175:                  return NotFound();
 176:              }
 177:   
 178:              return View(course);
 179:          }
 180:          #endregion
 181:   
 182:          // POST: Courses/Delete/5
 183:          [HttpPost, ActionName("Delete")]
 184:          [ValidateAntiForgeryToken]
 185:          public async Task<IActionResult> DeleteConfirmed(int id)
 186:          {
 187:              var course = await _context.Courses
 188:                  .SingleOrDefaultAsync(m => m.CourseID == id);
 189:              if (course != null)
 190:              {
 191:                  _context.Courses.Remove(course);
 192:                  await _context.SaveChangesAsync();
 193:              }
 194:              return RedirectToAction(nameof(Index));
 195:          }
 196:   
 197:          #region snippet_UpdateGet
 198:          public IActionResult UpdateCourseCredits()
 199:          {
 200:              return View();
 201:          }
 202:          #endregion
 203:   
 204:          #region snippet_UpdatePost
 205:          [HttpPost]
 206:          public async Task<IActionResult> UpdateCourseCredits(int? multiplier)
 207:          {
 208:              if (multiplier != null)
 209:              {
 210:                  ViewData["RowsAffected"] = 
 211:                      await _context.Database.ExecuteSqlCommandAsync(
 212:                          "UPDATE Course SET Credits = Credits * {0}",
 213:                          parameters: multiplier);
 214:              }
 215:              return View();
 216:          }
 217:          #endregion
 218:      }
 219:  }

[!code-csharpMain]

   1:  //#define ScaffoldedCode
   2:  #define RevisedIndexMethod
   3:   
   4:  using System;
   5:  using System.Collections.Generic;
   6:  using System.Linq;
   7:  using System.Threading.Tasks;
   8:  using Microsoft.AspNetCore.Mvc;
   9:  using Microsoft.AspNetCore.Mvc.Rendering;
  10:  using Microsoft.EntityFrameworkCore;
  11:  using ContosoUniversity.Data;
  12:  using ContosoUniversity.Models;
  13:   
  14:  namespace ContosoUniversity.Controllers
  15:  {
  16:      public class CoursesController : Controller
  17:      {
  18:          private readonly SchoolContext _context;
  19:   
  20:          public CoursesController(SchoolContext context)
  21:          {
  22:              _context = context;    
  23:          }
  24:   
  25:          // GET: Courses
  26:  #if ScaffoldedCode
  27:          public async Task<IActionResult> Index()
  28:          {
  29:              var schoolContext = _context.Courses
  30:                  .Include(c => c.Department)
  31:                  .AsNoTracking();
  32:              return View(await schoolContext.ToListAsync());
  33:          }
  34:  #elif RevisedIndexMethod
  35:          #region snippet_RevisedIndexMethod
  36:          public async Task<IActionResult> Index()
  37:          {
  38:              var courses = _context.Courses
  39:                  .Include(c => c.Department)
  40:                  .AsNoTracking();
  41:              return View(await courses.ToListAsync());
  42:          }
  43:          #endregion
  44:  #endif
  45:          // GET: Courses/Details/5
  46:          #region snippet_Details
  47:          public async Task<IActionResult> Details(int? id)
  48:          {
  49:              if (id == null)
  50:              {
  51:                  return NotFound();
  52:              }
  53:   
  54:              var course = await _context.Courses
  55:                  .Include(c => c.Department)
  56:                  .AsNoTracking()
  57:                  .SingleOrDefaultAsync(m => m.CourseID == id);
  58:              if (course == null)
  59:              {
  60:                  return NotFound();
  61:              }
  62:   
  63:              return View(course);
  64:          }
  65:          #endregion
  66:   
  67:          // GET: Courses/Create
  68:          #region snippet_CreateGet
  69:          public IActionResult Create()
  70:          {
  71:              PopulateDepartmentsDropDownList();
  72:              return View();
  73:          }
  74:          #endregion
  75:          // POST: Courses/Create
  76:          #region snippet_CreatePost
  77:          [HttpPost]
  78:          [ValidateAntiForgeryToken]
  79:          public async Task<IActionResult> Create([Bind("CourseID,Credits,DepartmentID,Title")] Course course)
  80:          {
  81:              if (ModelState.IsValid)
  82:              {
  83:                  _context.Add(course);
  84:                  await _context.SaveChangesAsync();
  85:                  return RedirectToAction(nameof(Index));
  86:              }
  87:              PopulateDepartmentsDropDownList(course.DepartmentID);
  88:              return View(course);
  89:          }
  90:          #endregion
  91:   
  92:          // GET: Courses/Edit/5
  93:          #region snippet_EditGet
  94:          public async Task<IActionResult> Edit(int? id)
  95:          {
  96:              if (id == null)
  97:              {
  98:                  return NotFound();
  99:              }
 100:   
 101:              var course = await _context.Courses
 102:                  .AsNoTracking()
 103:                  .SingleOrDefaultAsync(m => m.CourseID == id);
 104:              if (course == null)
 105:              {
 106:                  return NotFound();
 107:              }
 108:              PopulateDepartmentsDropDownList(course.DepartmentID);
 109:              return View(course);
 110:          }
 111:          #endregion
 112:   
 113:   
 114:          // POST: Courses/Edit/5
 115:          #region snippet_EditPost
 116:          [HttpPost, ActionName("Edit")]
 117:          [ValidateAntiForgeryToken]
 118:          public async Task<IActionResult> EditPost(int? id)
 119:          {
 120:              if (id == null)
 121:              {
 122:                  return NotFound();
 123:              }
 124:   
 125:              var courseToUpdate = await _context.Courses
 126:                  .SingleOrDefaultAsync(c => c.CourseID == id);
 127:   
 128:              if (await TryUpdateModelAsync<Course>(courseToUpdate,
 129:                  "",
 130:                  c => c.Credits, c => c.DepartmentID, c => c.Title))
 131:              {
 132:                  try
 133:                  {
 134:                      await _context.SaveChangesAsync();
 135:                  }
 136:                  catch (DbUpdateException /* ex */)
 137:                  {
 138:                      //Log the error (uncomment ex variable name and write a log.)
 139:                      ModelState.AddModelError("", "Unable to save changes. " +
 140:                          "Try again, and if the problem persists, " +
 141:                          "see your system administrator.");
 142:                  }
 143:                  return RedirectToAction(nameof(Index));
 144:              }
 145:              PopulateDepartmentsDropDownList(courseToUpdate.DepartmentID);
 146:              return View(courseToUpdate);
 147:          }
 148:          #endregion
 149:   
 150:          #region snippet_Departments
 151:          private void PopulateDepartmentsDropDownList(object selectedDepartment = null)
 152:          {
 153:              var departmentsQuery = from d in _context.Departments
 154:                                     orderby d.Name
 155:                                     select d;
 156:              ViewBag.DepartmentID = new SelectList(departmentsQuery.AsNoTracking(), "DepartmentID", "Name", selectedDepartment);
 157:          }
 158:          #endregion
 159:   
 160:          // GET: Courses/Delete/5
 161:          #region snippet_DeleteGet
 162:          public async Task<IActionResult> Delete(int? id)
 163:          {
 164:              if (id == null)
 165:              {
 166:                  return NotFound();
 167:              }
 168:   
 169:              var course = await _context.Courses
 170:                  .Include(c => c.Department)
 171:                  .AsNoTracking()
 172:                  .SingleOrDefaultAsync(m => m.CourseID == id);
 173:              if (course == null)
 174:              {
 175:                  return NotFound();
 176:              }
 177:   
 178:              return View(course);
 179:          }
 180:          #endregion
 181:   
 182:          // POST: Courses/Delete/5
 183:          [HttpPost, ActionName("Delete")]
 184:          [ValidateAntiForgeryToken]
 185:          public async Task<IActionResult> DeleteConfirmed(int id)
 186:          {
 187:              var course = await _context.Courses
 188:                  .SingleOrDefaultAsync(m => m.CourseID == id);
 189:              if (course != null)
 190:              {
 191:                  _context.Courses.Remove(course);
 192:                  await _context.SaveChangesAsync();
 193:              }
 194:              return RedirectToAction(nameof(Index));
 195:          }
 196:   
 197:          #region snippet_UpdateGet
 198:          public IActionResult UpdateCourseCredits()
 199:          {
 200:              return View();
 201:          }
 202:          #endregion
 203:   
 204:          #region snippet_UpdatePost
 205:          [HttpPost]
 206:          public async Task<IActionResult> UpdateCourseCredits(int? multiplier)
 207:          {
 208:              if (multiplier != null)
 209:              {
 210:                  ViewData["RowsAffected"] = 
 211:                      await _context.Database.ExecuteSqlCommandAsync(
 212:                          "UPDATE Course SET Credits = Credits * {0}",
 213:                          parameters: multiplier);
 214:              }
 215:              return View();
 216:          }
 217:          #endregion
 218:      }
 219:  }

After the Edit HttpPost method, create a new method that loads department info for the drop-down list.

[!code-csharpMain]

   1:  //#define ScaffoldedCode
   2:  #define RevisedIndexMethod
   3:   
   4:  using System;
   5:  using System.Collections.Generic;
   6:  using System.Linq;
   7:  using System.Threading.Tasks;
   8:  using Microsoft.AspNetCore.Mvc;
   9:  using Microsoft.AspNetCore.Mvc.Rendering;
  10:  using Microsoft.EntityFrameworkCore;
  11:  using ContosoUniversity.Data;
  12:  using ContosoUniversity.Models;
  13:   
  14:  namespace ContosoUniversity.Controllers
  15:  {
  16:      public class CoursesController : Controller
  17:      {
  18:          private readonly SchoolContext _context;
  19:   
  20:          public CoursesController(SchoolContext context)
  21:          {
  22:              _context = context;    
  23:          }
  24:   
  25:          // GET: Courses
  26:  #if ScaffoldedCode
  27:          public async Task<IActionResult> Index()
  28:          {
  29:              var schoolContext = _context.Courses
  30:                  .Include(c => c.Department)
  31:                  .AsNoTracking();
  32:              return View(await schoolContext.ToListAsync());
  33:          }
  34:  #elif RevisedIndexMethod
  35:          #region snippet_RevisedIndexMethod
  36:          public async Task<IActionResult> Index()
  37:          {
  38:              var courses = _context.Courses
  39:                  .Include(c => c.Department)
  40:                  .AsNoTracking();
  41:              return View(await courses.ToListAsync());
  42:          }
  43:          #endregion
  44:  #endif
  45:          // GET: Courses/Details/5
  46:          #region snippet_Details
  47:          public async Task<IActionResult> Details(int? id)
  48:          {
  49:              if (id == null)
  50:              {
  51:                  return NotFound();
  52:              }
  53:   
  54:              var course = await _context.Courses
  55:                  .Include(c => c.Department)
  56:                  .AsNoTracking()
  57:                  .SingleOrDefaultAsync(m => m.CourseID == id);
  58:              if (course == null)
  59:              {
  60:                  return NotFound();
  61:              }
  62:   
  63:              return View(course);
  64:          }
  65:          #endregion
  66:   
  67:          // GET: Courses/Create
  68:          #region snippet_CreateGet
  69:          public IActionResult Create()
  70:          {
  71:              PopulateDepartmentsDropDownList();
  72:              return View();
  73:          }
  74:          #endregion
  75:          // POST: Courses/Create
  76:          #region snippet_CreatePost
  77:          [HttpPost]
  78:          [ValidateAntiForgeryToken]
  79:          public async Task<IActionResult> Create([Bind("CourseID,Credits,DepartmentID,Title")] Course course)
  80:          {
  81:              if (ModelState.IsValid)
  82:              {
  83:                  _context.Add(course);
  84:                  await _context.SaveChangesAsync();
  85:                  return RedirectToAction(nameof(Index));
  86:              }
  87:              PopulateDepartmentsDropDownList(course.DepartmentID);
  88:              return View(course);
  89:          }
  90:          #endregion
  91:   
  92:          // GET: Courses/Edit/5
  93:          #region snippet_EditGet
  94:          public async Task<IActionResult> Edit(int? id)
  95:          {
  96:              if (id == null)
  97:              {
  98:                  return NotFound();
  99:              }
 100:   
 101:              var course = await _context.Courses
 102:                  .AsNoTracking()
 103:                  .SingleOrDefaultAsync(m => m.CourseID == id);
 104:              if (course == null)
 105:              {
 106:                  return NotFound();
 107:              }
 108:              PopulateDepartmentsDropDownList(course.DepartmentID);
 109:              return View(course);
 110:          }
 111:          #endregion
 112:   
 113:   
 114:          // POST: Courses/Edit/5
 115:          #region snippet_EditPost
 116:          [HttpPost, ActionName("Edit")]
 117:          [ValidateAntiForgeryToken]
 118:          public async Task<IActionResult> EditPost(int? id)
 119:          {
 120:              if (id == null)
 121:              {
 122:                  return NotFound();
 123:              }
 124:   
 125:              var courseToUpdate = await _context.Courses
 126:                  .SingleOrDefaultAsync(c => c.CourseID == id);
 127:   
 128:              if (await TryUpdateModelAsync<Course>(courseToUpdate,
 129:                  "",
 130:                  c => c.Credits, c => c.DepartmentID, c => c.Title))
 131:              {
 132:                  try
 133:                  {
 134:                      await _context.SaveChangesAsync();
 135:                  }
 136:                  catch (DbUpdateException /* ex */)
 137:                  {
 138:                      //Log the error (uncomment ex variable name and write a log.)
 139:                      ModelState.AddModelError("", "Unable to save changes. " +
 140:                          "Try again, and if the problem persists, " +
 141:                          "see your system administrator.");
 142:                  }
 143:                  return RedirectToAction(nameof(Index));
 144:              }
 145:              PopulateDepartmentsDropDownList(courseToUpdate.DepartmentID);
 146:              return View(courseToUpdate);
 147:          }
 148:          #endregion
 149:   
 150:          #region snippet_Departments
 151:          private void PopulateDepartmentsDropDownList(object selectedDepartment = null)
 152:          {
 153:              var departmentsQuery = from d in _context.Departments
 154:                                     orderby d.Name
 155:                                     select d;
 156:              ViewBag.DepartmentID = new SelectList(departmentsQuery.AsNoTracking(), "DepartmentID", "Name", selectedDepartment);
 157:          }
 158:          #endregion
 159:   
 160:          // GET: Courses/Delete/5
 161:          #region snippet_DeleteGet
 162:          public async Task<IActionResult> Delete(int? id)
 163:          {
 164:              if (id == null)
 165:              {
 166:                  return NotFound();
 167:              }
 168:   
 169:              var course = await _context.Courses
 170:                  .Include(c => c.Department)
 171:                  .AsNoTracking()
 172:                  .SingleOrDefaultAsync(m => m.CourseID == id);
 173:              if (course == null)
 174:              {
 175:                  return NotFound();
 176:              }
 177:   
 178:              return View(course);
 179:          }
 180:          #endregion
 181:   
 182:          // POST: Courses/Delete/5
 183:          [HttpPost, ActionName("Delete")]
 184:          [ValidateAntiForgeryToken]
 185:          public async Task<IActionResult> DeleteConfirmed(int id)
 186:          {
 187:              var course = await _context.Courses
 188:                  .SingleOrDefaultAsync(m => m.CourseID == id);
 189:              if (course != null)
 190:              {
 191:                  _context.Courses.Remove(course);
 192:                  await _context.SaveChangesAsync();
 193:              }
 194:              return RedirectToAction(nameof(Index));
 195:          }
 196:   
 197:          #region snippet_UpdateGet
 198:          public IActionResult UpdateCourseCredits()
 199:          {
 200:              return View();
 201:          }
 202:          #endregion
 203:   
 204:          #region snippet_UpdatePost
 205:          [HttpPost]
 206:          public async Task<IActionResult> UpdateCourseCredits(int? multiplier)
 207:          {
 208:              if (multiplier != null)
 209:              {
 210:                  ViewData["RowsAffected"] = 
 211:                      await _context.Database.ExecuteSqlCommandAsync(
 212:                          "UPDATE Course SET Credits = Credits * {0}",
 213:                          parameters: multiplier);
 214:              }
 215:              return View();
 216:          }
 217:          #endregion
 218:      }
 219:  }

The PopulateDepartmentsDropDownList method gets a list of all departments sorted by name, creates a SelectList collection for a drop-down list, and passes the collection to the view in ViewBag. The method accepts the optional selectedDepartment parameter that allows the calling code to specify the item that will be selected when the drop-down list is rendered. The view will pass the name “DepartmentID” to the <select> tag helper, and the helper then knows to look in the ViewBag object for a SelectList named “DepartmentID”.

The HttpGet Create method calls the PopulateDepartmentsDropDownList method without setting the selected item, because for a new course the department is not established yet:

[!code-csharpMain]

   1:  //#define ScaffoldedCode
   2:  #define RevisedIndexMethod
   3:   
   4:  using System;
   5:  using System.Collections.Generic;
   6:  using System.Linq;
   7:  using System.Threading.Tasks;
   8:  using Microsoft.AspNetCore.Mvc;
   9:  using Microsoft.AspNetCore.Mvc.Rendering;
  10:  using Microsoft.EntityFrameworkCore;
  11:  using ContosoUniversity.Data;
  12:  using ContosoUniversity.Models;
  13:   
  14:  namespace ContosoUniversity.Controllers
  15:  {
  16:      public class CoursesController : Controller
  17:      {
  18:          private readonly SchoolContext _context;
  19:   
  20:          public CoursesController(SchoolContext context)
  21:          {
  22:              _context = context;    
  23:          }
  24:   
  25:          // GET: Courses
  26:  #if ScaffoldedCode
  27:          public async Task<IActionResult> Index()
  28:          {
  29:              var schoolContext = _context.Courses
  30:                  .Include(c => c.Department)
  31:                  .AsNoTracking();
  32:              return View(await schoolContext.ToListAsync());
  33:          }
  34:  #elif RevisedIndexMethod
  35:          #region snippet_RevisedIndexMethod
  36:          public async Task<IActionResult> Index()
  37:          {
  38:              var courses = _context.Courses
  39:                  .Include(c => c.Department)
  40:                  .AsNoTracking();
  41:              return View(await courses.ToListAsync());
  42:          }
  43:          #endregion
  44:  #endif
  45:          // GET: Courses/Details/5
  46:          #region snippet_Details
  47:          public async Task<IActionResult> Details(int? id)
  48:          {
  49:              if (id == null)
  50:              {
  51:                  return NotFound();
  52:              }
  53:   
  54:              var course = await _context.Courses
  55:                  .Include(c => c.Department)
  56:                  .AsNoTracking()
  57:                  .SingleOrDefaultAsync(m => m.CourseID == id);
  58:              if (course == null)
  59:              {
  60:                  return NotFound();
  61:              }
  62:   
  63:              return View(course);
  64:          }
  65:          #endregion
  66:   
  67:          // GET: Courses/Create
  68:          #region snippet_CreateGet
  69:          public IActionResult Create()
  70:          {
  71:              PopulateDepartmentsDropDownList();
  72:              return View();
  73:          }
  74:          #endregion
  75:          // POST: Courses/Create
  76:          #region snippet_CreatePost
  77:          [HttpPost]
  78:          [ValidateAntiForgeryToken]
  79:          public async Task<IActionResult> Create([Bind("CourseID,Credits,DepartmentID,Title")] Course course)
  80:          {
  81:              if (ModelState.IsValid)
  82:              {
  83:                  _context.Add(course);
  84:                  await _context.SaveChangesAsync();
  85:                  return RedirectToAction(nameof(Index));
  86:              }
  87:              PopulateDepartmentsDropDownList(course.DepartmentID);
  88:              return View(course);
  89:          }
  90:          #endregion
  91:   
  92:          // GET: Courses/Edit/5
  93:          #region snippet_EditGet
  94:          public async Task<IActionResult> Edit(int? id)
  95:          {
  96:              if (id == null)
  97:              {
  98:                  return NotFound();
  99:              }
 100:   
 101:              var course = await _context.Courses
 102:                  .AsNoTracking()
 103:                  .SingleOrDefaultAsync(m => m.CourseID == id);
 104:              if (course == null)
 105:              {
 106:                  return NotFound();
 107:              }
 108:              PopulateDepartmentsDropDownList(course.DepartmentID);
 109:              return View(course);
 110:          }
 111:          #endregion
 112:   
 113:   
 114:          // POST: Courses/Edit/5
 115:          #region snippet_EditPost
 116:          [HttpPost, ActionName("Edit")]
 117:          [ValidateAntiForgeryToken]
 118:          public async Task<IActionResult> EditPost(int? id)
 119:          {
 120:              if (id == null)
 121:              {
 122:                  return NotFound();
 123:              }
 124:   
 125:              var courseToUpdate = await _context.Courses
 126:                  .SingleOrDefaultAsync(c => c.CourseID == id);
 127:   
 128:              if (await TryUpdateModelAsync<Course>(courseToUpdate,
 129:                  "",
 130:                  c => c.Credits, c => c.DepartmentID, c => c.Title))
 131:              {
 132:                  try
 133:                  {
 134:                      await _context.SaveChangesAsync();
 135:                  }
 136:                  catch (DbUpdateException /* ex */)
 137:                  {
 138:                      //Log the error (uncomment ex variable name and write a log.)
 139:                      ModelState.AddModelError("", "Unable to save changes. " +
 140:                          "Try again, and if the problem persists, " +
 141:                          "see your system administrator.");
 142:                  }
 143:                  return RedirectToAction(nameof(Index));
 144:              }
 145:              PopulateDepartmentsDropDownList(courseToUpdate.DepartmentID);
 146:              return View(courseToUpdate);
 147:          }
 148:          #endregion
 149:   
 150:          #region snippet_Departments
 151:          private void PopulateDepartmentsDropDownList(object selectedDepartment = null)
 152:          {
 153:              var departmentsQuery = from d in _context.Departments
 154:                                     orderby d.Name
 155:                                     select d;
 156:              ViewBag.DepartmentID = new SelectList(departmentsQuery.AsNoTracking(), "DepartmentID", "Name", selectedDepartment);
 157:          }
 158:          #endregion
 159:   
 160:          // GET: Courses/Delete/5
 161:          #region snippet_DeleteGet
 162:          public async Task<IActionResult> Delete(int? id)
 163:          {
 164:              if (id == null)
 165:              {
 166:                  return NotFound();
 167:              }
 168:   
 169:              var course = await _context.Courses
 170:                  .Include(c => c.Department)
 171:                  .AsNoTracking()
 172:                  .SingleOrDefaultAsync(m => m.CourseID == id);
 173:              if (course == null)
 174:              {
 175:                  return NotFound();
 176:              }
 177:   
 178:              return View(course);
 179:          }
 180:          #endregion
 181:   
 182:          // POST: Courses/Delete/5
 183:          [HttpPost, ActionName("Delete")]
 184:          [ValidateAntiForgeryToken]
 185:          public async Task<IActionResult> DeleteConfirmed(int id)
 186:          {
 187:              var course = await _context.Courses
 188:                  .SingleOrDefaultAsync(m => m.CourseID == id);
 189:              if (course != null)
 190:              {
 191:                  _context.Courses.Remove(course);
 192:                  await _context.SaveChangesAsync();
 193:              }
 194:              return RedirectToAction(nameof(Index));
 195:          }
 196:   
 197:          #region snippet_UpdateGet
 198:          public IActionResult UpdateCourseCredits()
 199:          {
 200:              return View();
 201:          }
 202:          #endregion
 203:   
 204:          #region snippet_UpdatePost
 205:          [HttpPost]
 206:          public async Task<IActionResult> UpdateCourseCredits(int? multiplier)
 207:          {
 208:              if (multiplier != null)
 209:              {
 210:                  ViewData["RowsAffected"] = 
 211:                      await _context.Database.ExecuteSqlCommandAsync(
 212:                          "UPDATE Course SET Credits = Credits * {0}",
 213:                          parameters: multiplier);
 214:              }
 215:              return View();
 216:          }
 217:          #endregion
 218:      }
 219:  }

The HttpGet Edit method sets the selected item, based on the ID of the department that is already assigned to the course being edited:

[!code-csharpMain]

   1:  //#define ScaffoldedCode
   2:  #define RevisedIndexMethod
   3:   
   4:  using System;
   5:  using System.Collections.Generic;
   6:  using System.Linq;
   7:  using System.Threading.Tasks;
   8:  using Microsoft.AspNetCore.Mvc;
   9:  using Microsoft.AspNetCore.Mvc.Rendering;
  10:  using Microsoft.EntityFrameworkCore;
  11:  using ContosoUniversity.Data;
  12:  using ContosoUniversity.Models;
  13:   
  14:  namespace ContosoUniversity.Controllers
  15:  {
  16:      public class CoursesController : Controller
  17:      {
  18:          private readonly SchoolContext _context;
  19:   
  20:          public CoursesController(SchoolContext context)
  21:          {
  22:              _context = context;    
  23:          }
  24:   
  25:          // GET: Courses
  26:  #if ScaffoldedCode
  27:          public async Task<IActionResult> Index()
  28:          {
  29:              var schoolContext = _context.Courses
  30:                  .Include(c => c.Department)
  31:                  .AsNoTracking();
  32:              return View(await schoolContext.ToListAsync());
  33:          }
  34:  #elif RevisedIndexMethod
  35:          #region snippet_RevisedIndexMethod
  36:          public async Task<IActionResult> Index()
  37:          {
  38:              var courses = _context.Courses
  39:                  .Include(c => c.Department)
  40:                  .AsNoTracking();
  41:              return View(await courses.ToListAsync());
  42:          }
  43:          #endregion
  44:  #endif
  45:          // GET: Courses/Details/5
  46:          #region snippet_Details
  47:          public async Task<IActionResult> Details(int? id)
  48:          {
  49:              if (id == null)
  50:              {
  51:                  return NotFound();
  52:              }
  53:   
  54:              var course = await _context.Courses
  55:                  .Include(c => c.Department)
  56:                  .AsNoTracking()
  57:                  .SingleOrDefaultAsync(m => m.CourseID == id);
  58:              if (course == null)
  59:              {
  60:                  return NotFound();
  61:              }
  62:   
  63:              return View(course);
  64:          }
  65:          #endregion
  66:   
  67:          // GET: Courses/Create
  68:          #region snippet_CreateGet
  69:          public IActionResult Create()
  70:          {
  71:              PopulateDepartmentsDropDownList();
  72:              return View();
  73:          }
  74:          #endregion
  75:          // POST: Courses/Create
  76:          #region snippet_CreatePost
  77:          [HttpPost]
  78:          [ValidateAntiForgeryToken]
  79:          public async Task<IActionResult> Create([Bind("CourseID,Credits,DepartmentID,Title")] Course course)
  80:          {
  81:              if (ModelState.IsValid)
  82:              {
  83:                  _context.Add(course);
  84:                  await _context.SaveChangesAsync();
  85:                  return RedirectToAction(nameof(Index));
  86:              }
  87:              PopulateDepartmentsDropDownList(course.DepartmentID);
  88:              return View(course);
  89:          }
  90:          #endregion
  91:   
  92:          // GET: Courses/Edit/5
  93:          #region snippet_EditGet
  94:          public async Task<IActionResult> Edit(int? id)
  95:          {
  96:              if (id == null)
  97:              {
  98:                  return NotFound();
  99:              }
 100:   
 101:              var course = await _context.Courses
 102:                  .AsNoTracking()
 103:                  .SingleOrDefaultAsync(m => m.CourseID == id);
 104:              if (course == null)
 105:              {
 106:                  return NotFound();
 107:              }
 108:              PopulateDepartmentsDropDownList(course.DepartmentID);
 109:              return View(course);
 110:          }
 111:          #endregion
 112:   
 113:   
 114:          // POST: Courses/Edit/5
 115:          #region snippet_EditPost
 116:          [HttpPost, ActionName("Edit")]
 117:          [ValidateAntiForgeryToken]
 118:          public async Task<IActionResult> EditPost(int? id)
 119:          {
 120:              if (id == null)
 121:              {
 122:                  return NotFound();
 123:              }
 124:   
 125:              var courseToUpdate = await _context.Courses
 126:                  .SingleOrDefaultAsync(c => c.CourseID == id);
 127:   
 128:              if (await TryUpdateModelAsync<Course>(courseToUpdate,
 129:                  "",
 130:                  c => c.Credits, c => c.DepartmentID, c => c.Title))
 131:              {
 132:                  try
 133:                  {
 134:                      await _context.SaveChangesAsync();
 135:                  }
 136:                  catch (DbUpdateException /* ex */)
 137:                  {
 138:                      //Log the error (uncomment ex variable name and write a log.)
 139:                      ModelState.AddModelError("", "Unable to save changes. " +
 140:                          "Try again, and if the problem persists, " +
 141:                          "see your system administrator.");
 142:                  }
 143:                  return RedirectToAction(nameof(Index));
 144:              }
 145:              PopulateDepartmentsDropDownList(courseToUpdate.DepartmentID);
 146:              return View(courseToUpdate);
 147:          }
 148:          #endregion
 149:   
 150:          #region snippet_Departments
 151:          private void PopulateDepartmentsDropDownList(object selectedDepartment = null)
 152:          {
 153:              var departmentsQuery = from d in _context.Departments
 154:                                     orderby d.Name
 155:                                     select d;
 156:              ViewBag.DepartmentID = new SelectList(departmentsQuery.AsNoTracking(), "DepartmentID", "Name", selectedDepartment);
 157:          }
 158:          #endregion
 159:   
 160:          // GET: Courses/Delete/5
 161:          #region snippet_DeleteGet
 162:          public async Task<IActionResult> Delete(int? id)
 163:          {
 164:              if (id == null)
 165:              {
 166:                  return NotFound();
 167:              }
 168:   
 169:              var course = await _context.Courses
 170:                  .Include(c => c.Department)
 171:                  .AsNoTracking()
 172:                  .SingleOrDefaultAsync(m => m.CourseID == id);
 173:              if (course == null)
 174:              {
 175:                  return NotFound();
 176:              }
 177:   
 178:              return View(course);
 179:          }
 180:          #endregion
 181:   
 182:          // POST: Courses/Delete/5
 183:          [HttpPost, ActionName("Delete")]
 184:          [ValidateAntiForgeryToken]
 185:          public async Task<IActionResult> DeleteConfirmed(int id)
 186:          {
 187:              var course = await _context.Courses
 188:                  .SingleOrDefaultAsync(m => m.CourseID == id);
 189:              if (course != null)
 190:              {
 191:                  _context.Courses.Remove(course);
 192:                  await _context.SaveChangesAsync();
 193:              }
 194:              return RedirectToAction(nameof(Index));
 195:          }
 196:   
 197:          #region snippet_UpdateGet
 198:          public IActionResult UpdateCourseCredits()
 199:          {
 200:              return View();
 201:          }
 202:          #endregion
 203:   
 204:          #region snippet_UpdatePost
 205:          [HttpPost]
 206:          public async Task<IActionResult> UpdateCourseCredits(int? multiplier)
 207:          {
 208:              if (multiplier != null)
 209:              {
 210:                  ViewData["RowsAffected"] = 
 211:                      await _context.Database.ExecuteSqlCommandAsync(
 212:                          "UPDATE Course SET Credits = Credits * {0}",
 213:                          parameters: multiplier);
 214:              }
 215:              return View();
 216:          }
 217:          #endregion
 218:      }
 219:  }

The HttpPost methods for both Create and Edit also include code that sets the selected item when they redisplay the page after an error. This ensures that when the page is redisplayed to show the error message, whatever department was selected stays selected.

Add .AsNoTracking to Details and Delete methods

To optimize performance of the Course Details and Delete pages, add AsNoTracking calls in the Details and HttpGet Delete methods.

[!code-csharpMain]

   1:  //#define ScaffoldedCode
   2:  #define RevisedIndexMethod
   3:   
   4:  using System;
   5:  using System.Collections.Generic;
   6:  using System.Linq;
   7:  using System.Threading.Tasks;
   8:  using Microsoft.AspNetCore.Mvc;
   9:  using Microsoft.AspNetCore.Mvc.Rendering;
  10:  using Microsoft.EntityFrameworkCore;
  11:  using ContosoUniversity.Data;
  12:  using ContosoUniversity.Models;
  13:   
  14:  namespace ContosoUniversity.Controllers
  15:  {
  16:      public class CoursesController : Controller
  17:      {
  18:          private readonly SchoolContext _context;
  19:   
  20:          public CoursesController(SchoolContext context)
  21:          {
  22:              _context = context;    
  23:          }
  24:   
  25:          // GET: Courses
  26:  #if ScaffoldedCode
  27:          public async Task<IActionResult> Index()
  28:          {
  29:              var schoolContext = _context.Courses
  30:                  .Include(c => c.Department)
  31:                  .AsNoTracking();
  32:              return View(await schoolContext.ToListAsync());
  33:          }
  34:  #elif RevisedIndexMethod
  35:          #region snippet_RevisedIndexMethod
  36:          public async Task<IActionResult> Index()
  37:          {
  38:              var courses = _context.Courses
  39:                  .Include(c => c.Department)
  40:                  .AsNoTracking();
  41:              return View(await courses.ToListAsync());
  42:          }
  43:          #endregion
  44:  #endif
  45:          // GET: Courses/Details/5
  46:          #region snippet_Details
  47:          public async Task<IActionResult> Details(int? id)
  48:          {
  49:              if (id == null)
  50:              {
  51:                  return NotFound();
  52:              }
  53:   
  54:              var course = await _context.Courses
  55:                  .Include(c => c.Department)
  56:                  .AsNoTracking()
  57:                  .SingleOrDefaultAsync(m => m.CourseID == id);
  58:              if (course == null)
  59:              {
  60:                  return NotFound();
  61:              }
  62:   
  63:              return View(course);
  64:          }
  65:          #endregion
  66:   
  67:          // GET: Courses/Create
  68:          #region snippet_CreateGet
  69:          public IActionResult Create()
  70:          {
  71:              PopulateDepartmentsDropDownList();
  72:              return View();
  73:          }
  74:          #endregion
  75:          // POST: Courses/Create
  76:          #region snippet_CreatePost
  77:          [HttpPost]
  78:          [ValidateAntiForgeryToken]
  79:          public async Task<IActionResult> Create([Bind("CourseID,Credits,DepartmentID,Title")] Course course)
  80:          {
  81:              if (ModelState.IsValid)
  82:              {
  83:                  _context.Add(course);
  84:                  await _context.SaveChangesAsync();
  85:                  return RedirectToAction(nameof(Index));
  86:              }
  87:              PopulateDepartmentsDropDownList(course.DepartmentID);
  88:              return View(course);
  89:          }
  90:          #endregion
  91:   
  92:          // GET: Courses/Edit/5
  93:          #region snippet_EditGet
  94:          public async Task<IActionResult> Edit(int? id)
  95:          {
  96:              if (id == null)
  97:              {
  98:                  return NotFound();
  99:              }
 100:   
 101:              var course = await _context.Courses
 102:                  .AsNoTracking()
 103:                  .SingleOrDefaultAsync(m => m.CourseID == id);
 104:              if (course == null)
 105:              {
 106:                  return NotFound();
 107:              }
 108:              PopulateDepartmentsDropDownList(course.DepartmentID);
 109:              return View(course);
 110:          }
 111:          #endregion
 112:   
 113:   
 114:          // POST: Courses/Edit/5
 115:          #region snippet_EditPost
 116:          [HttpPost, ActionName("Edit")]
 117:          [ValidateAntiForgeryToken]
 118:          public async Task<IActionResult> EditPost(int? id)
 119:          {
 120:              if (id == null)
 121:              {
 122:                  return NotFound();
 123:              }
 124:   
 125:              var courseToUpdate = await _context.Courses
 126:                  .SingleOrDefaultAsync(c => c.CourseID == id);
 127:   
 128:              if (await TryUpdateModelAsync<Course>(courseToUpdate,
 129:                  "",
 130:                  c => c.Credits, c => c.DepartmentID, c => c.Title))
 131:              {
 132:                  try
 133:                  {
 134:                      await _context.SaveChangesAsync();
 135:                  }
 136:                  catch (DbUpdateException /* ex */)
 137:                  {
 138:                      //Log the error (uncomment ex variable name and write a log.)
 139:                      ModelState.AddModelError("", "Unable to save changes. " +
 140:                          "Try again, and if the problem persists, " +
 141:                          "see your system administrator.");
 142:                  }
 143:                  return RedirectToAction(nameof(Index));
 144:              }
 145:              PopulateDepartmentsDropDownList(courseToUpdate.DepartmentID);
 146:              return View(courseToUpdate);
 147:          }
 148:          #endregion
 149:   
 150:          #region snippet_Departments
 151:          private void PopulateDepartmentsDropDownList(object selectedDepartment = null)
 152:          {
 153:              var departmentsQuery = from d in _context.Departments
 154:                                     orderby d.Name
 155:                                     select d;
 156:              ViewBag.DepartmentID = new SelectList(departmentsQuery.AsNoTracking(), "DepartmentID", "Name", selectedDepartment);
 157:          }
 158:          #endregion
 159:   
 160:          // GET: Courses/Delete/5
 161:          #region snippet_DeleteGet
 162:          public async Task<IActionResult> Delete(int? id)
 163:          {
 164:              if (id == null)
 165:              {
 166:                  return NotFound();
 167:              }
 168:   
 169:              var course = await _context.Courses
 170:                  .Include(c => c.Department)
 171:                  .AsNoTracking()
 172:                  .SingleOrDefaultAsync(m => m.CourseID == id);
 173:              if (course == null)
 174:              {
 175:                  return NotFound();
 176:              }
 177:   
 178:              return View(course);
 179:          }
 180:          #endregion
 181:   
 182:          // POST: Courses/Delete/5
 183:          [HttpPost, ActionName("Delete")]
 184:          [ValidateAntiForgeryToken]
 185:          public async Task<IActionResult> DeleteConfirmed(int id)
 186:          {
 187:              var course = await _context.Courses
 188:                  .SingleOrDefaultAsync(m => m.CourseID == id);
 189:              if (course != null)
 190:              {
 191:                  _context.Courses.Remove(course);
 192:                  await _context.SaveChangesAsync();
 193:              }
 194:              return RedirectToAction(nameof(Index));
 195:          }
 196:   
 197:          #region snippet_UpdateGet
 198:          public IActionResult UpdateCourseCredits()
 199:          {
 200:              return View();
 201:          }
 202:          #endregion
 203:   
 204:          #region snippet_UpdatePost
 205:          [HttpPost]
 206:          public async Task<IActionResult> UpdateCourseCredits(int? multiplier)
 207:          {
 208:              if (multiplier != null)
 209:              {
 210:                  ViewData["RowsAffected"] = 
 211:                      await _context.Database.ExecuteSqlCommandAsync(
 212:                          "UPDATE Course SET Credits = Credits * {0}",
 213:                          parameters: multiplier);
 214:              }
 215:              return View();
 216:          }
 217:          #endregion
 218:      }
 219:  }

[!code-csharpMain]

   1:  //#define ScaffoldedCode
   2:  #define RevisedIndexMethod
   3:   
   4:  using System;
   5:  using System.Collections.Generic;
   6:  using System.Linq;
   7:  using System.Threading.Tasks;
   8:  using Microsoft.AspNetCore.Mvc;
   9:  using Microsoft.AspNetCore.Mvc.Rendering;
  10:  using Microsoft.EntityFrameworkCore;
  11:  using ContosoUniversity.Data;
  12:  using ContosoUniversity.Models;
  13:   
  14:  namespace ContosoUniversity.Controllers
  15:  {
  16:      public class CoursesController : Controller
  17:      {
  18:          private readonly SchoolContext _context;
  19:   
  20:          public CoursesController(SchoolContext context)
  21:          {
  22:              _context = context;    
  23:          }
  24:   
  25:          // GET: Courses
  26:  #if ScaffoldedCode
  27:          public async Task<IActionResult> Index()
  28:          {
  29:              var schoolContext = _context.Courses
  30:                  .Include(c => c.Department)
  31:                  .AsNoTracking();
  32:              return View(await schoolContext.ToListAsync());
  33:          }
  34:  #elif RevisedIndexMethod
  35:          #region snippet_RevisedIndexMethod
  36:          public async Task<IActionResult> Index()
  37:          {
  38:              var courses = _context.Courses
  39:                  .Include(c => c.Department)
  40:                  .AsNoTracking();
  41:              return View(await courses.ToListAsync());
  42:          }
  43:          #endregion
  44:  #endif
  45:          // GET: Courses/Details/5
  46:          #region snippet_Details
  47:          public async Task<IActionResult> Details(int? id)
  48:          {
  49:              if (id == null)
  50:              {
  51:                  return NotFound();
  52:              }
  53:   
  54:              var course = await _context.Courses
  55:                  .Include(c => c.Department)
  56:                  .AsNoTracking()
  57:                  .SingleOrDefaultAsync(m => m.CourseID == id);
  58:              if (course == null)
  59:              {
  60:                  return NotFound();
  61:              }
  62:   
  63:              return View(course);
  64:          }
  65:          #endregion
  66:   
  67:          // GET: Courses/Create
  68:          #region snippet_CreateGet
  69:          public IActionResult Create()
  70:          {
  71:              PopulateDepartmentsDropDownList();
  72:              return View();
  73:          }
  74:          #endregion
  75:          // POST: Courses/Create
  76:          #region snippet_CreatePost
  77:          [HttpPost]
  78:          [ValidateAntiForgeryToken]
  79:          public async Task<IActionResult> Create([Bind("CourseID,Credits,DepartmentID,Title")] Course course)
  80:          {
  81:              if (ModelState.IsValid)
  82:              {
  83:                  _context.Add(course);
  84:                  await _context.SaveChangesAsync();
  85:                  return RedirectToAction(nameof(Index));
  86:              }
  87:              PopulateDepartmentsDropDownList(course.DepartmentID);
  88:              return View(course);
  89:          }
  90:          #endregion
  91:   
  92:          // GET: Courses/Edit/5
  93:          #region snippet_EditGet
  94:          public async Task<IActionResult> Edit(int? id)
  95:          {
  96:              if (id == null)
  97:              {
  98:                  return NotFound();
  99:              }
 100:   
 101:              var course = await _context.Courses
 102:                  .AsNoTracking()
 103:                  .SingleOrDefaultAsync(m => m.CourseID == id);
 104:              if (course == null)
 105:              {
 106:                  return NotFound();
 107:              }
 108:              PopulateDepartmentsDropDownList(course.DepartmentID);
 109:              return View(course);
 110:          }
 111:          #endregion
 112:   
 113:   
 114:          // POST: Courses/Edit/5
 115:          #region snippet_EditPost
 116:          [HttpPost, ActionName("Edit")]
 117:          [ValidateAntiForgeryToken]
 118:          public async Task<IActionResult> EditPost(int? id)
 119:          {
 120:              if (id == null)
 121:              {
 122:                  return NotFound();
 123:              }
 124:   
 125:              var courseToUpdate = await _context.Courses
 126:                  .SingleOrDefaultAsync(c => c.CourseID == id);
 127:   
 128:              if (await TryUpdateModelAsync<Course>(courseToUpdate,
 129:                  "",
 130:                  c => c.Credits, c => c.DepartmentID, c => c.Title))
 131:              {
 132:                  try
 133:                  {
 134:                      await _context.SaveChangesAsync();
 135:                  }
 136:                  catch (DbUpdateException /* ex */)
 137:                  {
 138:                      //Log the error (uncomment ex variable name and write a log.)
 139:                      ModelState.AddModelError("", "Unable to save changes. " +
 140:                          "Try again, and if the problem persists, " +
 141:                          "see your system administrator.");
 142:                  }
 143:                  return RedirectToAction(nameof(Index));
 144:              }
 145:              PopulateDepartmentsDropDownList(courseToUpdate.DepartmentID);
 146:              return View(courseToUpdate);
 147:          }
 148:          #endregion
 149:   
 150:          #region snippet_Departments
 151:          private void PopulateDepartmentsDropDownList(object selectedDepartment = null)
 152:          {
 153:              var departmentsQuery = from d in _context.Departments
 154:                                     orderby d.Name
 155:                                     select d;
 156:              ViewBag.DepartmentID = new SelectList(departmentsQuery.AsNoTracking(), "DepartmentID", "Name", selectedDepartment);
 157:          }
 158:          #endregion
 159:   
 160:          // GET: Courses/Delete/5
 161:          #region snippet_DeleteGet
 162:          public async Task<IActionResult> Delete(int? id)
 163:          {
 164:              if (id == null)
 165:              {
 166:                  return NotFound();
 167:              }
 168:   
 169:              var course = await _context.Courses
 170:                  .Include(c => c.Department)
 171:                  .AsNoTracking()
 172:                  .SingleOrDefaultAsync(m => m.CourseID == id);
 173:              if (course == null)
 174:              {
 175:                  return NotFound();
 176:              }
 177:   
 178:              return View(course);
 179:          }
 180:          #endregion
 181:   
 182:          // POST: Courses/Delete/5
 183:          [HttpPost, ActionName("Delete")]
 184:          [ValidateAntiForgeryToken]
 185:          public async Task<IActionResult> DeleteConfirmed(int id)
 186:          {
 187:              var course = await _context.Courses
 188:                  .SingleOrDefaultAsync(m => m.CourseID == id);
 189:              if (course != null)
 190:              {
 191:                  _context.Courses.Remove(course);
 192:                  await _context.SaveChangesAsync();
 193:              }
 194:              return RedirectToAction(nameof(Index));
 195:          }
 196:   
 197:          #region snippet_UpdateGet
 198:          public IActionResult UpdateCourseCredits()
 199:          {
 200:              return View();
 201:          }
 202:          #endregion
 203:   
 204:          #region snippet_UpdatePost
 205:          [HttpPost]
 206:          public async Task<IActionResult> UpdateCourseCredits(int? multiplier)
 207:          {
 208:              if (multiplier != null)
 209:              {
 210:                  ViewData["RowsAffected"] = 
 211:                      await _context.Database.ExecuteSqlCommandAsync(
 212:                          "UPDATE Course SET Credits = Credits * {0}",
 213:                          parameters: multiplier);
 214:              }
 215:              return View();
 216:          }
 217:          #endregion
 218:      }
 219:  }

Modify the Course views

In Views/Courses/Create.cshtml, add a “Select Department” option to the Department drop-down list, change the caption from DepartmentID to Department, and add a validation message.

[!code-htmlMain]

   1:  @model ContosoUniversity.Models.Course
   2:   
   3:  @{
   4:      ViewData["Title"] = "Create";
   5:  }
   6:   
   7:  <h2>Create</h2>
   8:   
   9:  <form asp-action="Create">
  10:      <div class="form-horizontal">
  11:          <h4>Course</h4>
  12:          <hr />
  13:          <div asp-validation-summary="ModelOnly" class="text-danger"></div>
  14:          <div class="form-group">
  15:              <label asp-for="CourseID" class="control-label"></label>
  16:              <input asp-for="CourseID" class="form-control" />
  17:              <span asp-validation-for="CourseID" class="text-danger" />
  18:          </div>
  19:          <div class="form-group">
  20:              <label asp-for="Title" class="control-label"></label>
  21:              <input asp-for="Title" class="form-control" />
  22:              <span asp-validation-for="Title" class="text-danger"></span>
  23:          </div>
  24:          <div class="form-group">
  25:              <label asp-for="Credits" class="control-label"></label>
  26:              <input asp-for="Credits" class="form-control" />
  27:              <span asp-validation-for="Credits" class="text-danger"></span>
  28:          </div>
  29:          <div class="form-group">
  30:              <label asp-for="Department" class="control-label"></label>
  31:              <select asp-for="DepartmentID" class="form-control" asp-items="ViewBag.DepartmentID">
  32:                  <option value="">-- Select Department --</option>
  33:              </select>
  34:              <span asp-validation-for="DepartmentID" class="text-danger" />
  35:          </div>
  36:          <div class="form-group">
  37:              <div class="col-md-offset-2 col-md-10">
  38:                  <input type="submit" value="Create" class="btn btn-default" />
  39:              </div>
  40:          </div>
  41:      </div>
  42:  </form>
  43:   
  44:  <div>
  45:      <a asp-action="Index">Back to List</a>
  46:  </div>
  47:   
  48:  @section Scripts {
  49:      @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
  50:  }

In Views/Courses/Edit.cshtml, make the same change for the Department field that you just did in Create.cshtml.

Also in Views/Courses/Edit.cshtml, add a course number field before the Title field. Because the course number is the primary key, it’s displayed, but it can’t be changed.

[!code-htmlMain]

   1:  @model ContosoUniversity.Models.Course
   2:   
   3:  @{
   4:      ViewData["Title"] = "Edit";
   5:  }
   6:   
   7:  <h2>Edit</h2>
   8:   
   9:  <form asp-action="Edit">
  10:      <div class="form-horizontal">
  11:          <h4>Course</h4>
  12:          <hr />
  13:          <div asp-validation-summary="ModelOnly" class="text-danger"></div>
  14:          <input type="hidden" asp-for="CourseID" />
  15:          <div class="form-group">
  16:              <label asp-for="CourseID" class="control-label"></label>
  17:              <div>@Html.DisplayFor(model => model.CourseID)</div>
  18:          </div>
  19:          <div class="form-group">
  20:              <label asp-for="Title" class="control-label"></label>
  21:              <input asp-for="Title" class="form-control" />
  22:              <span asp-validation-for="Title" class="text-danger"></span>
  23:          </div>
  24:          <div class="form-group">
  25:              <label asp-for="Credits" class="control-label"></label>
  26:              <input asp-for="Credits" class="form-control" />
  27:              <span asp-validation-for="Credits" class="text-danger"></span>
  28:          </div>
  29:          <div class="form-group">
  30:              <label asp-for="Department" class="control-label"></label>
  31:              <select asp-for="DepartmentID" class="form-control" asp-items="ViewBag.DepartmentID">
  32:                  <option value="">-- Select Department --</option>
  33:              </select>
  34:              </div>
  35:          </div>
  36:          <div class="form-group">
  37:                  <input type="submit" value="Save" class="btn btn-default" />
  38:          </div>
  39:      </div>
  40:  </form>
  41:   
  42:  <div>
  43:      <a asp-action="Index">Back to List</a>
  44:  </div>
  45:   
  46:  @section Scripts {
  47:      @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
  48:  }

There’s already a hidden field (<input type="hidden">) for the course number in the Edit view. Adding a <label> tag helper doesn’t eliminate the need for the hidden field because it doesn’t cause the course number to be included in the posted data when the user clicks Save on the Edit page.

In Views/Courses/Delete.cshtml, add a course number field at the top and change department ID to department name.

[!code-htmlMain]

   1:  @model ContosoUniversity.Models.Course
   2:   
   3:  @{
   4:      ViewData["Title"] = "Delete";
   5:  }
   6:   
   7:  <h2>Delete</h2>
   8:   
   9:  <h3>Are you sure you want to delete this?</h3>
  10:  <div>
  11:      <h4>Course</h4>
  12:      <hr />
  13:      <dl class="dl-horizontal">
  14:          <dt>
  15:              @Html.DisplayNameFor(model => model.CourseID)
  16:          </dt>
  17:          <dd>
  18:              @Html.DisplayFor(model => model.CourseID)
  19:          </dd>
  20:          <dt>
  21:              @Html.DisplayNameFor(model => model.Title)
  22:          </dt>
  23:          <dd>
  24:              @Html.DisplayFor(model => model.Title)
  25:          </dd>
  26:          <dt>
  27:              @Html.DisplayNameFor(model => model.Credits)
  28:          </dt>
  29:          <dd>
  30:              @Html.DisplayFor(model => model.Credits)
  31:          </dd>
  32:          <dt>
  33:              @Html.DisplayNameFor(model => model.Department)
  34:          </dt>
  35:          <dd>
  36:              @Html.DisplayFor(model => model.Department.Name)
  37:          </dd>
  38:      </dl>
  39:      
  40:      <form asp-action="Delete">
  41:          <div class="form-actions no-color">
  42:              <input type="submit" value="Delete" class="btn btn-default" /> |
  43:              <a asp-action="Index">Back to List</a>
  44:          </div>
  45:      </form>
  46:  </div>

In Views/Courses/Details.cshtml, make the same change that you just did for Delete.cshtml.

Test the Course pages

Run the app, select the Courses tab, click Create New, and enter data for a new course:

Course Create page
Course Create page

Click Create. The Courses Index page is displayed with the new course added to the list. The department name in the Index page list comes from the navigation property, showing that the relationship was established correctly.

Click Edit on a course in the Courses Index page.

Course Edit page
Course Edit page

Change data on the page and click Save. The Courses Index page is displayed with the updated course data.

Add an Edit Page for Instructors

When you edit an instructor record, you want to be able to update the instructor’s office assignment. The Instructor entity has a one-to-zero-or-one relationship with the OfficeAssignment entity, which means your code has to handle the following situations:

Update the Instructors controller

In InstructorsController.cs, change the code in the HttpGet Edit method so that it loads the Instructor entity’s OfficeAssignment navigation property and calls AsNoTracking:

[!code-csharpMain]

   1:  #define ExplicitLoading // or EagerLoading or ScaffoldedCode
   2:  #define EditCourses // or EditOfficeAssignment
   3:   
   4:  using System;
   5:  using System.Collections.Generic;
   6:  using System.Linq;
   7:  using System.Threading.Tasks;
   8:  using Microsoft.AspNetCore.Mvc;
   9:  using Microsoft.AspNetCore.Mvc.Rendering;
  10:  using Microsoft.EntityFrameworkCore;
  11:  using ContosoUniversity.Data;
  12:  using ContosoUniversity.Models;
  13:  #region snippet_Using
  14:  using ContosoUniversity.Models.SchoolViewModels;
  15:  #endregion
  16:   
  17:  namespace ContosoUniversity.Controllers
  18:  {
  19:      public class InstructorsController : Controller
  20:      {
  21:          private readonly SchoolContext _context;
  22:   
  23:          public InstructorsController(SchoolContext context)
  24:          {
  25:              _context = context;
  26:          }
  27:   
  28:          // GET: Instructors
  29:  #if ScaffoldedCode
  30:          #region snippet_ScaffoldedCode
  31:          public async Task<IActionResult> Index()
  32:          {
  33:              return View(await _context.Instructors.AsNoTracking().ToListAsync());
  34:          }
  35:          #endregion
  36:  #elif EagerLoading
  37:          #region snippet_EagerLoading
  38:          public async Task<IActionResult> Index(int? id, int? courseID)
  39:          {
  40:              var viewModel = new InstructorIndexData();
  41:              #region snippet_ThenInclude
  42:              viewModel.Instructors = await _context.Instructors
  43:                    .Include(i => i.OfficeAssignment)
  44:                    .Include(i => i.CourseAssignments)
  45:                      .ThenInclude(i => i.Course)
  46:                          .ThenInclude(i => i.Enrollments)
  47:                              .ThenInclude(i => i.Student)
  48:                    .Include(i => i.CourseAssignments)
  49:                      .ThenInclude(i => i.Course)
  50:                          .ThenInclude(i => i.Department)
  51:                    .AsNoTracking()
  52:                    .OrderBy(i => i.LastName)
  53:                    .ToListAsync();
  54:              #endregion
  55:              
  56:              if (id != null)
  57:              {
  58:                  ViewData["InstructorID"] = id.Value;
  59:                  Instructor instructor = viewModel.Instructors.Where(
  60:                      i => i.ID == id.Value).Single();
  61:                  viewModel.Courses = instructor.CourseAssignments.Select(s => s.Course);
  62:              }
  63:   
  64:              if (courseID != null)
  65:              {
  66:                  ViewData["CourseID"] = courseID.Value;
  67:                  viewModel.Enrollments = viewModel.Courses.Where(
  68:                      x => x.CourseID == courseID).Single().Enrollments;
  69:              }
  70:   
  71:              return View(viewModel);
  72:          }
  73:          #endregion
  74:  #elif ExplicitLoading
  75:          #region snippet_ExplicitLoading
  76:          public async Task<IActionResult> Index(int? id, int? courseID)
  77:          {
  78:              var viewModel = new InstructorIndexData();
  79:              viewModel.Instructors = await _context.Instructors
  80:                    .Include(i => i.OfficeAssignment)
  81:                    .Include(i => i.CourseAssignments)
  82:                      .ThenInclude(i => i.Course)
  83:                          .ThenInclude(i => i.Department)
  84:                    .OrderBy(i => i.LastName)
  85:                    .ToListAsync();
  86:   
  87:              if (id != null)
  88:              {
  89:                  ViewData["InstructorID"] = id.Value;
  90:                  Instructor instructor = viewModel.Instructors.Where(
  91:                      i => i.ID == id.Value).Single();
  92:                  viewModel.Courses = instructor.CourseAssignments.Select(s => s.Course);
  93:              }
  94:   
  95:              if (courseID != null)
  96:              {
  97:                  ViewData["CourseID"] = courseID.Value;
  98:                  var selectedCourse = viewModel.Courses.Where(x => x.CourseID == courseID).Single();
  99:                  await _context.Entry(selectedCourse).Collection(x => x.Enrollments).LoadAsync();
 100:                  foreach (Enrollment enrollment in selectedCourse.Enrollments)
 101:                  {
 102:                      await _context.Entry(enrollment).Reference(x => x.Student).LoadAsync();
 103:                  }
 104:                  viewModel.Enrollments = selectedCourse.Enrollments;
 105:              }
 106:   
 107:              return View(viewModel);
 108:          }
 109:          #endregion
 110:  #endif
 111:          // GET: Instructors/Details/5
 112:          public async Task<IActionResult> Details(int? id)
 113:          {
 114:              if (id == null)
 115:              {
 116:                  return NotFound();
 117:              }
 118:   
 119:              var instructor = await _context.Instructors
 120:                  .AsNoTracking()
 121:                  .SingleOrDefaultAsync(m => m.ID == id);
 122:              if (instructor == null)
 123:              {
 124:                  return NotFound();
 125:              }
 126:   
 127:              return View(instructor);
 128:          }
 129:   
 130:          // GET: Instructors/Create
 131:          #region snippet_Create
 132:          public IActionResult Create()
 133:          {
 134:              var instructor = new Instructor();
 135:              instructor.CourseAssignments = new List<CourseAssignment>();
 136:              PopulateAssignedCourseData(instructor);
 137:              return View();
 138:          }
 139:   
 140:          // POST: Instructors/Create
 141:          [HttpPost]
 142:          [ValidateAntiForgeryToken]
 143:          public async Task<IActionResult> Create([Bind("FirstMidName,HireDate,LastName,OfficeAssignment")] Instructor instructor, string[] selectedCourses)
 144:          {
 145:              if (selectedCourses != null)
 146:              {
 147:                  instructor.CourseAssignments = new List<CourseAssignment>();
 148:                  foreach (var course in selectedCourses)
 149:                  {
 150:                      var courseToAdd = new CourseAssignment { InstructorID = instructor.ID, CourseID = int.Parse(course) };
 151:                      instructor.CourseAssignments.Add(courseToAdd);
 152:                  }
 153:              }
 154:              if (ModelState.IsValid)
 155:              {
 156:                  _context.Add(instructor);
 157:                  await _context.SaveChangesAsync();
 158:                  return RedirectToAction(nameof(Index));
 159:              }
 160:              PopulateAssignedCourseData(instructor);
 161:              return View(instructor);
 162:          }
 163:          #endregion
 164:   
 165:          // GET: Instructors/Edit/5
 166:  #if EditOfficeAssignment
 167:          #region snippet_EditGetOA
 168:          public async Task<IActionResult> Edit(int? id)
 169:          {
 170:              if (id == null)
 171:              {
 172:                  return NotFound();
 173:              }
 174:   
 175:              var instructor = await _context.Instructors
 176:                  .Include(i => i.OfficeAssignment)
 177:                  .AsNoTracking()
 178:                  .SingleOrDefaultAsync(m => m.ID == id);
 179:              if (instructor == null)
 180:              {
 181:                  return NotFound();
 182:              }
 183:              return View(instructor);
 184:          }
 185:          #endregion
 186:  #elif EditCourses
 187:          #region snippet_EditGetCourses
 188:          public async Task<IActionResult> Edit(int? id)
 189:          {
 190:              if (id == null)
 191:              {
 192:                  return NotFound();
 193:              }
 194:   
 195:              var instructor = await _context.Instructors
 196:                  .Include(i => i.OfficeAssignment)
 197:                  .Include(i => i.CourseAssignments).ThenInclude(i => i.Course)
 198:                  .AsNoTracking()
 199:                  .SingleOrDefaultAsync(m => m.ID == id);
 200:              if (instructor == null)
 201:              {
 202:                  return NotFound();
 203:              }
 204:              PopulateAssignedCourseData(instructor);
 205:              return View(instructor);
 206:          }
 207:   
 208:          private void PopulateAssignedCourseData(Instructor instructor)
 209:          {
 210:              var allCourses = _context.Courses;
 211:              var instructorCourses = new HashSet<int>(instructor.CourseAssignments.Select(c => c.CourseID));
 212:              var viewModel = new List<AssignedCourseData>();
 213:              foreach (var course in allCourses)
 214:              {
 215:                  viewModel.Add(new AssignedCourseData
 216:                  {
 217:                      CourseID = course.CourseID,
 218:                      Title = course.Title,
 219:                      Assigned = instructorCourses.Contains(course.CourseID)
 220:                  });
 221:              }
 222:              ViewData["Courses"] = viewModel;
 223:          }
 224:          #endregion
 225:  #endif
 226:          // POST: Instructors/Edit/5
 227:          // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
 228:          // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
 229:  #if EditOfficeAssignment
 230:          #region snippet_EditPostOA
 231:          [HttpPost, ActionName("Edit")]
 232:          [ValidateAntiForgeryToken]
 233:          public async Task<IActionResult> EditPost(int? id)
 234:          {
 235:              if (id == null)
 236:              {
 237:                  return NotFound();
 238:              }
 239:   
 240:              var instructorToUpdate = await _context.Instructors
 241:                  .Include(i => i.OfficeAssignment)
 242:                  .SingleOrDefaultAsync(s => s.ID == id);
 243:   
 244:              if (await TryUpdateModelAsync<Instructor>(
 245:                  instructorToUpdate,
 246:                  "",
 247:                  i => i.FirstMidName, i => i.LastName, i => i.HireDate, i => i.OfficeAssignment))
 248:              {
 249:                  if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment?.Location))
 250:                  {
 251:                      instructorToUpdate.OfficeAssignment = null;
 252:                  }
 253:                  try
 254:                  {
 255:                      await _context.SaveChangesAsync();
 256:                  }
 257:                  catch (DbUpdateException /* ex */)
 258:                  {
 259:                      //Log the error (uncomment ex variable name and write a log.)
 260:                      ModelState.AddModelError("", "Unable to save changes. " +
 261:                          "Try again, and if the problem persists, " +
 262:                          "see your system administrator.");
 263:                  }
 264:                  return RedirectToAction(nameof(Index));
 265:              }
 266:              return View(instructorToUpdate);
 267:          }
 268:          #endregion
 269:   
 270:  #elif EditCourses
 271:          #region snippet_EditPostCourses
 272:          [HttpPost]
 273:          [ValidateAntiForgeryToken]
 274:          public async Task<IActionResult> Edit(int? id, string[] selectedCourses)
 275:          {
 276:              if (id == null)
 277:              {
 278:                  return NotFound();
 279:              }
 280:   
 281:              var instructorToUpdate = await _context.Instructors
 282:                  .Include(i => i.OfficeAssignment)
 283:                  .Include(i => i.CourseAssignments)
 284:                      .ThenInclude(i => i.Course)
 285:                  .SingleOrDefaultAsync(m => m.ID == id);
 286:   
 287:              if (await TryUpdateModelAsync<Instructor>(
 288:                  instructorToUpdate,
 289:                  "",
 290:                  i => i.FirstMidName, i => i.LastName, i => i.HireDate, i => i.OfficeAssignment))
 291:              {
 292:                  if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment?.Location))
 293:                  {
 294:                      instructorToUpdate.OfficeAssignment = null;
 295:                  }
 296:                  UpdateInstructorCourses(selectedCourses, instructorToUpdate);
 297:                  try
 298:                  {
 299:                      await _context.SaveChangesAsync();
 300:                  }
 301:                  catch (DbUpdateException /* ex */)
 302:                  {
 303:                      //Log the error (uncomment ex variable name and write a log.)
 304:                      ModelState.AddModelError("", "Unable to save changes. " +
 305:                          "Try again, and if the problem persists, " +
 306:                          "see your system administrator.");
 307:                  }
 308:                  return RedirectToAction(nameof(Index));
 309:              }
 310:              UpdateInstructorCourses(selectedCourses, instructorToUpdate);
 311:              PopulateAssignedCourseData(instructorToUpdate);
 312:              return View(instructorToUpdate);
 313:          }
 314:          #endregion
 315:   
 316:          #region snippet_UpdateCourses
 317:          private void UpdateInstructorCourses(string[] selectedCourses, Instructor instructorToUpdate)
 318:          {
 319:              if (selectedCourses == null)
 320:              {
 321:                  instructorToUpdate.CourseAssignments = new List<CourseAssignment>();
 322:                  return;
 323:              }
 324:   
 325:              var selectedCoursesHS = new HashSet<string>(selectedCourses);
 326:              var instructorCourses = new HashSet<int>
 327:                  (instructorToUpdate.CourseAssignments.Select(c => c.Course.CourseID));
 328:              foreach (var course in _context.Courses)
 329:              {
 330:                  if (selectedCoursesHS.Contains(course.CourseID.ToString()))
 331:                  {
 332:                      if (!instructorCourses.Contains(course.CourseID))
 333:                      {
 334:                          instructorToUpdate.CourseAssignments.Add(new CourseAssignment { InstructorID = instructorToUpdate.ID, CourseID = course.CourseID });
 335:                      }
 336:                  }
 337:                  else
 338:                  {
 339:   
 340:                      if (instructorCourses.Contains(course.CourseID))
 341:                      {
 342:                          CourseAssignment courseToRemove = instructorToUpdate.CourseAssignments.SingleOrDefault(i => i.CourseID == course.CourseID);
 343:                          _context.Remove(courseToRemove);
 344:                      }
 345:                  }
 346:              }
 347:          }
 348:          #endregion
 349:  #endif
 350:          // GET: Instructors/Delete/5
 351:          public async Task<IActionResult> Delete(int? id)
 352:          {
 353:              if (id == null)
 354:              {
 355:                  return NotFound();
 356:              }
 357:   
 358:              var instructor = await _context.Instructors
 359:                  .AsNoTracking()
 360:                  .SingleOrDefaultAsync(m => m.ID == id);
 361:              if (instructor == null)
 362:              {
 363:                  return NotFound();
 364:              }
 365:   
 366:              return View(instructor);
 367:          }
 368:   
 369:          #region snippet_DeleteConfirmed
 370:          [HttpPost, ActionName("Delete")]
 371:          [ValidateAntiForgeryToken]
 372:          public async Task<IActionResult> DeleteConfirmed(int id)
 373:          {
 374:              Instructor instructor = await _context.Instructors
 375:                  .Include(i => i.CourseAssignments)
 376:                  .SingleAsync(i => i.ID == id);
 377:   
 378:              var departments = await _context.Departments
 379:                  .Where(d => d.InstructorID == id)
 380:                  .ToListAsync();
 381:              departments.ForEach(d => d.InstructorID = null);
 382:   
 383:              _context.Instructors.Remove(instructor);
 384:   
 385:              await _context.SaveChangesAsync();
 386:              return RedirectToAction(nameof(Index));
 387:          }
 388:          #endregion
 389:      }
 390:  }

Replace the HttpPost Edit method with the following code to handle office assignment updates:

[!code-csharpMain]

   1:  #define ExplicitLoading // or EagerLoading or ScaffoldedCode
   2:  #define EditCourses // or EditOfficeAssignment
   3:   
   4:  using System;
   5:  using System.Collections.Generic;
   6:  using System.Linq;
   7:  using System.Threading.Tasks;
   8:  using Microsoft.AspNetCore.Mvc;
   9:  using Microsoft.AspNetCore.Mvc.Rendering;
  10:  using Microsoft.EntityFrameworkCore;
  11:  using ContosoUniversity.Data;
  12:  using ContosoUniversity.Models;
  13:  #region snippet_Using
  14:  using ContosoUniversity.Models.SchoolViewModels;
  15:  #endregion
  16:   
  17:  namespace ContosoUniversity.Controllers
  18:  {
  19:      public class InstructorsController : Controller
  20:      {
  21:          private readonly SchoolContext _context;
  22:   
  23:          public InstructorsController(SchoolContext context)
  24:          {
  25:              _context = context;
  26:          }
  27:   
  28:          // GET: Instructors
  29:  #if ScaffoldedCode
  30:          #region snippet_ScaffoldedCode
  31:          public async Task<IActionResult> Index()
  32:          {
  33:              return View(await _context.Instructors.AsNoTracking().ToListAsync());
  34:          }
  35:          #endregion
  36:  #elif EagerLoading
  37:          #region snippet_EagerLoading
  38:          public async Task<IActionResult> Index(int? id, int? courseID)
  39:          {
  40:              var viewModel = new InstructorIndexData();
  41:              #region snippet_ThenInclude
  42:              viewModel.Instructors = await _context.Instructors
  43:                    .Include(i => i.OfficeAssignment)
  44:                    .Include(i => i.CourseAssignments)
  45:                      .ThenInclude(i => i.Course)
  46:                          .ThenInclude(i => i.Enrollments)
  47:                              .ThenInclude(i => i.Student)
  48:                    .Include(i => i.CourseAssignments)
  49:                      .ThenInclude(i => i.Course)
  50:                          .ThenInclude(i => i.Department)
  51:                    .AsNoTracking()
  52:                    .OrderBy(i => i.LastName)
  53:                    .ToListAsync();
  54:              #endregion
  55:              
  56:              if (id != null)
  57:              {
  58:                  ViewData["InstructorID"] = id.Value;
  59:                  Instructor instructor = viewModel.Instructors.Where(
  60:                      i => i.ID == id.Value).Single();
  61:                  viewModel.Courses = instructor.CourseAssignments.Select(s => s.Course);
  62:              }
  63:   
  64:              if (courseID != null)
  65:              {
  66:                  ViewData["CourseID"] = courseID.Value;
  67:                  viewModel.Enrollments = viewModel.Courses.Where(
  68:                      x => x.CourseID == courseID).Single().Enrollments;
  69:              }
  70:   
  71:              return View(viewModel);
  72:          }
  73:          #endregion
  74:  #elif ExplicitLoading
  75:          #region snippet_ExplicitLoading
  76:          public async Task<IActionResult> Index(int? id, int? courseID)
  77:          {
  78:              var viewModel = new InstructorIndexData();
  79:              viewModel.Instructors = await _context.Instructors
  80:                    .Include(i => i.OfficeAssignment)
  81:                    .Include(i => i.CourseAssignments)
  82:                      .ThenInclude(i => i.Course)
  83:                          .ThenInclude(i => i.Department)
  84:                    .OrderBy(i => i.LastName)
  85:                    .ToListAsync();
  86:   
  87:              if (id != null)
  88:              {
  89:                  ViewData["InstructorID"] = id.Value;
  90:                  Instructor instructor = viewModel.Instructors.Where(
  91:                      i => i.ID == id.Value).Single();
  92:                  viewModel.Courses = instructor.CourseAssignments.Select(s => s.Course);
  93:              }
  94:   
  95:              if (courseID != null)
  96:              {
  97:                  ViewData["CourseID"] = courseID.Value;
  98:                  var selectedCourse = viewModel.Courses.Where(x => x.CourseID == courseID).Single();
  99:                  await _context.Entry(selectedCourse).Collection(x => x.Enrollments).LoadAsync();
 100:                  foreach (Enrollment enrollment in selectedCourse.Enrollments)
 101:                  {
 102:                      await _context.Entry(enrollment).Reference(x => x.Student).LoadAsync();
 103:                  }
 104:                  viewModel.Enrollments = selectedCourse.Enrollments;
 105:              }
 106:   
 107:              return View(viewModel);
 108:          }
 109:          #endregion
 110:  #endif
 111:          // GET: Instructors/Details/5
 112:          public async Task<IActionResult> Details(int? id)
 113:          {
 114:              if (id == null)
 115:              {
 116:                  return NotFound();
 117:              }
 118:   
 119:              var instructor = await _context.Instructors
 120:                  .AsNoTracking()
 121:                  .SingleOrDefaultAsync(m => m.ID == id);
 122:              if (instructor == null)
 123:              {
 124:                  return NotFound();
 125:              }
 126:   
 127:              return View(instructor);
 128:          }
 129:   
 130:          // GET: Instructors/Create
 131:          #region snippet_Create
 132:          public IActionResult Create()
 133:          {
 134:              var instructor = new Instructor();
 135:              instructor.CourseAssignments = new List<CourseAssignment>();
 136:              PopulateAssignedCourseData(instructor);
 137:              return View();
 138:          }
 139:   
 140:          // POST: Instructors/Create
 141:          [HttpPost]
 142:          [ValidateAntiForgeryToken]
 143:          public async Task<IActionResult> Create([Bind("FirstMidName,HireDate,LastName,OfficeAssignment")] Instructor instructor, string[] selectedCourses)
 144:          {
 145:              if (selectedCourses != null)
 146:              {
 147:                  instructor.CourseAssignments = new List<CourseAssignment>();
 148:                  foreach (var course in selectedCourses)
 149:                  {
 150:                      var courseToAdd = new CourseAssignment { InstructorID = instructor.ID, CourseID = int.Parse(course) };
 151:                      instructor.CourseAssignments.Add(courseToAdd);
 152:                  }
 153:              }
 154:              if (ModelState.IsValid)
 155:              {
 156:                  _context.Add(instructor);
 157:                  await _context.SaveChangesAsync();
 158:                  return RedirectToAction(nameof(Index));
 159:              }
 160:              PopulateAssignedCourseData(instructor);
 161:              return View(instructor);
 162:          }
 163:          #endregion
 164:   
 165:          // GET: Instructors/Edit/5
 166:  #if EditOfficeAssignment
 167:          #region snippet_EditGetOA
 168:          public async Task<IActionResult> Edit(int? id)
 169:          {
 170:              if (id == null)
 171:              {
 172:                  return NotFound();
 173:              }
 174:   
 175:              var instructor = await _context.Instructors
 176:                  .Include(i => i.OfficeAssignment)
 177:                  .AsNoTracking()
 178:                  .SingleOrDefaultAsync(m => m.ID == id);
 179:              if (instructor == null)
 180:              {
 181:                  return NotFound();
 182:              }
 183:              return View(instructor);
 184:          }
 185:          #endregion
 186:  #elif EditCourses
 187:          #region snippet_EditGetCourses
 188:          public async Task<IActionResult> Edit(int? id)
 189:          {
 190:              if (id == null)
 191:              {
 192:                  return NotFound();
 193:              }
 194:   
 195:              var instructor = await _context.Instructors
 196:                  .Include(i => i.OfficeAssignment)
 197:                  .Include(i => i.CourseAssignments).ThenInclude(i => i.Course)
 198:                  .AsNoTracking()
 199:                  .SingleOrDefaultAsync(m => m.ID == id);
 200:              if (instructor == null)
 201:              {
 202:                  return NotFound();
 203:              }
 204:              PopulateAssignedCourseData(instructor);
 205:              return View(instructor);
 206:          }
 207:   
 208:          private void PopulateAssignedCourseData(Instructor instructor)
 209:          {
 210:              var allCourses = _context.Courses;
 211:              var instructorCourses = new HashSet<int>(instructor.CourseAssignments.Select(c => c.CourseID));
 212:              var viewModel = new List<AssignedCourseData>();
 213:              foreach (var course in allCourses)
 214:              {
 215:                  viewModel.Add(new AssignedCourseData
 216:                  {
 217:                      CourseID = course.CourseID,
 218:                      Title = course.Title,
 219:                      Assigned = instructorCourses.Contains(course.CourseID)
 220:                  });
 221:              }
 222:              ViewData["Courses"] = viewModel;
 223:          }
 224:          #endregion
 225:  #endif
 226:          // POST: Instructors/Edit/5
 227:          // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
 228:          // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
 229:  #if EditOfficeAssignment
 230:          #region snippet_EditPostOA
 231:          [HttpPost, ActionName("Edit")]
 232:          [ValidateAntiForgeryToken]
 233:          public async Task<IActionResult> EditPost(int? id)
 234:          {
 235:              if (id == null)
 236:              {
 237:                  return NotFound();
 238:              }
 239:   
 240:              var instructorToUpdate = await _context.Instructors
 241:                  .Include(i => i.OfficeAssignment)
 242:                  .SingleOrDefaultAsync(s => s.ID == id);
 243:   
 244:              if (await TryUpdateModelAsync<Instructor>(
 245:                  instructorToUpdate,
 246:                  "",
 247:                  i => i.FirstMidName, i => i.LastName, i => i.HireDate, i => i.OfficeAssignment))
 248:              {
 249:                  if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment?.Location))
 250:                  {
 251:                      instructorToUpdate.OfficeAssignment = null;
 252:                  }
 253:                  try
 254:                  {
 255:                      await _context.SaveChangesAsync();
 256:                  }
 257:                  catch (DbUpdateException /* ex */)
 258:                  {
 259:                      //Log the error (uncomment ex variable name and write a log.)
 260:                      ModelState.AddModelError("", "Unable to save changes. " +
 261:                          "Try again, and if the problem persists, " +
 262:                          "see your system administrator.");
 263:                  }
 264:                  return RedirectToAction(nameof(Index));
 265:              }
 266:              return View(instructorToUpdate);
 267:          }
 268:          #endregion
 269:   
 270:  #elif EditCourses
 271:          #region snippet_EditPostCourses
 272:          [HttpPost]
 273:          [ValidateAntiForgeryToken]
 274:          public async Task<IActionResult> Edit(int? id, string[] selectedCourses)
 275:          {
 276:              if (id == null)
 277:              {
 278:                  return NotFound();
 279:              }
 280:   
 281:              var instructorToUpdate = await _context.Instructors
 282:                  .Include(i => i.OfficeAssignment)
 283:                  .Include(i => i.CourseAssignments)
 284:                      .ThenInclude(i => i.Course)
 285:                  .SingleOrDefaultAsync(m => m.ID == id);
 286:   
 287:              if (await TryUpdateModelAsync<Instructor>(
 288:                  instructorToUpdate,
 289:                  "",
 290:                  i => i.FirstMidName, i => i.LastName, i => i.HireDate, i => i.OfficeAssignment))
 291:              {
 292:                  if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment?.Location))
 293:                  {
 294:                      instructorToUpdate.OfficeAssignment = null;
 295:                  }
 296:                  UpdateInstructorCourses(selectedCourses, instructorToUpdate);
 297:                  try
 298:                  {
 299:                      await _context.SaveChangesAsync();
 300:                  }
 301:                  catch (DbUpdateException /* ex */)
 302:                  {
 303:                      //Log the error (uncomment ex variable name and write a log.)
 304:                      ModelState.AddModelError("", "Unable to save changes. " +
 305:                          "Try again, and if the problem persists, " +
 306:                          "see your system administrator.");
 307:                  }
 308:                  return RedirectToAction(nameof(Index));
 309:              }
 310:              UpdateInstructorCourses(selectedCourses, instructorToUpdate);
 311:              PopulateAssignedCourseData(instructorToUpdate);
 312:              return View(instructorToUpdate);
 313:          }
 314:          #endregion
 315:   
 316:          #region snippet_UpdateCourses
 317:          private void UpdateInstructorCourses(string[] selectedCourses, Instructor instructorToUpdate)
 318:          {
 319:              if (selectedCourses == null)
 320:              {
 321:                  instructorToUpdate.CourseAssignments = new List<CourseAssignment>();
 322:                  return;
 323:              }
 324:   
 325:              var selectedCoursesHS = new HashSet<string>(selectedCourses);
 326:              var instructorCourses = new HashSet<int>
 327:                  (instructorToUpdate.CourseAssignments.Select(c => c.Course.CourseID));
 328:              foreach (var course in _context.Courses)
 329:              {
 330:                  if (selectedCoursesHS.Contains(course.CourseID.ToString()))
 331:                  {
 332:                      if (!instructorCourses.Contains(course.CourseID))
 333:                      {
 334:                          instructorToUpdate.CourseAssignments.Add(new CourseAssignment { InstructorID = instructorToUpdate.ID, CourseID = course.CourseID });
 335:                      }
 336:                  }
 337:                  else
 338:                  {
 339:   
 340:                      if (instructorCourses.Contains(course.CourseID))
 341:                      {
 342:                          CourseAssignment courseToRemove = instructorToUpdate.CourseAssignments.SingleOrDefault(i => i.CourseID == course.CourseID);
 343:                          _context.Remove(courseToRemove);
 344:                      }
 345:                  }
 346:              }
 347:          }
 348:          #endregion
 349:  #endif
 350:          // GET: Instructors/Delete/5
 351:          public async Task<IActionResult> Delete(int? id)
 352:          {
 353:              if (id == null)
 354:              {
 355:                  return NotFound();
 356:              }
 357:   
 358:              var instructor = await _context.Instructors
 359:                  .AsNoTracking()
 360:                  .SingleOrDefaultAsync(m => m.ID == id);
 361:              if (instructor == null)
 362:              {
 363:                  return NotFound();
 364:              }
 365:   
 366:              return View(instructor);
 367:          }
 368:   
 369:          #region snippet_DeleteConfirmed
 370:          [HttpPost, ActionName("Delete")]
 371:          [ValidateAntiForgeryToken]
 372:          public async Task<IActionResult> DeleteConfirmed(int id)
 373:          {
 374:              Instructor instructor = await _context.Instructors
 375:                  .Include(i => i.CourseAssignments)
 376:                  .SingleAsync(i => i.ID == id);
 377:   
 378:              var departments = await _context.Departments
 379:                  .Where(d => d.InstructorID == id)
 380:                  .ToListAsync();
 381:              departments.ForEach(d => d.InstructorID = null);
 382:   
 383:              _context.Instructors.Remove(instructor);
 384:   
 385:              await _context.SaveChangesAsync();
 386:              return RedirectToAction(nameof(Index));
 387:          }
 388:          #endregion
 389:      }
 390:  }

The code does the following:

Update the Instructor Edit view

In Views/Instructors/Edit.cshtml, add a new field for editing the office location, at the end before the Save button:

[!code-htmlMain]

   1:  @model ContosoUniversity.Models.Instructor
   2:   
   3:  @{
   4:      ViewData["Title"] = "Edit";
   5:  }
   6:   
   7:  <h2>Edit</h2>
   8:   
   9:  <form asp-action="Edit">
  10:      <div class="form-horizontal">
  11:          <h4>Instructor</h4>
  12:          <hr />
  13:          <div asp-validation-summary="ModelOnly" class="text-danger"></div>
  14:          <input type="hidden" asp-for="ID" />
  15:          <div class="form-group">
  16:              <label asp-for="LastName" class="control-label"></label>
  17:              <input asp-for="LastName" class="form-control" />
  18:              <span asp-validation-for="LastName" class="text-danger"></span>
  19:          </div>
  20:          <div class="form-group">
  21:              <label asp-for="FirstMidName" class="control-label"></label>
  22:              <input asp-for="FirstMidName" class="form-control" />
  23:              <span asp-validation-for="FirstMidName" class="text-danger"></span>
  24:          </div>
  25:          <div class="form-group">
  26:              <label asp-for="HireDate" class="control-label"></label>
  27:              <input asp-for="HireDate" class="form-control" />
  28:              <span asp-validation-for="HireDate" class="text-danger"></span>
  29:          </div>
  30:          <div class="form-group">
  31:              <label asp-for="OfficeAssignment.Location" class="control-label"></label>
  32:              <input asp-for="OfficeAssignment.Location" class="form-control" />
  33:              <span asp-validation-for="OfficeAssignment.Location" class="text-danger" />
  34:          </div>
  35:          <div class="form-group">
  36:              <div class="col-md-offset-2 col-md-10">
  37:                  <table>
  38:                      <tr>
  39:                          @{
  40:                              int cnt = 0;
  41:                              List<ContosoUniversity.Models.SchoolViewModels.AssignedCourseData> courses = ViewBag.Courses;
  42:   
  43:                              foreach (var course in courses)
  44:                              {
  45:                                  if (cnt++ % 3 == 0)
  46:                                  {
  47:                                      @:</tr><tr>
  48:                                  }
  49:                                  @:<td>
  50:                                      <input type="checkbox"
  51:                                             name="selectedCourses"
  52:                                             value="@course.CourseID"
  53:                                             @(Html.Raw(course.Assigned ? "checked=\"checked\"" : "")) />
  54:                                             @course.CourseID @:  @course.Title
  55:                                  @:</td>
  56:                              }
  57:                              @:</tr>
  58:                          }
  59:                  </table>
  60:              </div>
  61:          </div>
  62:   
  63:          <div class="form-group">
  64:              <input type="submit" value="Save" class="btn btn-default" />
  65:          </div>
  66:      </div>
  67:  </form>
  68:   
  69:  <div>
  70:      <a asp-action="Index">Back to List</a>
  71:  </div>
  72:   
  73:  @section Scripts {
  74:      @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
  75:  }

Run the app, select the Instructors tab, and then click Edit on an instructor. Change the Office Location and click Save.

Instructor Edit page
Instructor Edit page

Add Course assignments to the Instructor Edit page

Instructors may teach any number of courses. Now you’ll enhance the Instructor Edit page by adding the ability to change course assignments using a group of check boxes, as shown in the following screen shot:

Instructor Edit page with courses
Instructor Edit page with courses

The relationship between the Course and Instructor entities is many-to-many. To add and remove relationships, you add and remove entities to and from the CourseAssignments join entity set.

The UI that enables you to change which courses an instructor is assigned to is a group of check boxes. A check box for every course in the database is displayed, and the ones that the instructor is currently assigned to are selected. The user can select or clear check boxes to change course assignments. If the number of courses were much greater, you would probably want to use a different method of presenting the data in the view, but you’d use the same method of manipulating a join entity to create or delete relationships.

Update the Instructors controller

To provide data to the view for the list of check boxes, you’ll use a view model class.

Create AssignedCourseData.cs in the SchoolViewModels folder and replace the existing code with the following code:

[!code-csharpMain]

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Linq;
   4:  using System.Threading.Tasks;
   5:   
   6:  namespace ContosoUniversity.Models.SchoolViewModels
   7:  {
   8:      public class AssignedCourseData
   9:      {
  10:          public int CourseID { get; set; }
  11:          public string Title { get; set; }
  12:          public bool Assigned { get; set; }
  13:      }
  14:  }

In InstructorsController.cs, replace the HttpGet Edit method with the following code. The changes are highlighted.

[!code-csharpMain]

   1:  #define ExplicitLoading // or EagerLoading or ScaffoldedCode
   2:  #define EditCourses // or EditOfficeAssignment
   3:   
   4:  using System;
   5:  using System.Collections.Generic;
   6:  using System.Linq;
   7:  using System.Threading.Tasks;
   8:  using Microsoft.AspNetCore.Mvc;
   9:  using Microsoft.AspNetCore.Mvc.Rendering;
  10:  using Microsoft.EntityFrameworkCore;
  11:  using ContosoUniversity.Data;
  12:  using ContosoUniversity.Models;
  13:  #region snippet_Using
  14:  using ContosoUniversity.Models.SchoolViewModels;
  15:  #endregion
  16:   
  17:  namespace ContosoUniversity.Controllers
  18:  {
  19:      public class InstructorsController : Controller
  20:      {
  21:          private readonly SchoolContext _context;
  22:   
  23:          public InstructorsController(SchoolContext context)
  24:          {
  25:              _context = context;
  26:          }
  27:   
  28:          // GET: Instructors
  29:  #if ScaffoldedCode
  30:          #region snippet_ScaffoldedCode
  31:          public async Task<IActionResult> Index()
  32:          {
  33:              return View(await _context.Instructors.AsNoTracking().ToListAsync());
  34:          }
  35:          #endregion
  36:  #elif EagerLoading
  37:          #region snippet_EagerLoading
  38:          public async Task<IActionResult> Index(int? id, int? courseID)
  39:          {
  40:              var viewModel = new InstructorIndexData();
  41:              #region snippet_ThenInclude
  42:              viewModel.Instructors = await _context.Instructors
  43:                    .Include(i => i.OfficeAssignment)
  44:                    .Include(i => i.CourseAssignments)
  45:                      .ThenInclude(i => i.Course)
  46:                          .ThenInclude(i => i.Enrollments)
  47:                              .ThenInclude(i => i.Student)
  48:                    .Include(i => i.CourseAssignments)
  49:                      .ThenInclude(i => i.Course)
  50:                          .ThenInclude(i => i.Department)
  51:                    .AsNoTracking()
  52:                    .OrderBy(i => i.LastName)
  53:                    .ToListAsync();
  54:              #endregion
  55:              
  56:              if (id != null)
  57:              {
  58:                  ViewData["InstructorID"] = id.Value;
  59:                  Instructor instructor = viewModel.Instructors.Where(
  60:                      i => i.ID == id.Value).Single();
  61:                  viewModel.Courses = instructor.CourseAssignments.Select(s => s.Course);
  62:              }
  63:   
  64:              if (courseID != null)
  65:              {
  66:                  ViewData["CourseID"] = courseID.Value;
  67:                  viewModel.Enrollments = viewModel.Courses.Where(
  68:                      x => x.CourseID == courseID).Single().Enrollments;
  69:              }
  70:   
  71:              return View(viewModel);
  72:          }
  73:          #endregion
  74:  #elif ExplicitLoading
  75:          #region snippet_ExplicitLoading
  76:          public async Task<IActionResult> Index(int? id, int? courseID)
  77:          {
  78:              var viewModel = new InstructorIndexData();
  79:              viewModel.Instructors = await _context.Instructors
  80:                    .Include(i => i.OfficeAssignment)
  81:                    .Include(i => i.CourseAssignments)
  82:                      .ThenInclude(i => i.Course)
  83:                          .ThenInclude(i => i.Department)
  84:                    .OrderBy(i => i.LastName)
  85:                    .ToListAsync();
  86:   
  87:              if (id != null)
  88:              {
  89:                  ViewData["InstructorID"] = id.Value;
  90:                  Instructor instructor = viewModel.Instructors.Where(
  91:                      i => i.ID == id.Value).Single();
  92:                  viewModel.Courses = instructor.CourseAssignments.Select(s => s.Course);
  93:              }
  94:   
  95:              if (courseID != null)
  96:              {
  97:                  ViewData["CourseID"] = courseID.Value;
  98:                  var selectedCourse = viewModel.Courses.Where(x => x.CourseID == courseID).Single();
  99:                  await _context.Entry(selectedCourse).Collection(x => x.Enrollments).LoadAsync();
 100:                  foreach (Enrollment enrollment in selectedCourse.Enrollments)
 101:                  {
 102:                      await _context.Entry(enrollment).Reference(x => x.Student).LoadAsync();
 103:                  }
 104:                  viewModel.Enrollments = selectedCourse.Enrollments;
 105:              }
 106:   
 107:              return View(viewModel);
 108:          }
 109:          #endregion
 110:  #endif
 111:          // GET: Instructors/Details/5
 112:          public async Task<IActionResult> Details(int? id)
 113:          {
 114:              if (id == null)
 115:              {
 116:                  return NotFound();
 117:              }
 118:   
 119:              var instructor = await _context.Instructors
 120:                  .AsNoTracking()
 121:                  .SingleOrDefaultAsync(m => m.ID == id);
 122:              if (instructor == null)
 123:              {
 124:                  return NotFound();
 125:              }
 126:   
 127:              return View(instructor);
 128:          }
 129:   
 130:          // GET: Instructors/Create
 131:          #region snippet_Create
 132:          public IActionResult Create()
 133:          {
 134:              var instructor = new Instructor();
 135:              instructor.CourseAssignments = new List<CourseAssignment>();
 136:              PopulateAssignedCourseData(instructor);
 137:              return View();
 138:          }
 139:   
 140:          // POST: Instructors/Create
 141:          [HttpPost]
 142:          [ValidateAntiForgeryToken]
 143:          public async Task<IActionResult> Create([Bind("FirstMidName,HireDate,LastName,OfficeAssignment")] Instructor instructor, string[] selectedCourses)
 144:          {
 145:              if (selectedCourses != null)
 146:              {
 147:                  instructor.CourseAssignments = new List<CourseAssignment>();
 148:                  foreach (var course in selectedCourses)
 149:                  {
 150:                      var courseToAdd = new CourseAssignment { InstructorID = instructor.ID, CourseID = int.Parse(course) };
 151:                      instructor.CourseAssignments.Add(courseToAdd);
 152:                  }
 153:              }
 154:              if (ModelState.IsValid)
 155:              {
 156:                  _context.Add(instructor);
 157:                  await _context.SaveChangesAsync();
 158:                  return RedirectToAction(nameof(Index));
 159:              }
 160:              PopulateAssignedCourseData(instructor);
 161:              return View(instructor);
 162:          }
 163:          #endregion
 164:   
 165:          // GET: Instructors/Edit/5
 166:  #if EditOfficeAssignment
 167:          #region snippet_EditGetOA
 168:          public async Task<IActionResult> Edit(int? id)
 169:          {
 170:              if (id == null)
 171:              {
 172:                  return NotFound();
 173:              }
 174:   
 175:              var instructor = await _context.Instructors
 176:                  .Include(i => i.OfficeAssignment)
 177:                  .AsNoTracking()
 178:                  .SingleOrDefaultAsync(m => m.ID == id);
 179:              if (instructor == null)
 180:              {
 181:                  return NotFound();
 182:              }
 183:              return View(instructor);
 184:          }
 185:          #endregion
 186:  #elif EditCourses
 187:          #region snippet_EditGetCourses
 188:          public async Task<IActionResult> Edit(int? id)
 189:          {
 190:              if (id == null)
 191:              {
 192:                  return NotFound();
 193:              }
 194:   
 195:              var instructor = await _context.Instructors
 196:                  .Include(i => i.OfficeAssignment)
 197:                  .Include(i => i.CourseAssignments).ThenInclude(i => i.Course)
 198:                  .AsNoTracking()
 199:                  .SingleOrDefaultAsync(m => m.ID == id);
 200:              if (instructor == null)
 201:              {
 202:                  return NotFound();
 203:              }
 204:              PopulateAssignedCourseData(instructor);
 205:              return View(instructor);
 206:          }
 207:   
 208:          private void PopulateAssignedCourseData(Instructor instructor)
 209:          {
 210:              var allCourses = _context.Courses;
 211:              var instructorCourses = new HashSet<int>(instructor.CourseAssignments.Select(c => c.CourseID));
 212:              var viewModel = new List<AssignedCourseData>();
 213:              foreach (var course in allCourses)
 214:              {
 215:                  viewModel.Add(new AssignedCourseData
 216:                  {
 217:                      CourseID = course.CourseID,
 218:                      Title = course.Title,
 219:                      Assigned = instructorCourses.Contains(course.CourseID)
 220:                  });
 221:              }
 222:              ViewData["Courses"] = viewModel;
 223:          }
 224:          #endregion
 225:  #endif
 226:          // POST: Instructors/Edit/5
 227:          // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
 228:          // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
 229:  #if EditOfficeAssignment
 230:          #region snippet_EditPostOA
 231:          [HttpPost, ActionName("Edit")]
 232:          [ValidateAntiForgeryToken]
 233:          public async Task<IActionResult> EditPost(int? id)
 234:          {
 235:              if (id == null)
 236:              {
 237:                  return NotFound();
 238:              }
 239:   
 240:              var instructorToUpdate = await _context.Instructors
 241:                  .Include(i => i.OfficeAssignment)
 242:                  .SingleOrDefaultAsync(s => s.ID == id);
 243:   
 244:              if (await TryUpdateModelAsync<Instructor>(
 245:                  instructorToUpdate,
 246:                  "",
 247:                  i => i.FirstMidName, i => i.LastName, i => i.HireDate, i => i.OfficeAssignment))
 248:              {
 249:                  if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment?.Location))
 250:                  {
 251:                      instructorToUpdate.OfficeAssignment = null;
 252:                  }
 253:                  try
 254:                  {
 255:                      await _context.SaveChangesAsync();
 256:                  }
 257:                  catch (DbUpdateException /* ex */)
 258:                  {
 259:                      //Log the error (uncomment ex variable name and write a log.)
 260:                      ModelState.AddModelError("", "Unable to save changes. " +
 261:                          "Try again, and if the problem persists, " +
 262:                          "see your system administrator.");
 263:                  }
 264:                  return RedirectToAction(nameof(Index));
 265:              }
 266:              return View(instructorToUpdate);
 267:          }
 268:          #endregion
 269:   
 270:  #elif EditCourses
 271:          #region snippet_EditPostCourses
 272:          [HttpPost]
 273:          [ValidateAntiForgeryToken]
 274:          public async Task<IActionResult> Edit(int? id, string[] selectedCourses)
 275:          {
 276:              if (id == null)
 277:              {
 278:                  return NotFound();
 279:              }
 280:   
 281:              var instructorToUpdate = await _context.Instructors
 282:                  .Include(i => i.OfficeAssignment)
 283:                  .Include(i => i.CourseAssignments)
 284:                      .ThenInclude(i => i.Course)
 285:                  .SingleOrDefaultAsync(m => m.ID == id);
 286:   
 287:              if (await TryUpdateModelAsync<Instructor>(
 288:                  instructorToUpdate,
 289:                  "",
 290:                  i => i.FirstMidName, i => i.LastName, i => i.HireDate, i => i.OfficeAssignment))
 291:              {
 292:                  if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment?.Location))
 293:                  {
 294:                      instructorToUpdate.OfficeAssignment = null;
 295:                  }
 296:                  UpdateInstructorCourses(selectedCourses, instructorToUpdate);
 297:                  try
 298:                  {
 299:                      await _context.SaveChangesAsync();
 300:                  }
 301:                  catch (DbUpdateException /* ex */)
 302:                  {
 303:                      //Log the error (uncomment ex variable name and write a log.)
 304:                      ModelState.AddModelError("", "Unable to save changes. " +
 305:                          "Try again, and if the problem persists, " +
 306:                          "see your system administrator.");
 307:                  }
 308:                  return RedirectToAction(nameof(Index));
 309:              }
 310:              UpdateInstructorCourses(selectedCourses, instructorToUpdate);
 311:              PopulateAssignedCourseData(instructorToUpdate);
 312:              return View(instructorToUpdate);
 313:          }
 314:          #endregion
 315:   
 316:          #region snippet_UpdateCourses
 317:          private void UpdateInstructorCourses(string[] selectedCourses, Instructor instructorToUpdate)
 318:          {
 319:              if (selectedCourses == null)
 320:              {
 321:                  instructorToUpdate.CourseAssignments = new List<CourseAssignment>();
 322:                  return;
 323:              }
 324:   
 325:              var selectedCoursesHS = new HashSet<string>(selectedCourses);
 326:              var instructorCourses = new HashSet<int>
 327:                  (instructorToUpdate.CourseAssignments.Select(c => c.Course.CourseID));
 328:              foreach (var course in _context.Courses)
 329:              {
 330:                  if (selectedCoursesHS.Contains(course.CourseID.ToString()))
 331:                  {
 332:                      if (!instructorCourses.Contains(course.CourseID))
 333:                      {
 334:                          instructorToUpdate.CourseAssignments.Add(new CourseAssignment { InstructorID = instructorToUpdate.ID, CourseID = course.CourseID });
 335:                      }
 336:                  }
 337:                  else
 338:                  {
 339:   
 340:                      if (instructorCourses.Contains(course.CourseID))
 341:                      {
 342:                          CourseAssignment courseToRemove = instructorToUpdate.CourseAssignments.SingleOrDefault(i => i.CourseID == course.CourseID);
 343:                          _context.Remove(courseToRemove);
 344:                      }
 345:                  }
 346:              }
 347:          }
 348:          #endregion
 349:  #endif
 350:          // GET: Instructors/Delete/5
 351:          public async Task<IActionResult> Delete(int? id)
 352:          {
 353:              if (id == null)
 354:              {
 355:                  return NotFound();
 356:              }
 357:   
 358:              var instructor = await _context.Instructors
 359:                  .AsNoTracking()
 360:                  .SingleOrDefaultAsync(m => m.ID == id);
 361:              if (instructor == null)
 362:              {
 363:                  return NotFound();
 364:              }
 365:   
 366:              return View(instructor);
 367:          }
 368:   
 369:          #region snippet_DeleteConfirmed
 370:          [HttpPost, ActionName("Delete")]
 371:          [ValidateAntiForgeryToken]
 372:          public async Task<IActionResult> DeleteConfirmed(int id)
 373:          {
 374:              Instructor instructor = await _context.Instructors
 375:                  .Include(i => i.CourseAssignments)
 376:                  .SingleAsync(i => i.ID == id);
 377:   
 378:              var departments = await _context.Departments
 379:                  .Where(d => d.InstructorID == id)
 380:                  .ToListAsync();
 381:              departments.ForEach(d => d.InstructorID = null);
 382:   
 383:              _context.Instructors.Remove(instructor);
 384:   
 385:              await _context.SaveChangesAsync();
 386:              return RedirectToAction(nameof(Index));
 387:          }
 388:          #endregion
 389:      }
 390:  }

The code adds eager loading for the Courses navigation property and calls the new PopulateAssignedCourseData method to provide information for the check box array using the AssignedCourseData view model class.

The code in the PopulateAssignedCourseData method reads through all Course entities in order to load a list of courses using the view model class. For each course, the code checks whether the course exists in the instructor’s Courses navigation property. To create efficient lookup when checking whether a course is assigned to the instructor, the courses assigned to the instructor are put into a HashSet collection. The Assigned property is set to true for courses the instructor is assigned to. The view will use this property to determine which check boxes must be displayed as selected. Finally, the list is passed to the view in ViewData.

Next, add the code that’s executed when the user clicks Save. Replace the EditPost method with the following code, and add a new method that updates the Courses navigation property of the Instructor entity.

[!code-csharpMain]

   1:  #define ExplicitLoading // or EagerLoading or ScaffoldedCode
   2:  #define EditCourses // or EditOfficeAssignment
   3:   
   4:  using System;
   5:  using System.Collections.Generic;
   6:  using System.Linq;
   7:  using System.Threading.Tasks;
   8:  using Microsoft.AspNetCore.Mvc;
   9:  using Microsoft.AspNetCore.Mvc.Rendering;
  10:  using Microsoft.EntityFrameworkCore;
  11:  using ContosoUniversity.Data;
  12:  using ContosoUniversity.Models;
  13:  #region snippet_Using
  14:  using ContosoUniversity.Models.SchoolViewModels;
  15:  #endregion
  16:   
  17:  namespace ContosoUniversity.Controllers
  18:  {
  19:      public class InstructorsController : Controller
  20:      {
  21:          private readonly SchoolContext _context;
  22:   
  23:          public InstructorsController(SchoolContext context)
  24:          {
  25:              _context = context;
  26:          }
  27:   
  28:          // GET: Instructors
  29:  #if ScaffoldedCode
  30:          #region snippet_ScaffoldedCode
  31:          public async Task<IActionResult> Index()
  32:          {
  33:              return View(await _context.Instructors.AsNoTracking().ToListAsync());
  34:          }
  35:          #endregion
  36:  #elif EagerLoading
  37:          #region snippet_EagerLoading
  38:          public async Task<IActionResult> Index(int? id, int? courseID)
  39:          {
  40:              var viewModel = new InstructorIndexData();
  41:              #region snippet_ThenInclude
  42:              viewModel.Instructors = await _context.Instructors
  43:                    .Include(i => i.OfficeAssignment)
  44:                    .Include(i => i.CourseAssignments)
  45:                      .ThenInclude(i => i.Course)
  46:                          .ThenInclude(i => i.Enrollments)
  47:                              .ThenInclude(i => i.Student)
  48:                    .Include(i => i.CourseAssignments)
  49:                      .ThenInclude(i => i.Course)
  50:                          .ThenInclude(i => i.Department)
  51:                    .AsNoTracking()
  52:                    .OrderBy(i => i.LastName)
  53:                    .ToListAsync();
  54:              #endregion
  55:              
  56:              if (id != null)
  57:              {
  58:                  ViewData["InstructorID"] = id.Value;
  59:                  Instructor instructor = viewModel.Instructors.Where(
  60:                      i => i.ID == id.Value).Single();
  61:                  viewModel.Courses = instructor.CourseAssignments.Select(s => s.Course);
  62:              }
  63:   
  64:              if (courseID != null)
  65:              {
  66:                  ViewData["CourseID"] = courseID.Value;
  67:                  viewModel.Enrollments = viewModel.Courses.Where(
  68:                      x => x.CourseID == courseID).Single().Enrollments;
  69:              }
  70:   
  71:              return View(viewModel);
  72:          }
  73:          #endregion
  74:  #elif ExplicitLoading
  75:          #region snippet_ExplicitLoading
  76:          public async Task<IActionResult> Index(int? id, int? courseID)
  77:          {
  78:              var viewModel = new InstructorIndexData();
  79:              viewModel.Instructors = await _context.Instructors
  80:                    .Include(i => i.OfficeAssignment)
  81:                    .Include(i => i.CourseAssignments)
  82:                      .ThenInclude(i => i.Course)
  83:                          .ThenInclude(i => i.Department)
  84:                    .OrderBy(i => i.LastName)
  85:                    .ToListAsync();
  86:   
  87:              if (id != null)
  88:              {
  89:                  ViewData["InstructorID"] = id.Value;
  90:                  Instructor instructor = viewModel.Instructors.Where(
  91:                      i => i.ID == id.Value).Single();
  92:                  viewModel.Courses = instructor.CourseAssignments.Select(s => s.Course);
  93:              }
  94:   
  95:              if (courseID != null)
  96:              {
  97:                  ViewData["CourseID"] = courseID.Value;
  98:                  var selectedCourse = viewModel.Courses.Where(x => x.CourseID == courseID).Single();
  99:                  await _context.Entry(selectedCourse).Collection(x => x.Enrollments).LoadAsync();
 100:                  foreach (Enrollment enrollment in selectedCourse.Enrollments)
 101:                  {
 102:                      await _context.Entry(enrollment).Reference(x => x.Student).LoadAsync();
 103:                  }
 104:                  viewModel.Enrollments = selectedCourse.Enrollments;
 105:              }
 106:   
 107:              return View(viewModel);
 108:          }
 109:          #endregion
 110:  #endif
 111:          // GET: Instructors/Details/5
 112:          public async Task<IActionResult> Details(int? id)
 113:          {
 114:              if (id == null)
 115:              {
 116:                  return NotFound();
 117:              }
 118:   
 119:              var instructor = await _context.Instructors
 120:                  .AsNoTracking()
 121:                  .SingleOrDefaultAsync(m => m.ID == id);
 122:              if (instructor == null)
 123:              {
 124:                  return NotFound();
 125:              }
 126:   
 127:              return View(instructor);
 128:          }
 129:   
 130:          // GET: Instructors/Create
 131:          #region snippet_Create
 132:          public IActionResult Create()
 133:          {
 134:              var instructor = new Instructor();
 135:              instructor.CourseAssignments = new List<CourseAssignment>();
 136:              PopulateAssignedCourseData(instructor);
 137:              return View();
 138:          }
 139:   
 140:          // POST: Instructors/Create
 141:          [HttpPost]
 142:          [ValidateAntiForgeryToken]
 143:          public async Task<IActionResult> Create([Bind("FirstMidName,HireDate,LastName,OfficeAssignment")] Instructor instructor, string[] selectedCourses)
 144:          {
 145:              if (selectedCourses != null)
 146:              {
 147:                  instructor.CourseAssignments = new List<CourseAssignment>();
 148:                  foreach (var course in selectedCourses)
 149:                  {
 150:                      var courseToAdd = new CourseAssignment { InstructorID = instructor.ID, CourseID = int.Parse(course) };
 151:                      instructor.CourseAssignments.Add(courseToAdd);
 152:                  }
 153:              }
 154:              if (ModelState.IsValid)
 155:              {
 156:                  _context.Add(instructor);
 157:                  await _context.SaveChangesAsync();
 158:                  return RedirectToAction(nameof(Index));
 159:              }
 160:              PopulateAssignedCourseData(instructor);
 161:              return View(instructor);
 162:          }
 163:          #endregion
 164:   
 165:          // GET: Instructors/Edit/5
 166:  #if EditOfficeAssignment
 167:          #region snippet_EditGetOA
 168:          public async Task<IActionResult> Edit(int? id)
 169:          {
 170:              if (id == null)
 171:              {
 172:                  return NotFound();
 173:              }
 174:   
 175:              var instructor = await _context.Instructors
 176:                  .Include(i => i.OfficeAssignment)
 177:                  .AsNoTracking()
 178:                  .SingleOrDefaultAsync(m => m.ID == id);
 179:              if (instructor == null)
 180:              {
 181:                  return NotFound();
 182:              }
 183:              return View(instructor);
 184:          }
 185:          #endregion
 186:  #elif EditCourses
 187:          #region snippet_EditGetCourses
 188:          public async Task<IActionResult> Edit(int? id)
 189:          {
 190:              if (id == null)
 191:              {
 192:                  return NotFound();
 193:              }
 194:   
 195:              var instructor = await _context.Instructors
 196:                  .Include(i => i.OfficeAssignment)
 197:                  .Include(i => i.CourseAssignments).ThenInclude(i => i.Course)
 198:                  .AsNoTracking()
 199:                  .SingleOrDefaultAsync(m => m.ID == id);
 200:              if (instructor == null)
 201:              {
 202:                  return NotFound();
 203:              }
 204:              PopulateAssignedCourseData(instructor);
 205:              return View(instructor);
 206:          }
 207:   
 208:          private void PopulateAssignedCourseData(Instructor instructor)
 209:          {
 210:              var allCourses = _context.Courses;
 211:              var instructorCourses = new HashSet<int>(instructor.CourseAssignments.Select(c => c.CourseID));
 212:              var viewModel = new List<AssignedCourseData>();
 213:              foreach (var course in allCourses)
 214:              {
 215:                  viewModel.Add(new AssignedCourseData
 216:                  {
 217:                      CourseID = course.CourseID,
 218:                      Title = course.Title,
 219:                      Assigned = instructorCourses.Contains(course.CourseID)
 220:                  });
 221:              }
 222:              ViewData["Courses"] = viewModel;
 223:          }
 224:          #endregion
 225:  #endif
 226:          // POST: Instructors/Edit/5
 227:          // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
 228:          // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
 229:  #if EditOfficeAssignment
 230:          #region snippet_EditPostOA
 231:          [HttpPost, ActionName("Edit")]
 232:          [ValidateAntiForgeryToken]
 233:          public async Task<IActionResult> EditPost(int? id)
 234:          {
 235:              if (id == null)
 236:              {
 237:                  return NotFound();
 238:              }
 239:   
 240:              var instructorToUpdate = await _context.Instructors
 241:                  .Include(i => i.OfficeAssignment)
 242:                  .SingleOrDefaultAsync(s => s.ID == id);
 243:   
 244:              if (await TryUpdateModelAsync<Instructor>(
 245:                  instructorToUpdate,
 246:                  "",
 247:                  i => i.FirstMidName, i => i.LastName, i => i.HireDate, i => i.OfficeAssignment))
 248:              {
 249:                  if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment?.Location))
 250:                  {
 251:                      instructorToUpdate.OfficeAssignment = null;
 252:                  }
 253:                  try
 254:                  {
 255:                      await _context.SaveChangesAsync();
 256:                  }
 257:                  catch (DbUpdateException /* ex */)
 258:                  {
 259:                      //Log the error (uncomment ex variable name and write a log.)
 260:                      ModelState.AddModelError("", "Unable to save changes. " +
 261:                          "Try again, and if the problem persists, " +
 262:                          "see your system administrator.");
 263:                  }
 264:                  return RedirectToAction(nameof(Index));
 265:              }
 266:              return View(instructorToUpdate);
 267:          }
 268:          #endregion
 269:   
 270:  #elif EditCourses
 271:          #region snippet_EditPostCourses
 272:          [HttpPost]
 273:          [ValidateAntiForgeryToken]
 274:          public async Task<IActionResult> Edit(int? id, string[] selectedCourses)
 275:          {
 276:              if (id == null)
 277:              {
 278:                  return NotFound();
 279:              }
 280:   
 281:              var instructorToUpdate = await _context.Instructors
 282:                  .Include(i => i.OfficeAssignment)
 283:                  .Include(i => i.CourseAssignments)
 284:                      .ThenInclude(i => i.Course)
 285:                  .SingleOrDefaultAsync(m => m.ID == id);
 286:   
 287:              if (await TryUpdateModelAsync<Instructor>(
 288:                  instructorToUpdate,
 289:                  "",
 290:                  i => i.FirstMidName, i => i.LastName, i => i.HireDate, i => i.OfficeAssignment))
 291:              {
 292:                  if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment?.Location))
 293:                  {
 294:                      instructorToUpdate.OfficeAssignment = null;
 295:                  }
 296:                  UpdateInstructorCourses(selectedCourses, instructorToUpdate);
 297:                  try
 298:                  {
 299:                      await _context.SaveChangesAsync();
 300:                  }
 301:                  catch (DbUpdateException /* ex */)
 302:                  {
 303:                      //Log the error (uncomment ex variable name and write a log.)
 304:                      ModelState.AddModelError("", "Unable to save changes. " +
 305:                          "Try again, and if the problem persists, " +
 306:                          "see your system administrator.");
 307:                  }
 308:                  return RedirectToAction(nameof(Index));
 309:              }
 310:              UpdateInstructorCourses(selectedCourses, instructorToUpdate);
 311:              PopulateAssignedCourseData(instructorToUpdate);
 312:              return View(instructorToUpdate);
 313:          }
 314:          #endregion
 315:   
 316:          #region snippet_UpdateCourses
 317:          private void UpdateInstructorCourses(string[] selectedCourses, Instructor instructorToUpdate)
 318:          {
 319:              if (selectedCourses == null)
 320:              {
 321:                  instructorToUpdate.CourseAssignments = new List<CourseAssignment>();
 322:                  return;
 323:              }
 324:   
 325:              var selectedCoursesHS = new HashSet<string>(selectedCourses);
 326:              var instructorCourses = new HashSet<int>
 327:                  (instructorToUpdate.CourseAssignments.Select(c => c.Course.CourseID));
 328:              foreach (var course in _context.Courses)
 329:              {
 330:                  if (selectedCoursesHS.Contains(course.CourseID.ToString()))
 331:                  {
 332:                      if (!instructorCourses.Contains(course.CourseID))
 333:                      {
 334:                          instructorToUpdate.CourseAssignments.Add(new CourseAssignment { InstructorID = instructorToUpdate.ID, CourseID = course.CourseID });
 335:                      }
 336:                  }
 337:                  else
 338:                  {
 339:   
 340:                      if (instructorCourses.Contains(course.CourseID))
 341:                      {
 342:                          CourseAssignment courseToRemove = instructorToUpdate.CourseAssignments.SingleOrDefault(i => i.CourseID == course.CourseID);
 343:                          _context.Remove(courseToRemove);
 344:                      }
 345:                  }
 346:              }
 347:          }
 348:          #endregion
 349:  #endif
 350:          // GET: Instructors/Delete/5
 351:          public async Task<IActionResult> Delete(int? id)
 352:          {
 353:              if (id == null)
 354:              {
 355:                  return NotFound();
 356:              }
 357:   
 358:              var instructor = await _context.Instructors
 359:                  .AsNoTracking()
 360:                  .SingleOrDefaultAsync(m => m.ID == id);
 361:              if (instructor == null)
 362:              {
 363:                  return NotFound();
 364:              }
 365:   
 366:              return View(instructor);
 367:          }
 368:   
 369:          #region snippet_DeleteConfirmed
 370:          [HttpPost, ActionName("Delete")]
 371:          [ValidateAntiForgeryToken]
 372:          public async Task<IActionResult> DeleteConfirmed(int id)
 373:          {
 374:              Instructor instructor = await _context.Instructors
 375:                  .Include(i => i.CourseAssignments)
 376:                  .SingleAsync(i => i.ID == id);
 377:   
 378:              var departments = await _context.Departments
 379:                  .Where(d => d.InstructorID == id)
 380:                  .ToListAsync();
 381:              departments.ForEach(d => d.InstructorID = null);
 382:   
 383:              _context.Instructors.Remove(instructor);
 384:   
 385:              await _context.SaveChangesAsync();
 386:              return RedirectToAction(nameof(Index));
 387:          }
 388:          #endregion
 389:      }
 390:  }

[!code-csharpMain]

   1:  #define ExplicitLoading // or EagerLoading or ScaffoldedCode
   2:  #define EditCourses // or EditOfficeAssignment
   3:   
   4:  using System;
   5:  using System.Collections.Generic;
   6:  using System.Linq;
   7:  using System.Threading.Tasks;
   8:  using Microsoft.AspNetCore.Mvc;
   9:  using Microsoft.AspNetCore.Mvc.Rendering;
  10:  using Microsoft.EntityFrameworkCore;
  11:  using ContosoUniversity.Data;
  12:  using ContosoUniversity.Models;
  13:  #region snippet_Using
  14:  using ContosoUniversity.Models.SchoolViewModels;
  15:  #endregion
  16:   
  17:  namespace ContosoUniversity.Controllers
  18:  {
  19:      public class InstructorsController : Controller
  20:      {
  21:          private readonly SchoolContext _context;
  22:   
  23:          public InstructorsController(SchoolContext context)
  24:          {
  25:              _context = context;
  26:          }
  27:   
  28:          // GET: Instructors
  29:  #if ScaffoldedCode
  30:          #region snippet_ScaffoldedCode
  31:          public async Task<IActionResult> Index()
  32:          {
  33:              return View(await _context.Instructors.AsNoTracking().ToListAsync());
  34:          }
  35:          #endregion
  36:  #elif EagerLoading
  37:          #region snippet_EagerLoading
  38:          public async Task<IActionResult> Index(int? id, int? courseID)
  39:          {
  40:              var viewModel = new InstructorIndexData();
  41:              #region snippet_ThenInclude
  42:              viewModel.Instructors = await _context.Instructors
  43:                    .Include(i => i.OfficeAssignment)
  44:                    .Include(i => i.CourseAssignments)
  45:                      .ThenInclude(i => i.Course)
  46:                          .ThenInclude(i => i.Enrollments)
  47:                              .ThenInclude(i => i.Student)
  48:                    .Include(i => i.CourseAssignments)
  49:                      .ThenInclude(i => i.Course)
  50:                          .ThenInclude(i => i.Department)
  51:                    .AsNoTracking()
  52:                    .OrderBy(i => i.LastName)
  53:                    .ToListAsync();
  54:              #endregion
  55:              
  56:              if (id != null)
  57:              {
  58:                  ViewData["InstructorID"] = id.Value;
  59:                  Instructor instructor = viewModel.Instructors.Where(
  60:                      i => i.ID == id.Value).Single();
  61:                  viewModel.Courses = instructor.CourseAssignments.Select(s => s.Course);
  62:              }
  63:   
  64:              if (courseID != null)
  65:              {
  66:                  ViewData["CourseID"] = courseID.Value;
  67:                  viewModel.Enrollments = viewModel.Courses.Where(
  68:                      x => x.CourseID == courseID).Single().Enrollments;
  69:              }
  70:   
  71:              return View(viewModel);
  72:          }
  73:          #endregion
  74:  #elif ExplicitLoading
  75:          #region snippet_ExplicitLoading
  76:          public async Task<IActionResult> Index(int? id, int? courseID)
  77:          {
  78:              var viewModel = new InstructorIndexData();
  79:              viewModel.Instructors = await _context.Instructors
  80:                    .Include(i => i.OfficeAssignment)
  81:                    .Include(i => i.CourseAssignments)
  82:                      .ThenInclude(i => i.Course)
  83:                          .ThenInclude(i => i.Department)
  84:                    .OrderBy(i => i.LastName)
  85:                    .ToListAsync();
  86:   
  87:              if (id != null)
  88:              {
  89:                  ViewData["InstructorID"] = id.Value;
  90:                  Instructor instructor = viewModel.Instructors.Where(
  91:                      i => i.ID == id.Value).Single();
  92:                  viewModel.Courses = instructor.CourseAssignments.Select(s => s.Course);
  93:              }
  94:   
  95:              if (courseID != null)
  96:              {
  97:                  ViewData["CourseID"] = courseID.Value;
  98:                  var selectedCourse = viewModel.Courses.Where(x => x.CourseID == courseID).Single();
  99:                  await _context.Entry(selectedCourse).Collection(x => x.Enrollments).LoadAsync();
 100:                  foreach (Enrollment enrollment in selectedCourse.Enrollments)
 101:                  {
 102:                      await _context.Entry(enrollment).Reference(x => x.Student).LoadAsync();
 103:                  }
 104:                  viewModel.Enrollments = selectedCourse.Enrollments;
 105:              }
 106:   
 107:              return View(viewModel);
 108:          }
 109:          #endregion
 110:  #endif
 111:          // GET: Instructors/Details/5
 112:          public async Task<IActionResult> Details(int? id)
 113:          {
 114:              if (id == null)
 115:              {
 116:                  return NotFound();
 117:              }
 118:   
 119:              var instructor = await _context.Instructors
 120:                  .AsNoTracking()
 121:                  .SingleOrDefaultAsync(m => m.ID == id);
 122:              if (instructor == null)
 123:              {
 124:                  return NotFound();
 125:              }
 126:   
 127:              return View(instructor);
 128:          }
 129:   
 130:          // GET: Instructors/Create
 131:          #region snippet_Create
 132:          public IActionResult Create()
 133:          {
 134:              var instructor = new Instructor();
 135:              instructor.CourseAssignments = new List<CourseAssignment>();
 136:              PopulateAssignedCourseData(instructor);
 137:              return View();
 138:          }
 139:   
 140:          // POST: Instructors/Create
 141:          [HttpPost]
 142:          [ValidateAntiForgeryToken]
 143:          public async Task<IActionResult> Create([Bind("FirstMidName,HireDate,LastName,OfficeAssignment")] Instructor instructor, string[] selectedCourses)
 144:          {
 145:              if (selectedCourses != null)
 146:              {
 147:                  instructor.CourseAssignments = new List<CourseAssignment>();
 148:                  foreach (var course in selectedCourses)
 149:                  {
 150:                      var courseToAdd = new CourseAssignment { InstructorID = instructor.ID, CourseID = int.Parse(course) };
 151:                      instructor.CourseAssignments.Add(courseToAdd);
 152:                  }
 153:              }
 154:              if (ModelState.IsValid)
 155:              {
 156:                  _context.Add(instructor);
 157:                  await _context.SaveChangesAsync();
 158:                  return RedirectToAction(nameof(Index));
 159:              }
 160:              PopulateAssignedCourseData(instructor);
 161:              return View(instructor);
 162:          }
 163:          #endregion
 164:   
 165:          // GET: Instructors/Edit/5
 166:  #if EditOfficeAssignment
 167:          #region snippet_EditGetOA
 168:          public async Task<IActionResult> Edit(int? id)
 169:          {
 170:              if (id == null)
 171:              {
 172:                  return NotFound();
 173:              }
 174:   
 175:              var instructor = await _context.Instructors
 176:                  .Include(i => i.OfficeAssignment)
 177:                  .AsNoTracking()
 178:                  .SingleOrDefaultAsync(m => m.ID == id);
 179:              if (instructor == null)
 180:              {
 181:                  return NotFound();
 182:              }
 183:              return View(instructor);
 184:          }
 185:          #endregion
 186:  #elif EditCourses
 187:          #region snippet_EditGetCourses
 188:          public async Task<IActionResult> Edit(int? id)
 189:          {
 190:              if (id == null)
 191:              {
 192:                  return NotFound();
 193:              }
 194:   
 195:              var instructor = await _context.Instructors
 196:                  .Include(i => i.OfficeAssignment)
 197:                  .Include(i => i.CourseAssignments).ThenInclude(i => i.Course)
 198:                  .AsNoTracking()
 199:                  .SingleOrDefaultAsync(m => m.ID == id);
 200:              if (instructor == null)
 201:              {
 202:                  return NotFound();
 203:              }
 204:              PopulateAssignedCourseData(instructor);
 205:              return View(instructor);
 206:          }
 207:   
 208:          private void PopulateAssignedCourseData(Instructor instructor)
 209:          {
 210:              var allCourses = _context.Courses;
 211:              var instructorCourses = new HashSet<int>(instructor.CourseAssignments.Select(c => c.CourseID));
 212:              var viewModel = new List<AssignedCourseData>();
 213:              foreach (var course in allCourses)
 214:              {
 215:                  viewModel.Add(new AssignedCourseData
 216:                  {
 217:                      CourseID = course.CourseID,
 218:                      Title = course.Title,
 219:                      Assigned = instructorCourses.Contains(course.CourseID)
 220:                  });
 221:              }
 222:              ViewData["Courses"] = viewModel;
 223:          }
 224:          #endregion
 225:  #endif
 226:          // POST: Instructors/Edit/5
 227:          // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
 228:          // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
 229:  #if EditOfficeAssignment
 230:          #region snippet_EditPostOA
 231:          [HttpPost, ActionName("Edit")]
 232:          [ValidateAntiForgeryToken]
 233:          public async Task<IActionResult> EditPost(int? id)
 234:          {
 235:              if (id == null)
 236:              {
 237:                  return NotFound();
 238:              }
 239:   
 240:              var instructorToUpdate = await _context.Instructors
 241:                  .Include(i => i.OfficeAssignment)
 242:                  .SingleOrDefaultAsync(s => s.ID == id);
 243:   
 244:              if (await TryUpdateModelAsync<Instructor>(
 245:                  instructorToUpdate,
 246:                  "",
 247:                  i => i.FirstMidName, i => i.LastName, i => i.HireDate, i => i.OfficeAssignment))
 248:              {
 249:                  if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment?.Location))
 250:                  {
 251:                      instructorToUpdate.OfficeAssignment = null;
 252:                  }
 253:                  try
 254:                  {
 255:                      await _context.SaveChangesAsync();
 256:                  }
 257:                  catch (DbUpdateException /* ex */)
 258:                  {
 259:                      //Log the error (uncomment ex variable name and write a log.)
 260:                      ModelState.AddModelError("", "Unable to save changes. " +
 261:                          "Try again, and if the problem persists, " +
 262:                          "see your system administrator.");
 263:                  }
 264:                  return RedirectToAction(nameof(Index));
 265:              }
 266:              return View(instructorToUpdate);
 267:          }
 268:          #endregion
 269:   
 270:  #elif EditCourses
 271:          #region snippet_EditPostCourses
 272:          [HttpPost]
 273:          [ValidateAntiForgeryToken]
 274:          public async Task<IActionResult> Edit(int? id, string[] selectedCourses)
 275:          {
 276:              if (id == null)
 277:              {
 278:                  return NotFound();
 279:              }
 280:   
 281:              var instructorToUpdate = await _context.Instructors
 282:                  .Include(i => i.OfficeAssignment)
 283:                  .Include(i => i.CourseAssignments)
 284:                      .ThenInclude(i => i.Course)
 285:                  .SingleOrDefaultAsync(m => m.ID == id);
 286:   
 287:              if (await TryUpdateModelAsync<Instructor>(
 288:                  instructorToUpdate,
 289:                  "",
 290:                  i => i.FirstMidName, i => i.LastName, i => i.HireDate, i => i.OfficeAssignment))
 291:              {
 292:                  if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment?.Location))
 293:                  {
 294:                      instructorToUpdate.OfficeAssignment = null;
 295:                  }
 296:                  UpdateInstructorCourses(selectedCourses, instructorToUpdate);
 297:                  try
 298:                  {
 299:                      await _context.SaveChangesAsync();
 300:                  }
 301:                  catch (DbUpdateException /* ex */)
 302:                  {
 303:                      //Log the error (uncomment ex variable name and write a log.)
 304:                      ModelState.AddModelError("", "Unable to save changes. " +
 305:                          "Try again, and if the problem persists, " +
 306:                          "see your system administrator.");
 307:                  }
 308:                  return RedirectToAction(nameof(Index));
 309:              }
 310:              UpdateInstructorCourses(selectedCourses, instructorToUpdate);
 311:              PopulateAssignedCourseData(instructorToUpdate);
 312:              return View(instructorToUpdate);
 313:          }
 314:          #endregion
 315:   
 316:          #region snippet_UpdateCourses
 317:          private void UpdateInstructorCourses(string[] selectedCourses, Instructor instructorToUpdate)
 318:          {
 319:              if (selectedCourses == null)
 320:              {
 321:                  instructorToUpdate.CourseAssignments = new List<CourseAssignment>();
 322:                  return;
 323:              }
 324:   
 325:              var selectedCoursesHS = new HashSet<string>(selectedCourses);
 326:              var instructorCourses = new HashSet<int>
 327:                  (instructorToUpdate.CourseAssignments.Select(c => c.Course.CourseID));
 328:              foreach (var course in _context.Courses)
 329:              {
 330:                  if (selectedCoursesHS.Contains(course.CourseID.ToString()))
 331:                  {
 332:                      if (!instructorCourses.Contains(course.CourseID))
 333:                      {
 334:                          instructorToUpdate.CourseAssignments.Add(new CourseAssignment { InstructorID = instructorToUpdate.ID, CourseID = course.CourseID });
 335:                      }
 336:                  }
 337:                  else
 338:                  {
 339:   
 340:                      if (instructorCourses.Contains(course.CourseID))
 341:                      {
 342:                          CourseAssignment courseToRemove = instructorToUpdate.CourseAssignments.SingleOrDefault(i => i.CourseID == course.CourseID);
 343:                          _context.Remove(courseToRemove);
 344:                      }
 345:                  }
 346:              }
 347:          }
 348:          #endregion
 349:  #endif
 350:          // GET: Instructors/Delete/5
 351:          public async Task<IActionResult> Delete(int? id)
 352:          {
 353:              if (id == null)
 354:              {
 355:                  return NotFound();
 356:              }
 357:   
 358:              var instructor = await _context.Instructors
 359:                  .AsNoTracking()
 360:                  .SingleOrDefaultAsync(m => m.ID == id);
 361:              if (instructor == null)
 362:              {
 363:                  return NotFound();
 364:              }
 365:   
 366:              return View(instructor);
 367:          }
 368:   
 369:          #region snippet_DeleteConfirmed
 370:          [HttpPost, ActionName("Delete")]
 371:          [ValidateAntiForgeryToken]
 372:          public async Task<IActionResult> DeleteConfirmed(int id)
 373:          {
 374:              Instructor instructor = await _context.Instructors
 375:                  .Include(i => i.CourseAssignments)
 376:                  .SingleAsync(i => i.ID == id);
 377:   
 378:              var departments = await _context.Departments
 379:                  .Where(d => d.InstructorID == id)
 380:                  .ToListAsync();
 381:              departments.ForEach(d => d.InstructorID = null);
 382:   
 383:              _context.Instructors.Remove(instructor);
 384:   
 385:              await _context.SaveChangesAsync();
 386:              return RedirectToAction(nameof(Index));
 387:          }
 388:          #endregion
 389:      }
 390:  }

The method signature is now different from the HttpGet Edit method, so the method name changes from EditPost back to Edit.

Since the view doesn’t have a collection of Course entities, the model binder can’t automatically update the CourseAssignments navigation property. Instead of using the model binder to update the CourseAssignments navigation property, you do that in the new UpdateInstructorCourses method. Therefore you need to exclude the CourseAssignments property from model binding. This doesn’t require any change to the code that calls TryUpdateModel because you’re using the whitelisting overload and CourseAssignments isn’t in the include list.

If no check boxes were selected, the code in UpdateInstructorCourses initializes the CourseAssignments navigation property with an empty collection and returns:

[!code-csharpMain]

   1:  #define ExplicitLoading // or EagerLoading or ScaffoldedCode
   2:  #define EditCourses // or EditOfficeAssignment
   3:   
   4:  using System;
   5:  using System.Collections.Generic;
   6:  using System.Linq;
   7:  using System.Threading.Tasks;
   8:  using Microsoft.AspNetCore.Mvc;
   9:  using Microsoft.AspNetCore.Mvc.Rendering;
  10:  using Microsoft.EntityFrameworkCore;
  11:  using ContosoUniversity.Data;
  12:  using ContosoUniversity.Models;
  13:  #region snippet_Using
  14:  using ContosoUniversity.Models.SchoolViewModels;
  15:  #endregion
  16:   
  17:  namespace ContosoUniversity.Controllers
  18:  {
  19:      public class InstructorsController : Controller
  20:      {
  21:          private readonly SchoolContext _context;
  22:   
  23:          public InstructorsController(SchoolContext context)
  24:          {
  25:              _context = context;
  26:          }
  27:   
  28:          // GET: Instructors
  29:  #if ScaffoldedCode
  30:          #region snippet_ScaffoldedCode
  31:          public async Task<IActionResult> Index()
  32:          {
  33:              return View(await _context.Instructors.AsNoTracking().ToListAsync());
  34:          }
  35:          #endregion
  36:  #elif EagerLoading
  37:          #region snippet_EagerLoading
  38:          public async Task<IActionResult> Index(int? id, int? courseID)
  39:          {
  40:              var viewModel = new InstructorIndexData();
  41:              #region snippet_ThenInclude
  42:              viewModel.Instructors = await _context.Instructors
  43:                    .Include(i => i.OfficeAssignment)
  44:                    .Include(i => i.CourseAssignments)
  45:                      .ThenInclude(i => i.Course)
  46:                          .ThenInclude(i => i.Enrollments)
  47:                              .ThenInclude(i => i.Student)
  48:                    .Include(i => i.CourseAssignments)
  49:                      .ThenInclude(i => i.Course)
  50:                          .ThenInclude(i => i.Department)
  51:                    .AsNoTracking()
  52:                    .OrderBy(i => i.LastName)
  53:                    .ToListAsync();
  54:              #endregion
  55:              
  56:              if (id != null)
  57:              {
  58:                  ViewData["InstructorID"] = id.Value;
  59:                  Instructor instructor = viewModel.Instructors.Where(
  60:                      i => i.ID == id.Value).Single();
  61:                  viewModel.Courses = instructor.CourseAssignments.Select(s => s.Course);
  62:              }
  63:   
  64:              if (courseID != null)
  65:              {
  66:                  ViewData["CourseID"] = courseID.Value;
  67:                  viewModel.Enrollments = viewModel.Courses.Where(
  68:                      x => x.CourseID == courseID).Single().Enrollments;
  69:              }
  70:   
  71:              return View(viewModel);
  72:          }
  73:          #endregion
  74:  #elif ExplicitLoading
  75:          #region snippet_ExplicitLoading
  76:          public async Task<IActionResult> Index(int? id, int? courseID)
  77:          {
  78:              var viewModel = new InstructorIndexData();
  79:              viewModel.Instructors = await _context.Instructors
  80:                    .Include(i => i.OfficeAssignment)
  81:                    .Include(i => i.CourseAssignments)
  82:                      .ThenInclude(i => i.Course)
  83:                          .ThenInclude(i => i.Department)
  84:                    .OrderBy(i => i.LastName)
  85:                    .ToListAsync();
  86:   
  87:              if (id != null)
  88:              {
  89:                  ViewData["InstructorID"] = id.Value;
  90:                  Instructor instructor = viewModel.Instructors.Where(
  91:                      i => i.ID == id.Value).Single();
  92:                  viewModel.Courses = instructor.CourseAssignments.Select(s => s.Course);
  93:              }
  94:   
  95:              if (courseID != null)
  96:              {
  97:                  ViewData["CourseID"] = courseID.Value;
  98:                  var selectedCourse = viewModel.Courses.Where(x => x.CourseID == courseID).Single();
  99:                  await _context.Entry(selectedCourse).Collection(x => x.Enrollments).LoadAsync();
 100:                  foreach (Enrollment enrollment in selectedCourse.Enrollments)
 101:                  {
 102:                      await _context.Entry(enrollment).Reference(x => x.Student).LoadAsync();
 103:                  }
 104:                  viewModel.Enrollments = selectedCourse.Enrollments;
 105:              }
 106:   
 107:              return View(viewModel);
 108:          }
 109:          #endregion
 110:  #endif
 111:          // GET: Instructors/Details/5
 112:          public async Task<IActionResult> Details(int? id)
 113:          {
 114:              if (id == null)
 115:              {
 116:                  return NotFound();
 117:              }
 118:   
 119:              var instructor = await _context.Instructors
 120:                  .AsNoTracking()
 121:                  .SingleOrDefaultAsync(m => m.ID == id);
 122:              if (instructor == null)
 123:              {
 124:                  return NotFound();
 125:              }
 126:   
 127:              return View(instructor);
 128:          }
 129:   
 130:          // GET: Instructors/Create
 131:          #region snippet_Create
 132:          public IActionResult Create()
 133:          {
 134:              var instructor = new Instructor();
 135:              instructor.CourseAssignments = new List<CourseAssignment>();
 136:              PopulateAssignedCourseData(instructor);
 137:              return View();
 138:          }
 139:   
 140:          // POST: Instructors/Create
 141:          [HttpPost]
 142:          [ValidateAntiForgeryToken]
 143:          public async Task<IActionResult> Create([Bind("FirstMidName,HireDate,LastName,OfficeAssignment")] Instructor instructor, string[] selectedCourses)
 144:          {
 145:              if (selectedCourses != null)
 146:              {
 147:                  instructor.CourseAssignments = new List<CourseAssignment>();
 148:                  foreach (var course in selectedCourses)
 149:                  {
 150:                      var courseToAdd = new CourseAssignment { InstructorID = instructor.ID, CourseID = int.Parse(course) };
 151:                      instructor.CourseAssignments.Add(courseToAdd);
 152:                  }
 153:              }
 154:              if (ModelState.IsValid)
 155:              {
 156:                  _context.Add(instructor);
 157:                  await _context.SaveChangesAsync();
 158:                  return RedirectToAction(nameof(Index));
 159:              }
 160:              PopulateAssignedCourseData(instructor);
 161:              return View(instructor);
 162:          }
 163:          #endregion
 164:   
 165:          // GET: Instructors/Edit/5
 166:  #if EditOfficeAssignment
 167:          #region snippet_EditGetOA
 168:          public async Task<IActionResult> Edit(int? id)
 169:          {
 170:              if (id == null)
 171:              {
 172:                  return NotFound();
 173:              }
 174:   
 175:              var instructor = await _context.Instructors
 176:                  .Include(i => i.OfficeAssignment)
 177:                  .AsNoTracking()
 178:                  .SingleOrDefaultAsync(m => m.ID == id);
 179:              if (instructor == null)
 180:              {
 181:                  return NotFound();
 182:              }
 183:              return View(instructor);
 184:          }
 185:          #endregion
 186:  #elif EditCourses
 187:          #region snippet_EditGetCourses
 188:          public async Task<IActionResult> Edit(int? id)
 189:          {
 190:              if (id == null)
 191:              {
 192:                  return NotFound();
 193:              }
 194:   
 195:              var instructor = await _context.Instructors
 196:                  .Include(i => i.OfficeAssignment)
 197:                  .Include(i => i.CourseAssignments).ThenInclude(i => i.Course)
 198:                  .AsNoTracking()
 199:                  .SingleOrDefaultAsync(m => m.ID == id);
 200:              if (instructor == null)
 201:              {
 202:                  return NotFound();
 203:              }
 204:              PopulateAssignedCourseData(instructor);
 205:              return View(instructor);
 206:          }
 207:   
 208:          private void PopulateAssignedCourseData(Instructor instructor)
 209:          {
 210:              var allCourses = _context.Courses;
 211:              var instructorCourses = new HashSet<int>(instructor.CourseAssignments.Select(c => c.CourseID));
 212:              var viewModel = new List<AssignedCourseData>();
 213:              foreach (var course in allCourses)
 214:              {
 215:                  viewModel.Add(new AssignedCourseData
 216:                  {
 217:                      CourseID = course.CourseID,
 218:                      Title = course.Title,
 219:                      Assigned = instructorCourses.Contains(course.CourseID)
 220:                  });
 221:              }
 222:              ViewData["Courses"] = viewModel;
 223:          }
 224:          #endregion
 225:  #endif
 226:          // POST: Instructors/Edit/5
 227:          // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
 228:          // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
 229:  #if EditOfficeAssignment
 230:          #region snippet_EditPostOA
 231:          [HttpPost, ActionName("Edit")]
 232:          [ValidateAntiForgeryToken]
 233:          public async Task<IActionResult> EditPost(int? id)
 234:          {
 235:              if (id == null)
 236:              {
 237:                  return NotFound();
 238:              }
 239:   
 240:              var instructorToUpdate = await _context.Instructors
 241:                  .Include(i => i.OfficeAssignment)
 242:                  .SingleOrDefaultAsync(s => s.ID == id);
 243:   
 244:              if (await TryUpdateModelAsync<Instructor>(
 245:                  instructorToUpdate,
 246:                  "",
 247:                  i => i.FirstMidName, i => i.LastName, i => i.HireDate, i => i.OfficeAssignment))
 248:              {
 249:                  if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment?.Location))
 250:                  {
 251:                      instructorToUpdate.OfficeAssignment = null;
 252:                  }
 253:                  try
 254:                  {
 255:                      await _context.SaveChangesAsync();
 256:                  }
 257:                  catch (DbUpdateException /* ex */)
 258:                  {
 259:                      //Log the error (uncomment ex variable name and write a log.)
 260:                      ModelState.AddModelError("", "Unable to save changes. " +
 261:                          "Try again, and if the problem persists, " +
 262:                          "see your system administrator.");
 263:                  }
 264:                  return RedirectToAction(nameof(Index));
 265:              }
 266:              return View(instructorToUpdate);
 267:          }
 268:          #endregion
 269:   
 270:  #elif EditCourses
 271:          #region snippet_EditPostCourses
 272:          [HttpPost]
 273:          [ValidateAntiForgeryToken]
 274:          public async Task<IActionResult> Edit(int? id, string[] selectedCourses)
 275:          {
 276:              if (id == null)
 277:              {
 278:                  return NotFound();
 279:              }
 280:   
 281:              var instructorToUpdate = await _context.Instructors
 282:                  .Include(i => i.OfficeAssignment)
 283:                  .Include(i => i.CourseAssignments)
 284:                      .ThenInclude(i => i.Course)
 285:                  .SingleOrDefaultAsync(m => m.ID == id);
 286:   
 287:              if (await TryUpdateModelAsync<Instructor>(
 288:                  instructorToUpdate,
 289:                  "",
 290:                  i => i.FirstMidName, i => i.LastName, i => i.HireDate, i => i.OfficeAssignment))
 291:              {
 292:                  if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment?.Location))
 293:                  {
 294:                      instructorToUpdate.OfficeAssignment = null;
 295:                  }
 296:                  UpdateInstructorCourses(selectedCourses, instructorToUpdate);
 297:                  try
 298:                  {
 299:                      await _context.SaveChangesAsync();
 300:                  }
 301:                  catch (DbUpdateException /* ex */)
 302:                  {
 303:                      //Log the error (uncomment ex variable name and write a log.)
 304:                      ModelState.AddModelError("", "Unable to save changes. " +
 305:                          "Try again, and if the problem persists, " +
 306:                          "see your system administrator.");
 307:                  }
 308:                  return RedirectToAction(nameof(Index));
 309:              }
 310:              UpdateInstructorCourses(selectedCourses, instructorToUpdate);
 311:              PopulateAssignedCourseData(instructorToUpdate);
 312:              return View(instructorToUpdate);
 313:          }
 314:          #endregion
 315:   
 316:          #region snippet_UpdateCourses
 317:          private void UpdateInstructorCourses(string[] selectedCourses, Instructor instructorToUpdate)
 318:          {
 319:              if (selectedCourses == null)
 320:              {
 321:                  instructorToUpdate.CourseAssignments = new List<CourseAssignment>();
 322:                  return;
 323:              }
 324:   
 325:              var selectedCoursesHS = new HashSet<string>(selectedCourses);
 326:              var instructorCourses = new HashSet<int>
 327:                  (instructorToUpdate.CourseAssignments.Select(c => c.Course.CourseID));
 328:              foreach (var course in _context.Courses)
 329:              {
 330:                  if (selectedCoursesHS.Contains(course.CourseID.ToString()))
 331:                  {
 332:                      if (!instructorCourses.Contains(course.CourseID))
 333:                      {
 334:                          instructorToUpdate.CourseAssignments.Add(new CourseAssignment { InstructorID = instructorToUpdate.ID, CourseID = course.CourseID });
 335:                      }
 336:                  }
 337:                  else
 338:                  {
 339:   
 340:                      if (instructorCourses.Contains(course.CourseID))
 341:                      {
 342:                          CourseAssignment courseToRemove = instructorToUpdate.CourseAssignments.SingleOrDefault(i => i.CourseID == course.CourseID);
 343:                          _context.Remove(courseToRemove);
 344:                      }
 345:                  }
 346:              }
 347:          }
 348:          #endregion
 349:  #endif
 350:          // GET: Instructors/Delete/5
 351:          public async Task<IActionResult> Delete(int? id)
 352:          {
 353:              if (id == null)
 354:              {
 355:                  return NotFound();
 356:              }
 357:   
 358:              var instructor = await _context.Instructors
 359:                  .AsNoTracking()
 360:                  .SingleOrDefaultAsync(m => m.ID == id);
 361:              if (instructor == null)
 362:              {
 363:                  return NotFound();
 364:              }
 365:   
 366:              return View(instructor);
 367:          }
 368:   
 369:          #region snippet_DeleteConfirmed
 370:          [HttpPost, ActionName("Delete")]
 371:          [ValidateAntiForgeryToken]
 372:          public async Task<IActionResult> DeleteConfirmed(int id)
 373:          {
 374:              Instructor instructor = await _context.Instructors
 375:                  .Include(i => i.CourseAssignments)
 376:                  .SingleAsync(i => i.ID == id);
 377:   
 378:              var departments = await _context.Departments
 379:                  .Where(d => d.InstructorID == id)
 380:                  .ToListAsync();
 381:              departments.ForEach(d => d.InstructorID = null);
 382:   
 383:              _context.Instructors.Remove(instructor);
 384:   
 385:              await _context.SaveChangesAsync();
 386:              return RedirectToAction(nameof(Index));
 387:          }
 388:          #endregion
 389:      }
 390:  }

The code then loops through all courses in the database and checks each course against the ones currently assigned to the instructor versus the ones that were selected in the view. To facilitate efficient lookups, the latter two collections are stored in HashSet objects.

If the check box for a course was selected but the course isn’t in the Instructor.CourseAssignments navigation property, the course is added to the collection in the navigation property.

[!code-csharpMain]

   1:  #define ExplicitLoading // or EagerLoading or ScaffoldedCode
   2:  #define EditCourses // or EditOfficeAssignment
   3:   
   4:  using System;
   5:  using System.Collections.Generic;
   6:  using System.Linq;
   7:  using System.Threading.Tasks;
   8:  using Microsoft.AspNetCore.Mvc;
   9:  using Microsoft.AspNetCore.Mvc.Rendering;
  10:  using Microsoft.EntityFrameworkCore;
  11:  using ContosoUniversity.Data;
  12:  using ContosoUniversity.Models;
  13:  #region snippet_Using
  14:  using ContosoUniversity.Models.SchoolViewModels;
  15:  #endregion
  16:   
  17:  namespace ContosoUniversity.Controllers
  18:  {
  19:      public class InstructorsController : Controller
  20:      {
  21:          private readonly SchoolContext _context;
  22:   
  23:          public InstructorsController(SchoolContext context)
  24:          {
  25:              _context = context;
  26:          }
  27:   
  28:          // GET: Instructors
  29:  #if ScaffoldedCode
  30:          #region snippet_ScaffoldedCode
  31:          public async Task<IActionResult> Index()
  32:          {
  33:              return View(await _context.Instructors.AsNoTracking().ToListAsync());
  34:          }
  35:          #endregion
  36:  #elif EagerLoading
  37:          #region snippet_EagerLoading
  38:          public async Task<IActionResult> Index(int? id, int? courseID)
  39:          {
  40:              var viewModel = new InstructorIndexData();
  41:              #region snippet_ThenInclude
  42:              viewModel.Instructors = await _context.Instructors
  43:                    .Include(i => i.OfficeAssignment)
  44:                    .Include(i => i.CourseAssignments)
  45:                      .ThenInclude(i => i.Course)
  46:                          .ThenInclude(i => i.Enrollments)
  47:                              .ThenInclude(i => i.Student)
  48:                    .Include(i => i.CourseAssignments)
  49:                      .ThenInclude(i => i.Course)
  50:                          .ThenInclude(i => i.Department)
  51:                    .AsNoTracking()
  52:                    .OrderBy(i => i.LastName)
  53:                    .ToListAsync();
  54:              #endregion
  55:              
  56:              if (id != null)
  57:              {
  58:                  ViewData["InstructorID"] = id.Value;
  59:                  Instructor instructor = viewModel.Instructors.Where(
  60:                      i => i.ID == id.Value).Single();
  61:                  viewModel.Courses = instructor.CourseAssignments.Select(s => s.Course);
  62:              }
  63:   
  64:              if (courseID != null)
  65:              {
  66:                  ViewData["CourseID"] = courseID.Value;
  67:                  viewModel.Enrollments = viewModel.Courses.Where(
  68:                      x => x.CourseID == courseID).Single().Enrollments;
  69:              }
  70:   
  71:              return View(viewModel);
  72:          }
  73:          #endregion
  74:  #elif ExplicitLoading
  75:          #region snippet_ExplicitLoading
  76:          public async Task<IActionResult> Index(int? id, int? courseID)
  77:          {
  78:              var viewModel = new InstructorIndexData();
  79:              viewModel.Instructors = await _context.Instructors
  80:                    .Include(i => i.OfficeAssignment)
  81:                    .Include(i => i.CourseAssignments)
  82:                      .ThenInclude(i => i.Course)
  83:                          .ThenInclude(i => i.Department)
  84:                    .OrderBy(i => i.LastName)
  85:                    .ToListAsync();
  86:   
  87:              if (id != null)
  88:              {
  89:                  ViewData["InstructorID"] = id.Value;
  90:                  Instructor instructor = viewModel.Instructors.Where(
  91:                      i => i.ID == id.Value).Single();
  92:                  viewModel.Courses = instructor.CourseAssignments.Select(s => s.Course);
  93:              }
  94:   
  95:              if (courseID != null)
  96:              {
  97:                  ViewData["CourseID"] = courseID.Value;
  98:                  var selectedCourse = viewModel.Courses.Where(x => x.CourseID == courseID).Single();
  99:                  await _context.Entry(selectedCourse).Collection(x => x.Enrollments).LoadAsync();
 100:                  foreach (Enrollment enrollment in selectedCourse.Enrollments)
 101:                  {
 102:                      await _context.Entry(enrollment).Reference(x => x.Student).LoadAsync();
 103:                  }
 104:                  viewModel.Enrollments = selectedCourse.Enrollments;
 105:              }
 106:   
 107:              return View(viewModel);
 108:          }
 109:          #endregion
 110:  #endif
 111:          // GET: Instructors/Details/5
 112:          public async Task<IActionResult> Details(int? id)
 113:          {
 114:              if (id == null)
 115:              {
 116:                  return NotFound();
 117:              }
 118:   
 119:              var instructor = await _context.Instructors
 120:                  .AsNoTracking()
 121:                  .SingleOrDefaultAsync(m => m.ID == id);
 122:              if (instructor == null)
 123:              {
 124:                  return NotFound();
 125:              }
 126:   
 127:              return View(instructor);
 128:          }
 129:   
 130:          // GET: Instructors/Create
 131:          #region snippet_Create
 132:          public IActionResult Create()
 133:          {
 134:              var instructor = new Instructor();
 135:              instructor.CourseAssignments = new List<CourseAssignment>();
 136:              PopulateAssignedCourseData(instructor);
 137:              return View();
 138:          }
 139:   
 140:          // POST: Instructors/Create
 141:          [HttpPost]
 142:          [ValidateAntiForgeryToken]
 143:          public async Task<IActionResult> Create([Bind("FirstMidName,HireDate,LastName,OfficeAssignment")] Instructor instructor, string[] selectedCourses)
 144:          {
 145:              if (selectedCourses != null)
 146:              {
 147:                  instructor.CourseAssignments = new List<CourseAssignment>();
 148:                  foreach (var course in selectedCourses)
 149:                  {
 150:                      var courseToAdd = new CourseAssignment { InstructorID = instructor.ID, CourseID = int.Parse(course) };
 151:                      instructor.CourseAssignments.Add(courseToAdd);
 152:                  }
 153:              }
 154:              if (ModelState.IsValid)
 155:              {
 156:                  _context.Add(instructor);
 157:                  await _context.SaveChangesAsync();
 158:                  return RedirectToAction(nameof(Index));
 159:              }
 160:              PopulateAssignedCourseData(instructor);
 161:              return View(instructor);
 162:          }
 163:          #endregion
 164:   
 165:          // GET: Instructors/Edit/5
 166:  #if EditOfficeAssignment
 167:          #region snippet_EditGetOA
 168:          public async Task<IActionResult> Edit(int? id)
 169:          {
 170:              if (id == null)
 171:              {
 172:                  return NotFound();
 173:              }
 174:   
 175:              var instructor = await _context.Instructors
 176:                  .Include(i => i.OfficeAssignment)
 177:                  .AsNoTracking()
 178:                  .SingleOrDefaultAsync(m => m.ID == id);
 179:              if (instructor == null)
 180:              {
 181:                  return NotFound();
 182:              }
 183:              return View(instructor);
 184:          }
 185:          #endregion
 186:  #elif EditCourses
 187:          #region snippet_EditGetCourses
 188:          public async Task<IActionResult> Edit(int? id)
 189:          {
 190:              if (id == null)
 191:              {
 192:                  return NotFound();
 193:              }
 194:   
 195:              var instructor = await _context.Instructors
 196:                  .Include(i => i.OfficeAssignment)
 197:                  .Include(i => i.CourseAssignments).ThenInclude(i => i.Course)
 198:                  .AsNoTracking()
 199:                  .SingleOrDefaultAsync(m => m.ID == id);
 200:              if (instructor == null)
 201:              {
 202:                  return NotFound();
 203:              }
 204:              PopulateAssignedCourseData(instructor);
 205:              return View(instructor);
 206:          }
 207:   
 208:          private void PopulateAssignedCourseData(Instructor instructor)
 209:          {
 210:              var allCourses = _context.Courses;
 211:              var instructorCourses = new HashSet<int>(instructor.CourseAssignments.Select(c => c.CourseID));
 212:              var viewModel = new List<AssignedCourseData>();
 213:              foreach (var course in allCourses)
 214:              {
 215:                  viewModel.Add(new AssignedCourseData
 216:                  {
 217:                      CourseID = course.CourseID,
 218:                      Title = course.Title,
 219:                      Assigned = instructorCourses.Contains(course.CourseID)
 220:                  });
 221:              }
 222:              ViewData["Courses"] = viewModel;
 223:          }
 224:          #endregion
 225:  #endif
 226:          // POST: Instructors/Edit/5
 227:          // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
 228:          // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
 229:  #if EditOfficeAssignment
 230:          #region snippet_EditPostOA
 231:          [HttpPost, ActionName("Edit")]
 232:          [ValidateAntiForgeryToken]
 233:          public async Task<IActionResult> EditPost(int? id)
 234:          {
 235:              if (id == null)
 236:              {
 237:                  return NotFound();
 238:              }
 239:   
 240:              var instructorToUpdate = await _context.Instructors
 241:                  .Include(i => i.OfficeAssignment)
 242:                  .SingleOrDefaultAsync(s => s.ID == id);
 243:   
 244:              if (await TryUpdateModelAsync<Instructor>(
 245:                  instructorToUpdate,
 246:                  "",
 247:                  i => i.FirstMidName, i => i.LastName, i => i.HireDate, i => i.OfficeAssignment))
 248:              {
 249:                  if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment?.Location))
 250:                  {
 251:                      instructorToUpdate.OfficeAssignment = null;
 252:                  }
 253:                  try
 254:                  {
 255:                      await _context.SaveChangesAsync();
 256:                  }
 257:                  catch (DbUpdateException /* ex */)
 258:                  {
 259:                      //Log the error (uncomment ex variable name and write a log.)
 260:                      ModelState.AddModelError("", "Unable to save changes. " +
 261:                          "Try again, and if the problem persists, " +
 262:                          "see your system administrator.");
 263:                  }
 264:                  return RedirectToAction(nameof(Index));
 265:              }
 266:              return View(instructorToUpdate);
 267:          }
 268:          #endregion
 269:   
 270:  #elif EditCourses
 271:          #region snippet_EditPostCourses
 272:          [HttpPost]
 273:          [ValidateAntiForgeryToken]
 274:          public async Task<IActionResult> Edit(int? id, string[] selectedCourses)
 275:          {
 276:              if (id == null)
 277:              {
 278:                  return NotFound();
 279:              }
 280:   
 281:              var instructorToUpdate = await _context.Instructors
 282:                  .Include(i => i.OfficeAssignment)
 283:                  .Include(i => i.CourseAssignments)
 284:                      .ThenInclude(i => i.Course)
 285:                  .SingleOrDefaultAsync(m => m.ID == id);
 286:   
 287:              if (await TryUpdateModelAsync<Instructor>(
 288:                  instructorToUpdate,
 289:                  "",
 290:                  i => i.FirstMidName, i => i.LastName, i => i.HireDate, i => i.OfficeAssignment))
 291:              {
 292:                  if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment?.Location))
 293:                  {
 294:                      instructorToUpdate.OfficeAssignment = null;
 295:                  }
 296:                  UpdateInstructorCourses(selectedCourses, instructorToUpdate);
 297:                  try
 298:                  {
 299:                      await _context.SaveChangesAsync();
 300:                  }
 301:                  catch (DbUpdateException /* ex */)
 302:                  {
 303:                      //Log the error (uncomment ex variable name and write a log.)
 304:                      ModelState.AddModelError("", "Unable to save changes. " +
 305:                          "Try again, and if the problem persists, " +
 306:                          "see your system administrator.");
 307:                  }
 308:                  return RedirectToAction(nameof(Index));
 309:              }
 310:              UpdateInstructorCourses(selectedCourses, instructorToUpdate);
 311:              PopulateAssignedCourseData(instructorToUpdate);
 312:              return View(instructorToUpdate);
 313:          }
 314:          #endregion
 315:   
 316:          #region snippet_UpdateCourses
 317:          private void UpdateInstructorCourses(string[] selectedCourses, Instructor instructorToUpdate)
 318:          {
 319:              if (selectedCourses == null)
 320:              {
 321:                  instructorToUpdate.CourseAssignments = new List<CourseAssignment>();
 322:                  return;
 323:              }
 324:   
 325:              var selectedCoursesHS = new HashSet<string>(selectedCourses);
 326:              var instructorCourses = new HashSet<int>
 327:                  (instructorToUpdate.CourseAssignments.Select(c => c.Course.CourseID));
 328:              foreach (var course in _context.Courses)
 329:              {
 330:                  if (selectedCoursesHS.Contains(course.CourseID.ToString()))
 331:                  {
 332:                      if (!instructorCourses.Contains(course.CourseID))
 333:                      {
 334:                          instructorToUpdate.CourseAssignments.Add(new CourseAssignment { InstructorID = instructorToUpdate.ID, CourseID = course.CourseID });
 335:                      }
 336:                  }
 337:                  else
 338:                  {
 339:   
 340:                      if (instructorCourses.Contains(course.CourseID))
 341:                      {
 342:                          CourseAssignment courseToRemove = instructorToUpdate.CourseAssignments.SingleOrDefault(i => i.CourseID == course.CourseID);
 343:                          _context.Remove(courseToRemove);
 344:                      }
 345:                  }
 346:              }
 347:          }
 348:          #endregion
 349:  #endif
 350:          // GET: Instructors/Delete/5
 351:          public async Task<IActionResult> Delete(int? id)
 352:          {
 353:              if (id == null)
 354:              {
 355:                  return NotFound();
 356:              }
 357:   
 358:              var instructor = await _context.Instructors
 359:                  .AsNoTracking()
 360:                  .SingleOrDefaultAsync(m => m.ID == id);
 361:              if (instructor == null)
 362:              {
 363:                  return NotFound();
 364:              }
 365:   
 366:              return View(instructor);
 367:          }
 368:   
 369:          #region snippet_DeleteConfirmed
 370:          [HttpPost, ActionName("Delete")]
 371:          [ValidateAntiForgeryToken]
 372:          public async Task<IActionResult> DeleteConfirmed(int id)
 373:          {
 374:              Instructor instructor = await _context.Instructors
 375:                  .Include(i => i.CourseAssignments)
 376:                  .SingleAsync(i => i.ID == id);
 377:   
 378:              var departments = await _context.Departments
 379:                  .Where(d => d.InstructorID == id)
 380:                  .ToListAsync();
 381:              departments.ForEach(d => d.InstructorID = null);
 382:   
 383:              _context.Instructors.Remove(instructor);
 384:   
 385:              await _context.SaveChangesAsync();
 386:              return RedirectToAction(nameof(Index));
 387:          }
 388:          #endregion
 389:      }
 390:  }

If the check box for a course wasn’t selected, but the course is in the Instructor.CourseAssignments navigation property, the course is removed from the navigation property.

[!code-csharpMain]

   1:  #define ExplicitLoading // or EagerLoading or ScaffoldedCode
   2:  #define EditCourses // or EditOfficeAssignment
   3:   
   4:  using System;
   5:  using System.Collections.Generic;
   6:  using System.Linq;
   7:  using System.Threading.Tasks;
   8:  using Microsoft.AspNetCore.Mvc;
   9:  using Microsoft.AspNetCore.Mvc.Rendering;
  10:  using Microsoft.EntityFrameworkCore;
  11:  using ContosoUniversity.Data;
  12:  using ContosoUniversity.Models;
  13:  #region snippet_Using
  14:  using ContosoUniversity.Models.SchoolViewModels;
  15:  #endregion
  16:   
  17:  namespace ContosoUniversity.Controllers
  18:  {
  19:      public class InstructorsController : Controller
  20:      {
  21:          private readonly SchoolContext _context;
  22:   
  23:          public InstructorsController(SchoolContext context)
  24:          {
  25:              _context = context;
  26:          }
  27:   
  28:          // GET: Instructors
  29:  #if ScaffoldedCode
  30:          #region snippet_ScaffoldedCode
  31:          public async Task<IActionResult> Index()
  32:          {
  33:              return View(await _context.Instructors.AsNoTracking().ToListAsync());
  34:          }
  35:          #endregion
  36:  #elif EagerLoading
  37:          #region snippet_EagerLoading
  38:          public async Task<IActionResult> Index(int? id, int? courseID)
  39:          {
  40:              var viewModel = new InstructorIndexData();
  41:              #region snippet_ThenInclude
  42:              viewModel.Instructors = await _context.Instructors
  43:                    .Include(i => i.OfficeAssignment)
  44:                    .Include(i => i.CourseAssignments)
  45:                      .ThenInclude(i => i.Course)
  46:                          .ThenInclude(i => i.Enrollments)
  47:                              .ThenInclude(i => i.Student)
  48:                    .Include(i => i.CourseAssignments)
  49:                      .ThenInclude(i => i.Course)
  50:                          .ThenInclude(i => i.Department)
  51:                    .AsNoTracking()
  52:                    .OrderBy(i => i.LastName)
  53:                    .ToListAsync();
  54:              #endregion
  55:              
  56:              if (id != null)
  57:              {
  58:                  ViewData["InstructorID"] = id.Value;
  59:                  Instructor instructor = viewModel.Instructors.Where(
  60:                      i => i.ID == id.Value).Single();
  61:                  viewModel.Courses = instructor.CourseAssignments.Select(s => s.Course);
  62:              }
  63:   
  64:              if (courseID != null)
  65:              {
  66:                  ViewData["CourseID"] = courseID.Value;
  67:                  viewModel.Enrollments = viewModel.Courses.Where(
  68:                      x => x.CourseID == courseID).Single().Enrollments;
  69:              }
  70:   
  71:              return View(viewModel);
  72:          }
  73:          #endregion
  74:  #elif ExplicitLoading
  75:          #region snippet_ExplicitLoading
  76:          public async Task<IActionResult> Index(int? id, int? courseID)
  77:          {
  78:              var viewModel = new InstructorIndexData();
  79:              viewModel.Instructors = await _context.Instructors
  80:                    .Include(i => i.OfficeAssignment)
  81:                    .Include(i => i.CourseAssignments)
  82:                      .ThenInclude(i => i.Course)
  83:                          .ThenInclude(i => i.Department)
  84:                    .OrderBy(i => i.LastName)
  85:                    .ToListAsync();
  86:   
  87:              if (id != null)
  88:              {
  89:                  ViewData["InstructorID"] = id.Value;
  90:                  Instructor instructor = viewModel.Instructors.Where(
  91:                      i => i.ID == id.Value).Single();
  92:                  viewModel.Courses = instructor.CourseAssignments.Select(s => s.Course);
  93:              }
  94:   
  95:              if (courseID != null)
  96:              {
  97:                  ViewData["CourseID"] = courseID.Value;
  98:                  var selectedCourse = viewModel.Courses.Where(x => x.CourseID == courseID).Single();
  99:                  await _context.Entry(selectedCourse).Collection(x => x.Enrollments).LoadAsync();
 100:                  foreach (Enrollment enrollment in selectedCourse.Enrollments)
 101:                  {
 102:                      await _context.Entry(enrollment).Reference(x => x.Student).LoadAsync();
 103:                  }
 104:                  viewModel.Enrollments = selectedCourse.Enrollments;
 105:              }
 106:   
 107:              return View(viewModel);
 108:          }
 109:          #endregion
 110:  #endif
 111:          // GET: Instructors/Details/5
 112:          public async Task<IActionResult> Details(int? id)
 113:          {
 114:              if (id == null)
 115:              {
 116:                  return NotFound();
 117:              }
 118:   
 119:              var instructor = await _context.Instructors
 120:                  .AsNoTracking()
 121:                  .SingleOrDefaultAsync(m => m.ID == id);
 122:              if (instructor == null)
 123:              {
 124:                  return NotFound();
 125:              }
 126:   
 127:              return View(instructor);
 128:          }
 129:   
 130:          // GET: Instructors/Create
 131:          #region snippet_Create
 132:          public IActionResult Create()
 133:          {
 134:              var instructor = new Instructor();
 135:              instructor.CourseAssignments = new List<CourseAssignment>();
 136:              PopulateAssignedCourseData(instructor);
 137:              return View();
 138:          }
 139:   
 140:          // POST: Instructors/Create
 141:          [HttpPost]
 142:          [ValidateAntiForgeryToken]
 143:          public async Task<IActionResult> Create([Bind("FirstMidName,HireDate,LastName,OfficeAssignment")] Instructor instructor, string[] selectedCourses)
 144:          {
 145:              if (selectedCourses != null)
 146:              {
 147:                  instructor.CourseAssignments = new List<CourseAssignment>();
 148:                  foreach (var course in selectedCourses)
 149:                  {
 150:                      var courseToAdd = new CourseAssignment { InstructorID = instructor.ID, CourseID = int.Parse(course) };
 151:                      instructor.CourseAssignments.Add(courseToAdd);
 152:                  }
 153:              }
 154:              if (ModelState.IsValid)
 155:              {
 156:                  _context.Add(instructor);
 157:                  await _context.SaveChangesAsync();
 158:                  return RedirectToAction(nameof(Index));
 159:              }
 160:              PopulateAssignedCourseData(instructor);
 161:              return View(instructor);
 162:          }
 163:          #endregion
 164:   
 165:          // GET: Instructors/Edit/5
 166:  #if EditOfficeAssignment
 167:          #region snippet_EditGetOA
 168:          public async Task<IActionResult> Edit(int? id)
 169:          {
 170:              if (id == null)
 171:              {
 172:                  return NotFound();
 173:              }
 174:   
 175:              var instructor = await _context.Instructors
 176:                  .Include(i => i.OfficeAssignment)
 177:                  .AsNoTracking()
 178:                  .SingleOrDefaultAsync(m => m.ID == id);
 179:              if (instructor == null)
 180:              {
 181:                  return NotFound();
 182:              }
 183:              return View(instructor);
 184:          }
 185:          #endregion
 186:  #elif EditCourses
 187:          #region snippet_EditGetCourses
 188:          public async Task<IActionResult> Edit(int? id)
 189:          {
 190:              if (id == null)
 191:              {
 192:                  return NotFound();
 193:              }
 194:   
 195:              var instructor = await _context.Instructors
 196:                  .Include(i => i.OfficeAssignment)
 197:                  .Include(i => i.CourseAssignments).ThenInclude(i => i.Course)
 198:                  .AsNoTracking()
 199:                  .SingleOrDefaultAsync(m => m.ID == id);
 200:              if (instructor == null)
 201:              {
 202:                  return NotFound();
 203:              }
 204:              PopulateAssignedCourseData(instructor);
 205:              return View(instructor);
 206:          }
 207:   
 208:          private void PopulateAssignedCourseData(Instructor instructor)
 209:          {
 210:              var allCourses = _context.Courses;
 211:              var instructorCourses = new HashSet<int>(instructor.CourseAssignments.Select(c => c.CourseID));
 212:              var viewModel = new List<AssignedCourseData>();
 213:              foreach (var course in allCourses)
 214:              {
 215:                  viewModel.Add(new AssignedCourseData
 216:                  {
 217:                      CourseID = course.CourseID,
 218:                      Title = course.Title,
 219:                      Assigned = instructorCourses.Contains(course.CourseID)
 220:                  });
 221:              }
 222:              ViewData["Courses"] = viewModel;
 223:          }
 224:          #endregion
 225:  #endif
 226:          // POST: Instructors/Edit/5
 227:          // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
 228:          // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
 229:  #if EditOfficeAssignment
 230:          #region snippet_EditPostOA
 231:          [HttpPost, ActionName("Edit")]
 232:          [ValidateAntiForgeryToken]
 233:          public async Task<IActionResult> EditPost(int? id)
 234:          {
 235:              if (id == null)
 236:              {
 237:                  return NotFound();
 238:              }
 239:   
 240:              var instructorToUpdate = await _context.Instructors
 241:                  .Include(i => i.OfficeAssignment)
 242:                  .SingleOrDefaultAsync(s => s.ID == id);
 243:   
 244:              if (await TryUpdateModelAsync<Instructor>(
 245:                  instructorToUpdate,
 246:                  "",
 247:                  i => i.FirstMidName, i => i.LastName, i => i.HireDate, i => i.OfficeAssignment))
 248:              {
 249:                  if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment?.Location))
 250:                  {
 251:                      instructorToUpdate.OfficeAssignment = null;
 252:                  }
 253:                  try
 254:                  {
 255:                      await _context.SaveChangesAsync();
 256:                  }
 257:                  catch (DbUpdateException /* ex */)
 258:                  {
 259:                      //Log the error (uncomment ex variable name and write a log.)
 260:                      ModelState.AddModelError("", "Unable to save changes. " +
 261:                          "Try again, and if the problem persists, " +
 262:                          "see your system administrator.");
 263:                  }
 264:                  return RedirectToAction(nameof(Index));
 265:              }
 266:              return View(instructorToUpdate);
 267:          }
 268:          #endregion
 269:   
 270:  #elif EditCourses
 271:          #region snippet_EditPostCourses
 272:          [HttpPost]
 273:          [ValidateAntiForgeryToken]
 274:          public async Task<IActionResult> Edit(int? id, string[] selectedCourses)
 275:          {
 276:              if (id == null)
 277:              {
 278:                  return NotFound();
 279:              }
 280:   
 281:              var instructorToUpdate = await _context.Instructors
 282:                  .Include(i => i.OfficeAssignment)
 283:                  .Include(i => i.CourseAssignments)
 284:                      .ThenInclude(i => i.Course)
 285:                  .SingleOrDefaultAsync(m => m.ID == id);
 286:   
 287:              if (await TryUpdateModelAsync<Instructor>(
 288:                  instructorToUpdate,
 289:                  "",
 290:                  i => i.FirstMidName, i => i.LastName, i => i.HireDate, i => i.OfficeAssignment))
 291:              {
 292:                  if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment?.Location))
 293:                  {
 294:                      instructorToUpdate.OfficeAssignment = null;
 295:                  }
 296:                  UpdateInstructorCourses(selectedCourses, instructorToUpdate);
 297:                  try
 298:                  {
 299:                      await _context.SaveChangesAsync();
 300:                  }
 301:                  catch (DbUpdateException /* ex */)
 302:                  {
 303:                      //Log the error (uncomment ex variable name and write a log.)
 304:                      ModelState.AddModelError("", "Unable to save changes. " +
 305:                          "Try again, and if the problem persists, " +
 306:                          "see your system administrator.");
 307:                  }
 308:                  return RedirectToAction(nameof(Index));
 309:              }
 310:              UpdateInstructorCourses(selectedCourses, instructorToUpdate);
 311:              PopulateAssignedCourseData(instructorToUpdate);
 312:              return View(instructorToUpdate);
 313:          }
 314:          #endregion
 315:   
 316:          #region snippet_UpdateCourses
 317:          private void UpdateInstructorCourses(string[] selectedCourses, Instructor instructorToUpdate)
 318:          {
 319:              if (selectedCourses == null)
 320:              {
 321:                  instructorToUpdate.CourseAssignments = new List<CourseAssignment>();
 322:                  return;
 323:              }
 324:   
 325:              var selectedCoursesHS = new HashSet<string>(selectedCourses);
 326:              var instructorCourses = new HashSet<int>
 327:                  (instructorToUpdate.CourseAssignments.Select(c => c.Course.CourseID));
 328:              foreach (var course in _context.Courses)
 329:              {
 330:                  if (selectedCoursesHS.Contains(course.CourseID.ToString()))
 331:                  {
 332:                      if (!instructorCourses.Contains(course.CourseID))
 333:                      {
 334:                          instructorToUpdate.CourseAssignments.Add(new CourseAssignment { InstructorID = instructorToUpdate.ID, CourseID = course.CourseID });
 335:                      }
 336:                  }
 337:                  else
 338:                  {
 339:   
 340:                      if (instructorCourses.Contains(course.CourseID))
 341:                      {
 342:                          CourseAssignment courseToRemove = instructorToUpdate.CourseAssignments.SingleOrDefault(i => i.CourseID == course.CourseID);
 343:                          _context.Remove(courseToRemove);
 344:                      }
 345:                  }
 346:              }
 347:          }
 348:          #endregion
 349:  #endif
 350:          // GET: Instructors/Delete/5
 351:          public async Task<IActionResult> Delete(int? id)
 352:          {
 353:              if (id == null)
 354:              {
 355:                  return NotFound();
 356:              }
 357:   
 358:              var instructor = await _context.Instructors
 359:                  .AsNoTracking()
 360:                  .SingleOrDefaultAsync(m => m.ID == id);
 361:              if (instructor == null)
 362:              {
 363:                  return NotFound();
 364:              }
 365:   
 366:              return View(instructor);
 367:          }
 368:   
 369:          #region snippet_DeleteConfirmed
 370:          [HttpPost, ActionName("Delete")]
 371:          [ValidateAntiForgeryToken]
 372:          public async Task<IActionResult> DeleteConfirmed(int id)
 373:          {
 374:              Instructor instructor = await _context.Instructors
 375:                  .Include(i => i.CourseAssignments)
 376:                  .SingleAsync(i => i.ID == id);
 377:   
 378:              var departments = await _context.Departments
 379:                  .Where(d => d.InstructorID == id)
 380:                  .ToListAsync();
 381:              departments.ForEach(d => d.InstructorID = null);
 382:   
 383:              _context.Instructors.Remove(instructor);
 384:   
 385:              await _context.SaveChangesAsync();
 386:              return RedirectToAction(nameof(Index));
 387:          }
 388:          #endregion
 389:      }
 390:  }

Update the Instructor views

In Views/Instructors/Edit.cshtml, add a Courses field with an array of check boxes by adding the following code immediately after the div elements for the Office field and before the div element for the Save button.

> [!NOTE] > When you paste the code in Visual Studio, line breaks will be changed in a way that breaks the code. Press Ctrl+Z one time to undo the automatic formatting. This will fix the line breaks so that they look like what you see here. The indentation doesn’t have to be perfect, but the @</tr><tr>, @:<td>, @:</td>, and @:</tr> lines must each be on a single line as shown or you’ll get a runtime error. With the block of new code selected, press Tab three times to line up the new code with the existing code. You can check the status of this problem here.

[!code-htmlMain]

   1:  @model ContosoUniversity.Models.Instructor
   2:   
   3:  @{
   4:      ViewData["Title"] = "Edit";
   5:  }
   6:   
   7:  <h2>Edit</h2>
   8:   
   9:  <form asp-action="Edit">
  10:      <div class="form-horizontal">
  11:          <h4>Instructor</h4>
  12:          <hr />
  13:          <div asp-validation-summary="ModelOnly" class="text-danger"></div>
  14:          <input type="hidden" asp-for="ID" />
  15:          <div class="form-group">
  16:              <label asp-for="LastName" class="control-label"></label>
  17:              <input asp-for="LastName" class="form-control" />
  18:              <span asp-validation-for="LastName" class="text-danger"></span>
  19:          </div>
  20:          <div class="form-group">
  21:              <label asp-for="FirstMidName" class="control-label"></label>
  22:              <input asp-for="FirstMidName" class="form-control" />
  23:              <span asp-validation-for="FirstMidName" class="text-danger"></span>
  24:          </div>
  25:          <div class="form-group">
  26:              <label asp-for="HireDate" class="control-label"></label>
  27:              <input asp-for="HireDate" class="form-control" />
  28:              <span asp-validation-for="HireDate" class="text-danger"></span>
  29:          </div>
  30:          <div class="form-group">
  31:              <label asp-for="OfficeAssignment.Location" class="control-label"></label>
  32:              <input asp-for="OfficeAssignment.Location" class="form-control" />
  33:              <span asp-validation-for="OfficeAssignment.Location" class="text-danger" />
  34:          </div>
  35:          <div class="form-group">
  36:              <div class="col-md-offset-2 col-md-10">
  37:                  <table>
  38:                      <tr>
  39:                          @{
  40:                              int cnt = 0;
  41:                              List<ContosoUniversity.Models.SchoolViewModels.AssignedCourseData> courses = ViewBag.Courses;
  42:   
  43:                              foreach (var course in courses)
  44:                              {
  45:                                  if (cnt++ % 3 == 0)
  46:                                  {
  47:                                      @:</tr><tr>
  48:                                  }
  49:                                  @:<td>
  50:                                      <input type="checkbox"
  51:                                             name="selectedCourses"
  52:                                             value="@course.CourseID"
  53:                                             @(Html.Raw(course.Assigned ? "checked=\"checked\"" : "")) />
  54:                                             @course.CourseID @:  @course.Title
  55:                                  @:</td>
  56:                              }
  57:                              @:</tr>
  58:                          }
  59:                  </table>
  60:              </div>
  61:          </div>
  62:   
  63:          <div class="form-group">
  64:              <input type="submit" value="Save" class="btn btn-default" />
  65:          </div>
  66:      </div>
  67:  </form>
  68:   
  69:  <div>
  70:      <a asp-action="Index">Back to List</a>
  71:  </div>
  72:   
  73:  @section Scripts {
  74:      @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
  75:  }

This code creates an HTML table that has three columns. In each column is a check box followed by a caption that consists of the course number and title. The check boxes all have the same name (“selectedCourses”), which informs the model binder that they are to be treated as a group. The value attribute of each check box is set to the value of CourseID. When the page is posted, the model binder passes an array to the controller that consists of the CourseID values for only the check boxes which are selected.

When the check boxes are initially rendered, those that are for courses assigned to the instructor have checked attributes, which selects them (displays them checked).

Run the app, select the Instructors tab, and click Edit on an instructor to see the Edit page.

Instructor Edit page with courses
Instructor Edit page with courses

Change some course assignments and click Save. The changes you make are reflected on the Index page.

[!NOTE] The approach taken here to edit instructor course data works well when there is a limited number of courses. For collections that are much larger, a different UI and a different updating method would be required.

Update the Delete page

In InstructorsController.cs, delete the DeleteConfirmed method and insert the following code in its place.

[!code-csharpMain]

   1:  #define ExplicitLoading // or EagerLoading or ScaffoldedCode
   2:  #define EditCourses // or EditOfficeAssignment
   3:   
   4:  using System;
   5:  using System.Collections.Generic;
   6:  using System.Linq;
   7:  using System.Threading.Tasks;
   8:  using Microsoft.AspNetCore.Mvc;
   9:  using Microsoft.AspNetCore.Mvc.Rendering;
  10:  using Microsoft.EntityFrameworkCore;
  11:  using ContosoUniversity.Data;
  12:  using ContosoUniversity.Models;
  13:  #region snippet_Using
  14:  using ContosoUniversity.Models.SchoolViewModels;
  15:  #endregion
  16:   
  17:  namespace ContosoUniversity.Controllers
  18:  {
  19:      public class InstructorsController : Controller
  20:      {
  21:          private readonly SchoolContext _context;
  22:   
  23:          public InstructorsController(SchoolContext context)
  24:          {
  25:              _context = context;
  26:          }
  27:   
  28:          // GET: Instructors
  29:  #if ScaffoldedCode
  30:          #region snippet_ScaffoldedCode
  31:          public async Task<IActionResult> Index()
  32:          {
  33:              return View(await _context.Instructors.AsNoTracking().ToListAsync());
  34:          }
  35:          #endregion
  36:  #elif EagerLoading
  37:          #region snippet_EagerLoading
  38:          public async Task<IActionResult> Index(int? id, int? courseID)
  39:          {
  40:              var viewModel = new InstructorIndexData();
  41:              #region snippet_ThenInclude
  42:              viewModel.Instructors = await _context.Instructors
  43:                    .Include(i => i.OfficeAssignment)
  44:                    .Include(i => i.CourseAssignments)
  45:                      .ThenInclude(i => i.Course)
  46:                          .ThenInclude(i => i.Enrollments)
  47:                              .ThenInclude(i => i.Student)
  48:                    .Include(i => i.CourseAssignments)
  49:                      .ThenInclude(i => i.Course)
  50:                          .ThenInclude(i => i.Department)
  51:                    .AsNoTracking()
  52:                    .OrderBy(i => i.LastName)
  53:                    .ToListAsync();
  54:              #endregion
  55:              
  56:              if (id != null)
  57:              {
  58:                  ViewData["InstructorID"] = id.Value;
  59:                  Instructor instructor = viewModel.Instructors.Where(
  60:                      i => i.ID == id.Value).Single();
  61:                  viewModel.Courses = instructor.CourseAssignments.Select(s => s.Course);
  62:              }
  63:   
  64:              if (courseID != null)
  65:              {
  66:                  ViewData["CourseID"] = courseID.Value;
  67:                  viewModel.Enrollments = viewModel.Courses.Where(
  68:                      x => x.CourseID == courseID).Single().Enrollments;
  69:              }
  70:   
  71:              return View(viewModel);
  72:          }
  73:          #endregion
  74:  #elif ExplicitLoading
  75:          #region snippet_ExplicitLoading
  76:          public async Task<IActionResult> Index(int? id, int? courseID)
  77:          {
  78:              var viewModel = new InstructorIndexData();
  79:              viewModel.Instructors = await _context.Instructors
  80:                    .Include(i => i.OfficeAssignment)
  81:                    .Include(i => i.CourseAssignments)
  82:                      .ThenInclude(i => i.Course)
  83:                          .ThenInclude(i => i.Department)
  84:                    .OrderBy(i => i.LastName)
  85:                    .ToListAsync();
  86:   
  87:              if (id != null)
  88:              {
  89:                  ViewData["InstructorID"] = id.Value;
  90:                  Instructor instructor = viewModel.Instructors.Where(
  91:                      i => i.ID == id.Value).Single();
  92:                  viewModel.Courses = instructor.CourseAssignments.Select(s => s.Course);
  93:              }
  94:   
  95:              if (courseID != null)
  96:              {
  97:                  ViewData["CourseID"] = courseID.Value;
  98:                  var selectedCourse = viewModel.Courses.Where(x => x.CourseID == courseID).Single();
  99:                  await _context.Entry(selectedCourse).Collection(x => x.Enrollments).LoadAsync();
 100:                  foreach (Enrollment enrollment in selectedCourse.Enrollments)
 101:                  {
 102:                      await _context.Entry(enrollment).Reference(x => x.Student).LoadAsync();
 103:                  }
 104:                  viewModel.Enrollments = selectedCourse.Enrollments;
 105:              }
 106:   
 107:              return View(viewModel);
 108:          }
 109:          #endregion
 110:  #endif
 111:          // GET: Instructors/Details/5
 112:          public async Task<IActionResult> Details(int? id)
 113:          {
 114:              if (id == null)
 115:              {
 116:                  return NotFound();
 117:              }
 118:   
 119:              var instructor = await _context.Instructors
 120:                  .AsNoTracking()
 121:                  .SingleOrDefaultAsync(m => m.ID == id);
 122:              if (instructor == null)
 123:              {
 124:                  return NotFound();
 125:              }
 126:   
 127:              return View(instructor);
 128:          }
 129:   
 130:          // GET: Instructors/Create
 131:          #region snippet_Create
 132:          public IActionResult Create()
 133:          {
 134:              var instructor = new Instructor();
 135:              instructor.CourseAssignments = new List<CourseAssignment>();
 136:              PopulateAssignedCourseData(instructor);
 137:              return View();
 138:          }
 139:   
 140:          // POST: Instructors/Create
 141:          [HttpPost]
 142:          [ValidateAntiForgeryToken]
 143:          public async Task<IActionResult> Create([Bind("FirstMidName,HireDate,LastName,OfficeAssignment")] Instructor instructor, string[] selectedCourses)
 144:          {
 145:              if (selectedCourses != null)
 146:              {
 147:                  instructor.CourseAssignments = new List<CourseAssignment>();
 148:                  foreach (var course in selectedCourses)
 149:                  {
 150:                      var courseToAdd = new CourseAssignment { InstructorID = instructor.ID, CourseID = int.Parse(course) };
 151:                      instructor.CourseAssignments.Add(courseToAdd);
 152:                  }
 153:              }
 154:              if (ModelState.IsValid)
 155:              {
 156:                  _context.Add(instructor);
 157:                  await _context.SaveChangesAsync();
 158:                  return RedirectToAction(nameof(Index));
 159:              }
 160:              PopulateAssignedCourseData(instructor);
 161:              return View(instructor);
 162:          }
 163:          #endregion
 164:   
 165:          // GET: Instructors/Edit/5
 166:  #if EditOfficeAssignment
 167:          #region snippet_EditGetOA
 168:          public async Task<IActionResult> Edit(int? id)
 169:          {
 170:              if (id == null)
 171:              {
 172:                  return NotFound();
 173:              }
 174:   
 175:              var instructor = await _context.Instructors
 176:                  .Include(i => i.OfficeAssignment)
 177:                  .AsNoTracking()
 178:                  .SingleOrDefaultAsync(m => m.ID == id);
 179:              if (instructor == null)
 180:              {
 181:                  return NotFound();
 182:              }
 183:              return View(instructor);
 184:          }
 185:          #endregion
 186:  #elif EditCourses
 187:          #region snippet_EditGetCourses
 188:          public async Task<IActionResult> Edit(int? id)
 189:          {
 190:              if (id == null)
 191:              {
 192:                  return NotFound();
 193:              }
 194:   
 195:              var instructor = await _context.Instructors
 196:                  .Include(i => i.OfficeAssignment)
 197:                  .Include(i => i.CourseAssignments).ThenInclude(i => i.Course)
 198:                  .AsNoTracking()
 199:                  .SingleOrDefaultAsync(m => m.ID == id);
 200:              if (instructor == null)
 201:              {
 202:                  return NotFound();
 203:              }
 204:              PopulateAssignedCourseData(instructor);
 205:              return View(instructor);
 206:          }
 207:   
 208:          private void PopulateAssignedCourseData(Instructor instructor)
 209:          {
 210:              var allCourses = _context.Courses;
 211:              var instructorCourses = new HashSet<int>(instructor.CourseAssignments.Select(c => c.CourseID));
 212:              var viewModel = new List<AssignedCourseData>();
 213:              foreach (var course in allCourses)
 214:              {
 215:                  viewModel.Add(new AssignedCourseData
 216:                  {
 217:                      CourseID = course.CourseID,
 218:                      Title = course.Title,
 219:                      Assigned = instructorCourses.Contains(course.CourseID)
 220:                  });
 221:              }
 222:              ViewData["Courses"] = viewModel;
 223:          }
 224:          #endregion
 225:  #endif
 226:          // POST: Instructors/Edit/5
 227:          // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
 228:          // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
 229:  #if EditOfficeAssignment
 230:          #region snippet_EditPostOA
 231:          [HttpPost, ActionName("Edit")]
 232:          [ValidateAntiForgeryToken]
 233:          public async Task<IActionResult> EditPost(int? id)
 234:          {
 235:              if (id == null)
 236:              {
 237:                  return NotFound();
 238:              }
 239:   
 240:              var instructorToUpdate = await _context.Instructors
 241:                  .Include(i => i.OfficeAssignment)
 242:                  .SingleOrDefaultAsync(s => s.ID == id);
 243:   
 244:              if (await TryUpdateModelAsync<Instructor>(
 245:                  instructorToUpdate,
 246:                  "",
 247:                  i => i.FirstMidName, i => i.LastName, i => i.HireDate, i => i.OfficeAssignment))
 248:              {
 249:                  if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment?.Location))
 250:                  {
 251:                      instructorToUpdate.OfficeAssignment = null;
 252:                  }
 253:                  try
 254:                  {
 255:                      await _context.SaveChangesAsync();
 256:                  }
 257:                  catch (DbUpdateException /* ex */)
 258:                  {
 259:                      //Log the error (uncomment ex variable name and write a log.)
 260:                      ModelState.AddModelError("", "Unable to save changes. " +
 261:                          "Try again, and if the problem persists, " +
 262:                          "see your system administrator.");
 263:                  }
 264:                  return RedirectToAction(nameof(Index));
 265:              }
 266:              return View(instructorToUpdate);
 267:          }
 268:          #endregion
 269:   
 270:  #elif EditCourses
 271:          #region snippet_EditPostCourses
 272:          [HttpPost]
 273:          [ValidateAntiForgeryToken]
 274:          public async Task<IActionResult> Edit(int? id, string[] selectedCourses)
 275:          {
 276:              if (id == null)
 277:              {
 278:                  return NotFound();
 279:              }
 280:   
 281:              var instructorToUpdate = await _context.Instructors
 282:                  .Include(i => i.OfficeAssignment)
 283:                  .Include(i => i.CourseAssignments)
 284:                      .ThenInclude(i => i.Course)
 285:                  .SingleOrDefaultAsync(m => m.ID == id);
 286:   
 287:              if (await TryUpdateModelAsync<Instructor>(
 288:                  instructorToUpdate,
 289:                  "",
 290:                  i => i.FirstMidName, i => i.LastName, i => i.HireDate, i => i.OfficeAssignment))
 291:              {
 292:                  if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment?.Location))
 293:                  {
 294:                      instructorToUpdate.OfficeAssignment = null;
 295:                  }
 296:                  UpdateInstructorCourses(selectedCourses, instructorToUpdate);
 297:                  try
 298:                  {
 299:                      await _context.SaveChangesAsync();
 300:                  }
 301:                  catch (DbUpdateException /* ex */)
 302:                  {
 303:                      //Log the error (uncomment ex variable name and write a log.)
 304:                      ModelState.AddModelError("", "Unable to save changes. " +
 305:                          "Try again, and if the problem persists, " +
 306:                          "see your system administrator.");
 307:                  }
 308:                  return RedirectToAction(nameof(Index));
 309:              }
 310:              UpdateInstructorCourses(selectedCourses, instructorToUpdate);
 311:              PopulateAssignedCourseData(instructorToUpdate);
 312:              return View(instructorToUpdate);
 313:          }
 314:          #endregion
 315:   
 316:          #region snippet_UpdateCourses
 317:          private void UpdateInstructorCourses(string[] selectedCourses, Instructor instructorToUpdate)
 318:          {
 319:              if (selectedCourses == null)
 320:              {
 321:                  instructorToUpdate.CourseAssignments = new List<CourseAssignment>();
 322:                  return;
 323:              }
 324:   
 325:              var selectedCoursesHS = new HashSet<string>(selectedCourses);
 326:              var instructorCourses = new HashSet<int>
 327:                  (instructorToUpdate.CourseAssignments.Select(c => c.Course.CourseID));
 328:              foreach (var course in _context.Courses)
 329:              {
 330:                  if (selectedCoursesHS.Contains(course.CourseID.ToString()))
 331:                  {
 332:                      if (!instructorCourses.Contains(course.CourseID))
 333:                      {
 334:                          instructorToUpdate.CourseAssignments.Add(new CourseAssignment { InstructorID = instructorToUpdate.ID, CourseID = course.CourseID });
 335:                      }
 336:                  }
 337:                  else
 338:                  {
 339:   
 340:                      if (instructorCourses.Contains(course.CourseID))
 341:                      {
 342:                          CourseAssignment courseToRemove = instructorToUpdate.CourseAssignments.SingleOrDefault(i => i.CourseID == course.CourseID);
 343:                          _context.Remove(courseToRemove);
 344:                      }
 345:                  }
 346:              }
 347:          }
 348:          #endregion
 349:  #endif
 350:          // GET: Instructors/Delete/5
 351:          public async Task<IActionResult> Delete(int? id)
 352:          {
 353:              if (id == null)
 354:              {
 355:                  return NotFound();
 356:              }
 357:   
 358:              var instructor = await _context.Instructors
 359:                  .AsNoTracking()
 360:                  .SingleOrDefaultAsync(m => m.ID == id);
 361:              if (instructor == null)
 362:              {
 363:                  return NotFound();
 364:              }
 365:   
 366:              return View(instructor);
 367:          }
 368:   
 369:          #region snippet_DeleteConfirmed
 370:          [HttpPost, ActionName("Delete")]
 371:          [ValidateAntiForgeryToken]
 372:          public async Task<IActionResult> DeleteConfirmed(int id)
 373:          {
 374:              Instructor instructor = await _context.Instructors
 375:                  .Include(i => i.CourseAssignments)
 376:                  .SingleAsync(i => i.ID == id);
 377:   
 378:              var departments = await _context.Departments
 379:                  .Where(d => d.InstructorID == id)
 380:                  .ToListAsync();
 381:              departments.ForEach(d => d.InstructorID = null);
 382:   
 383:              _context.Instructors.Remove(instructor);
 384:   
 385:              await _context.SaveChangesAsync();
 386:              return RedirectToAction(nameof(Index));
 387:          }
 388:          #endregion
 389:      }
 390:  }

This code makes the following changes:

Add office location and courses to the Create page

In InstructorsController.cs, delete the HttpGet and HttpPost Create methods, and then add the following code in their place:

[!code-csharpMain]

   1:  #define ExplicitLoading // or EagerLoading or ScaffoldedCode
   2:  #define EditCourses // or EditOfficeAssignment
   3:   
   4:  using System;
   5:  using System.Collections.Generic;
   6:  using System.Linq;
   7:  using System.Threading.Tasks;
   8:  using Microsoft.AspNetCore.Mvc;
   9:  using Microsoft.AspNetCore.Mvc.Rendering;
  10:  using Microsoft.EntityFrameworkCore;
  11:  using ContosoUniversity.Data;
  12:  using ContosoUniversity.Models;
  13:  #region snippet_Using
  14:  using ContosoUniversity.Models.SchoolViewModels;
  15:  #endregion
  16:   
  17:  namespace ContosoUniversity.Controllers
  18:  {
  19:      public class InstructorsController : Controller
  20:      {
  21:          private readonly SchoolContext _context;
  22:   
  23:          public InstructorsController(SchoolContext context)
  24:          {
  25:              _context = context;
  26:          }
  27:   
  28:          // GET: Instructors
  29:  #if ScaffoldedCode
  30:          #region snippet_ScaffoldedCode
  31:          public async Task<IActionResult> Index()
  32:          {
  33:              return View(await _context.Instructors.AsNoTracking().ToListAsync());
  34:          }
  35:          #endregion
  36:  #elif EagerLoading
  37:          #region snippet_EagerLoading
  38:          public async Task<IActionResult> Index(int? id, int? courseID)
  39:          {
  40:              var viewModel = new InstructorIndexData();
  41:              #region snippet_ThenInclude
  42:              viewModel.Instructors = await _context.Instructors
  43:                    .Include(i => i.OfficeAssignment)
  44:                    .Include(i => i.CourseAssignments)
  45:                      .ThenInclude(i => i.Course)
  46:                          .ThenInclude(i => i.Enrollments)
  47:                              .ThenInclude(i => i.Student)
  48:                    .Include(i => i.CourseAssignments)
  49:                      .ThenInclude(i => i.Course)
  50:                          .ThenInclude(i => i.Department)
  51:                    .AsNoTracking()
  52:                    .OrderBy(i => i.LastName)
  53:                    .ToListAsync();
  54:              #endregion
  55:              
  56:              if (id != null)
  57:              {
  58:                  ViewData["InstructorID"] = id.Value;
  59:                  Instructor instructor = viewModel.Instructors.Where(
  60:                      i => i.ID == id.Value).Single();
  61:                  viewModel.Courses = instructor.CourseAssignments.Select(s => s.Course);
  62:              }
  63:   
  64:              if (courseID != null)
  65:              {
  66:                  ViewData["CourseID"] = courseID.Value;
  67:                  viewModel.Enrollments = viewModel.Courses.Where(
  68:                      x => x.CourseID == courseID).Single().Enrollments;
  69:              }
  70:   
  71:              return View(viewModel);
  72:          }
  73:          #endregion
  74:  #elif ExplicitLoading
  75:          #region snippet_ExplicitLoading
  76:          public async Task<IActionResult> Index(int? id, int? courseID)
  77:          {
  78:              var viewModel = new InstructorIndexData();
  79:              viewModel.Instructors = await _context.Instructors
  80:                    .Include(i => i.OfficeAssignment)
  81:                    .Include(i => i.CourseAssignments)
  82:                      .ThenInclude(i => i.Course)
  83:                          .ThenInclude(i => i.Department)
  84:                    .OrderBy(i => i.LastName)
  85:                    .ToListAsync();
  86:   
  87:              if (id != null)
  88:              {
  89:                  ViewData["InstructorID"] = id.Value;
  90:                  Instructor instructor = viewModel.Instructors.Where(
  91:                      i => i.ID == id.Value).Single();
  92:                  viewModel.Courses = instructor.CourseAssignments.Select(s => s.Course);
  93:              }
  94:   
  95:              if (courseID != null)
  96:              {
  97:                  ViewData["CourseID"] = courseID.Value;
  98:                  var selectedCourse = viewModel.Courses.Where(x => x.CourseID == courseID).Single();
  99:                  await _context.Entry(selectedCourse).Collection(x => x.Enrollments).LoadAsync();
 100:                  foreach (Enrollment enrollment in selectedCourse.Enrollments)
 101:                  {
 102:                      await _context.Entry(enrollment).Reference(x => x.Student).LoadAsync();
 103:                  }
 104:                  viewModel.Enrollments = selectedCourse.Enrollments;
 105:              }
 106:   
 107:              return View(viewModel);
 108:          }
 109:          #endregion
 110:  #endif
 111:          // GET: Instructors/Details/5
 112:          public async Task<IActionResult> Details(int? id)
 113:          {
 114:              if (id == null)
 115:              {
 116:                  return NotFound();
 117:              }
 118:   
 119:              var instructor = await _context.Instructors
 120:                  .AsNoTracking()
 121:                  .SingleOrDefaultAsync(m => m.ID == id);
 122:              if (instructor == null)
 123:              {
 124:                  return NotFound();
 125:              }
 126:   
 127:              return View(instructor);
 128:          }
 129:   
 130:          // GET: Instructors/Create
 131:          #region snippet_Create
 132:          public IActionResult Create()
 133:          {
 134:              var instructor = new Instructor();
 135:              instructor.CourseAssignments = new List<CourseAssignment>();
 136:              PopulateAssignedCourseData(instructor);
 137:              return View();
 138:          }
 139:   
 140:          // POST: Instructors/Create
 141:          [HttpPost]
 142:          [ValidateAntiForgeryToken]
 143:          public async Task<IActionResult> Create([Bind("FirstMidName,HireDate,LastName,OfficeAssignment")] Instructor instructor, string[] selectedCourses)
 144:          {
 145:              if (selectedCourses != null)
 146:              {
 147:                  instructor.CourseAssignments = new List<CourseAssignment>();
 148:                  foreach (var course in selectedCourses)
 149:                  {
 150:                      var courseToAdd = new CourseAssignment { InstructorID = instructor.ID, CourseID = int.Parse(course) };
 151:                      instructor.CourseAssignments.Add(courseToAdd);
 152:                  }
 153:              }
 154:              if (ModelState.IsValid)
 155:              {
 156:                  _context.Add(instructor);
 157:                  await _context.SaveChangesAsync();
 158:                  return RedirectToAction(nameof(Index));
 159:              }
 160:              PopulateAssignedCourseData(instructor);
 161:              return View(instructor);
 162:          }
 163:          #endregion
 164:   
 165:          // GET: Instructors/Edit/5
 166:  #if EditOfficeAssignment
 167:          #region snippet_EditGetOA
 168:          public async Task<IActionResult> Edit(int? id)
 169:          {
 170:              if (id == null)
 171:              {
 172:                  return NotFound();
 173:              }
 174:   
 175:              var instructor = await _context.Instructors
 176:                  .Include(i => i.OfficeAssignment)
 177:                  .AsNoTracking()
 178:                  .SingleOrDefaultAsync(m => m.ID == id);
 179:              if (instructor == null)
 180:              {
 181:                  return NotFound();
 182:              }
 183:              return View(instructor);
 184:          }
 185:          #endregion
 186:  #elif EditCourses
 187:          #region snippet_EditGetCourses
 188:          public async Task<IActionResult> Edit(int? id)
 189:          {
 190:              if (id == null)
 191:              {
 192:                  return NotFound();
 193:              }
 194:   
 195:              var instructor = await _context.Instructors
 196:                  .Include(i => i.OfficeAssignment)
 197:                  .Include(i => i.CourseAssignments).ThenInclude(i => i.Course)
 198:                  .AsNoTracking()
 199:                  .SingleOrDefaultAsync(m => m.ID == id);
 200:              if (instructor == null)
 201:              {
 202:                  return NotFound();
 203:              }
 204:              PopulateAssignedCourseData(instructor);
 205:              return View(instructor);
 206:          }
 207:   
 208:          private void PopulateAssignedCourseData(Instructor instructor)
 209:          {
 210:              var allCourses = _context.Courses;
 211:              var instructorCourses = new HashSet<int>(instructor.CourseAssignments.Select(c => c.CourseID));
 212:              var viewModel = new List<AssignedCourseData>();
 213:              foreach (var course in allCourses)
 214:              {
 215:                  viewModel.Add(new AssignedCourseData
 216:                  {
 217:                      CourseID = course.CourseID,
 218:                      Title = course.Title,
 219:                      Assigned = instructorCourses.Contains(course.CourseID)
 220:                  });
 221:              }
 222:              ViewData["Courses"] = viewModel;
 223:          }
 224:          #endregion
 225:  #endif
 226:          // POST: Instructors/Edit/5
 227:          // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
 228:          // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
 229:  #if EditOfficeAssignment
 230:          #region snippet_EditPostOA
 231:          [HttpPost, ActionName("Edit")]
 232:          [ValidateAntiForgeryToken]
 233:          public async Task<IActionResult> EditPost(int? id)
 234:          {
 235:              if (id == null)
 236:              {
 237:                  return NotFound();
 238:              }
 239:   
 240:              var instructorToUpdate = await _context.Instructors
 241:                  .Include(i => i.OfficeAssignment)
 242:                  .SingleOrDefaultAsync(s => s.ID == id);
 243:   
 244:              if (await TryUpdateModelAsync<Instructor>(
 245:                  instructorToUpdate,
 246:                  "",
 247:                  i => i.FirstMidName, i => i.LastName, i => i.HireDate, i => i.OfficeAssignment))
 248:              {
 249:                  if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment?.Location))
 250:                  {
 251:                      instructorToUpdate.OfficeAssignment = null;
 252:                  }
 253:                  try
 254:                  {
 255:                      await _context.SaveChangesAsync();
 256:                  }
 257:                  catch (DbUpdateException /* ex */)
 258:                  {
 259:                      //Log the error (uncomment ex variable name and write a log.)
 260:                      ModelState.AddModelError("", "Unable to save changes. " +
 261:                          "Try again, and if the problem persists, " +
 262:                          "see your system administrator.");
 263:                  }
 264:                  return RedirectToAction(nameof(Index));
 265:              }
 266:              return View(instructorToUpdate);
 267:          }
 268:          #endregion
 269:   
 270:  #elif EditCourses
 271:          #region snippet_EditPostCourses
 272:          [HttpPost]
 273:          [ValidateAntiForgeryToken]
 274:          public async Task<IActionResult> Edit(int? id, string[] selectedCourses)
 275:          {
 276:              if (id == null)
 277:              {
 278:                  return NotFound();
 279:              }
 280:   
 281:              var instructorToUpdate = await _context.Instructors
 282:                  .Include(i => i.OfficeAssignment)
 283:                  .Include(i => i.CourseAssignments)
 284:                      .ThenInclude(i => i.Course)
 285:                  .SingleOrDefaultAsync(m => m.ID == id);
 286:   
 287:              if (await TryUpdateModelAsync<Instructor>(
 288:                  instructorToUpdate,
 289:                  "",
 290:                  i => i.FirstMidName, i => i.LastName, i => i.HireDate, i => i.OfficeAssignment))
 291:              {
 292:                  if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment?.Location))
 293:                  {
 294:                      instructorToUpdate.OfficeAssignment = null;
 295:                  }
 296:                  UpdateInstructorCourses(selectedCourses, instructorToUpdate);
 297:                  try
 298:                  {
 299:                      await _context.SaveChangesAsync();
 300:                  }
 301:                  catch (DbUpdateException /* ex */)
 302:                  {
 303:                      //Log the error (uncomment ex variable name and write a log.)
 304:                      ModelState.AddModelError("", "Unable to save changes. " +
 305:                          "Try again, and if the problem persists, " +
 306:                          "see your system administrator.");
 307:                  }
 308:                  return RedirectToAction(nameof(Index));
 309:              }
 310:              UpdateInstructorCourses(selectedCourses, instructorToUpdate);
 311:              PopulateAssignedCourseData(instructorToUpdate);
 312:              return View(instructorToUpdate);
 313:          }
 314:          #endregion
 315:   
 316:          #region snippet_UpdateCourses
 317:          private void UpdateInstructorCourses(string[] selectedCourses, Instructor instructorToUpdate)
 318:          {
 319:              if (selectedCourses == null)
 320:              {
 321:                  instructorToUpdate.CourseAssignments = new List<CourseAssignment>();
 322:                  return;
 323:              }
 324:   
 325:              var selectedCoursesHS = new HashSet<string>(selectedCourses);
 326:              var instructorCourses = new HashSet<int>
 327:                  (instructorToUpdate.CourseAssignments.Select(c => c.Course.CourseID));
 328:              foreach (var course in _context.Courses)
 329:              {
 330:                  if (selectedCoursesHS.Contains(course.CourseID.ToString()))
 331:                  {
 332:                      if (!instructorCourses.Contains(course.CourseID))
 333:                      {
 334:                          instructorToUpdate.CourseAssignments.Add(new CourseAssignment { InstructorID = instructorToUpdate.ID, CourseID = course.CourseID });
 335:                      }
 336:                  }
 337:                  else
 338:                  {
 339:   
 340:                      if (instructorCourses.Contains(course.CourseID))
 341:                      {
 342:                          CourseAssignment courseToRemove = instructorToUpdate.CourseAssignments.SingleOrDefault(i => i.CourseID == course.CourseID);
 343:                          _context.Remove(courseToRemove);
 344:                      }
 345:                  }
 346:              }
 347:          }
 348:          #endregion
 349:  #endif
 350:          // GET: Instructors/Delete/5
 351:          public async Task<IActionResult> Delete(int? id)
 352:          {
 353:              if (id == null)
 354:              {
 355:                  return NotFound();
 356:              }
 357:   
 358:              var instructor = await _context.Instructors
 359:                  .AsNoTracking()
 360:                  .SingleOrDefaultAsync(m => m.ID == id);
 361:              if (instructor == null)
 362:              {
 363:                  return NotFound();
 364:              }
 365:   
 366:              return View(instructor);
 367:          }
 368:   
 369:          #region snippet_DeleteConfirmed
 370:          [HttpPost, ActionName("Delete")]
 371:          [ValidateAntiForgeryToken]
 372:          public async Task<IActionResult> DeleteConfirmed(int id)
 373:          {
 374:              Instructor instructor = await _context.Instructors
 375:                  .Include(i => i.CourseAssignments)
 376:                  .SingleAsync(i => i.ID == id);
 377:   
 378:              var departments = await _context.Departments
 379:                  .Where(d => d.InstructorID == id)
 380:                  .ToListAsync();
 381:              departments.ForEach(d => d.InstructorID = null);
 382:   
 383:              _context.Instructors.Remove(instructor);
 384:   
 385:              await _context.SaveChangesAsync();
 386:              return RedirectToAction(nameof(Index));
 387:          }
 388:          #endregion
 389:      }
 390:  }

This code is similar to what you saw for the Edit methods except that initially no courses are selected. The HttpGet Create method calls the PopulateAssignedCourseData method not because there might be courses selected but in order to provide an empty collection for the foreach loop in the view (otherwise the view code would throw a null reference exception).

The HttpPost Create method adds each selected course to the CourseAssignments navigation property before it checks for validation errors and adds the new instructor to the database. Courses are added even if there are model errors so that when there are model errors (for an example, the user keyed an invalid date), and the page is redisplayed with an error message, any course selections that were made are automatically restored.

Notice that in order to be able to add courses to the CourseAssignments navigation property you have to initialize the property as an empty collection:

As an alternative to doing this in controller code, you could do it in the Instructor model by changing the property getter to automatically create the collection if it doesn’t exist, as shown in the following example:

If you modify the CourseAssignments property in this way, you can remove the explicit property initialization code in the controller.

In Views/Instructor/Create.cshtml, add an office location text box and check boxes for courses before the Submit button. As in the case of the Edit page, fix the formatting if Visual Studio reformats the code when you paste it.

[!code-htmlMain]

   1:  @model ContosoUniversity.Models.Instructor
   2:   
   3:  @{
   4:      ViewData["Title"] = "Create";
   5:  }
   6:   
   7:  <h2>Create</h2>
   8:   
   9:  <form asp-action="Create">
  10:      <div class="form-horizontal">
  11:          <h4>Instructor</h4>
  12:          <hr />
  13:          <div asp-validation-summary="ModelOnly" class="text-danger"></div>
  14:          <div class="form-group">
  15:              <label asp-for="LastName" class="control-label"></label>
  16:              <input asp-for="LastName" class="form-control" />
  17:              <span asp-validation-for="LastName" class="text-danger"></span>
  18:          </div>
  19:          <div class="form-group">
  20:              <label asp-for="FirstMidName" class="control-label"></label>
  21:              <input asp-for="FirstMidName" class="form-control" />
  22:              <span asp-validation-for="FirstMidName" class="text-danger"></span>
  23:          </div>
  24:          <div class="form-group">
  25:              <label asp-for="HireDate" class="control-label"></label>
  26:              <input asp-for="HireDate" class="form-control" />
  27:              <span asp-validation-for="HireDate" class="text-danger"></span>
  28:          </div>
  29:          <div class="form-group">
  30:              <label asp-for="OfficeAssignment.Location" class="control-label"></label>
  31:              <input asp-for="OfficeAssignment.Location" class="form-control" />
  32:              <span asp-validation-for="OfficeAssignment.Location" class="text-danger" />
  33:          </div>
  34:   
  35:          <div class="form-group">
  36:              <div class="col-md-offset-2 col-md-10">
  37:                  <table>
  38:                      <tr>
  39:                          @{
  40:                              int cnt = 0;
  41:                              List<ContosoUniversity.Models.SchoolViewModels.AssignedCourseData> courses = ViewBag.Courses;
  42:   
  43:                              foreach (var course in courses)
  44:                              {
  45:                                  if (cnt++ % 3 == 0)
  46:                                  {
  47:                                      @:</tr><tr>
  48:                                  }
  49:                                  @:<td>
  50:                                      <input type="checkbox"
  51:                                             name="selectedCourses"
  52:                                             value="@course.CourseID"
  53:                                             @(Html.Raw(course.Assigned ? "checked=\"checked\"" : "")) />
  54:                                             @course.CourseID @:  @course.Title
  55:                                      @:</td>
  56:                              }
  57:                              @:</tr>
  58:                          }
  59:                  </table>
  60:              </div>
  61:          </div>
  62:   
  63:          <div class="form-group">
  64:              <div class="col-md-offset-2 col-md-10">
  65:                  <input type="submit" value="Create" class="btn btn-default" />
  66:              </div>
  67:          </div>
  68:      </div>
  69:  </form>
  70:   
  71:  <div>
  72:      <a asp-action="Index">Back to List</a>
  73:  </div>
  74:   
  75:  @section Scripts {
  76:      @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
  77:  }

Test by running the app and creating an instructor.

Handling Transactions

As explained in the CRUD tutorial, the Entity Framework implicitly implements transactions. For scenarios where you need more control – for example, if you want to include operations done outside of Entity Framework in a transaction – see Transactions.

Summary

You have now completed the introduction to working with related data. In the next tutorial you’ll see how to handle concurrency conflicts.

Previous Next





Comments ( )
<00>  <01>  <02>  <03>  <04>  <05>  <06>  <07>  <08>  <09>  <10>  <11>  <12>  <13>  <14>  <15>  <16>  <17>  <18>  <19>  <20>  <21>  <22>  <23
Link to this page: //www.vb-net.com/AspNet-DocAndSamples-2017/aspnetcore/data/ef-mvc/update-related-data.htm
<SITEMAP>  <MVC>  <ASP>  <NET>  <DATA>  <KIOSK>  <FLEX>  <SQL>  <NOTES>  <LINUX>  <MONO>  <FREEWARE>  <DOCS>  <ENG>  <CHAT ME>  <ABOUT ME>  < THANKS ME>