After reading complex the data, it is time to be able to update it. This video fills the following points from the ASP.NET MVC tutorial:
- Customize Courses pages
- Add Instructors Edit page
- Add courses to Edit page
- Update Delete page
- Add office location and courses to Create page
I liked explicitly the generation of the checkboxes, making a relation to a corresponding table:
This is the html-razor syntax for the generation:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<div class="form-group"> <table> <tr> @{ List<ContosoUniversity.Models.SchoolViewModels.AssignedCourseData> courses = ViewBag.Courses; foreach (var course in courses) { @:</tr><tr><td> <input type="checkbox" name="selectedCourses" value="@course.CourseId" @(Html.Raw(course.Assigned ? "checked=\"checked\"" : ""))/> @course.CourseId @: @course.Title @:</td> } } </tr> </table> </div> |
In the InstructorController.cs the Edit POST and GETÂ methods look quite nicely as well – reading and writing from multiple database tables correctly:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
[HttpPost] [ValidateAntiForgeryToken] public async Task<IActionResult> Edit(int? id, string[] selectedCourses) { if (id == null) { return NotFound(); } var instructorToUpdate = await _context.Instructors .Include(i => i.OfficeAssignment) .Include(i => i.CourseAssignments) .ThenInclude(i=> i.Course) .FirstOrDefaultAsync(s => s.ID == id); if (await TryUpdateModelAsync<Instructor>( instructorToUpdate, "", i => i.FirstMidName, i => i.LastName, i => i.HireDate, i => i.OfficeAssignment)) { if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment?.Location)) { instructorToUpdate.OfficeAssignment = null; } UpdateInstructorCourses(selectedCourses, instructorToUpdate); try { await _context.SaveChangesAsync(); } catch (DbUpdateException) { ModelState.AddModelError("", "Unable to save changes. Please restart!"); } return RedirectToAction(nameof(Index)); } UpdateInstructorCourses(selectedCourses, instructorToUpdate); PopulateAssignedCourseData(instructorToUpdate); return View(instructorToUpdate); } // GET: Instructors/Edit/5 public async Task<IActionResult> Edit(int? id) { if (id == null) { return NotFound(); } var instructor = await _context.Instructors .Include(i => i.OfficeAssignment) .Include(i=> i.CourseAssignments) .ThenInclude(i=>i.Course) .AsNoTracking() .FirstOrDefaultAsync(m => m.ID == id); if (instructor == null) { return NotFound(); } PopulateAssignedCourseData(instructor); return View(instructor); } |
The additional methods PopulateAssignedCourseData() and UpdateInstructorCourses() do what their names say – pretty much rendering relevant course data implementing Update functionailty for the courses:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
private void UpdateInstructorCourses(string[] selectedCourses, Instructor instructorToUpdate) { if (selectedCourses == null) { instructorToUpdate.CourseAssignments = new List<CourseAssignment>(); return; } var selectedCoursesNotNull = new HashSet<string>(selectedCourses); var instructorCourses = new HashSet<int> (instructorToUpdate.CourseAssignments.Select(c => c.Course.CourseId)); foreach (var course in _context.Courses) { if (selectedCoursesNotNull.Contains(course.CourseId.ToString())) { if (!instructorCourses.Contains(course.CourseId)) { instructorToUpdate.CourseAssignments.Add(new CourseAssignment { InstructorId = instructorToUpdate.ID, CourseId = course.CourseId }); } } else { if (instructorCourses.Contains(course.CourseId)) { CourseAssignment courseToRemove = instructorToUpdate.CourseAssignments.FirstOrDefault(i => i.CourseId == course.CourseId); _context.Remove(courseToRemove); } } } } |
The Course.cs class used in the method below is in the Models folder:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
private void PopulateAssignedCourseData(Instructor instructor) { var allCourses = _context.Courses; var instructorCourses = new HashSet<int>(instructor.CourseAssignments.Select(c => c.CourseId)); var viewModel = new List<AssignedCourseData>(); foreach (var course in allCourses) { viewModel.Add(new AssignedCourseData { CourseId = course.CourseId, Title = course.Title, Assigned = instructorCourses.Contains(course.CourseId) }); } ViewData["Courses"] = viewModel; } |
Everything is available in GitHub – https://github.com/Vitosh/ASP/tree/master/EFCoreAsp.NetMvcWebApp/ContosoUniversity007
Enjoy! 🙂