Hướng dẫn: Cập nhật dữ liệu liên quan - ASP.NET MVC với EF Core
Trong hướng dẫn trước, bạn đã hiển thị dữ liệu liên quan; trong hướng dẫn này, bạn sẽ cập nhật dữ liệu liên quan bằng cách cập nhật các trường foreign key (khóa ngoại) và các thuộc tính navigation (điều hướng).
Trong hướng dẫn này, bạn:
- Tùy chỉnh các trang Courses
- Thêm trang Instructors Edit
- Thêm các khóa học vào trang Edit
- Cập nhật trang Delete
- Thêm vị trí văn phòng và khóa học vào trang Create
Điều kiện tiên quyết
Tùy chỉnh các trang Courses
Khi một entity Course mới được tạo, nó phải có mối quan hệ với một khoa đã tồn tại. Để thuận tiện cho điều này, mã đã được scaffolded (tạo khung) bao gồm các phương thức controller và các view Create và Edit có chứa dropdown list (danh sách thả xuống) để chọn khoa. Dropdown list đặt thuộc tính foreign key Course.DepartmentID, và đó là tất cả những gì Entity Framework cần để tải thuộc tính navigation Department với entity Department thích hợp. Bạn sẽ sử dụng mã đã được scaffolded, nhưng thay đổi nó một chút để thêm xử lý lỗi và sắp xếp dropdown list.
Trong CoursesController.cs, xóa bốn phương thức Create và Edit và thay thế bằng mã sau:
public IActionResult Create()
{
PopulateDepartmentsDropDownList();
return View();
}[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("CourseID,Credits,DepartmentID,Title")] Course course)
{
if (ModelState.IsValid)
{
_context.Add(course);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
PopulateDepartmentsDropDownList(course.DepartmentID);
return View(course);
}public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var course = await _context.Courses
.AsNoTracking()
.FirstOrDefaultAsync(m => m.CourseID == id);
if (course == null)
{
return NotFound();
}
PopulateDepartmentsDropDownList(course.DepartmentID);
return View(course);
}[HttpPost, ActionName("Edit")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> EditPost(int? id)
{
if (id == null)
{
return NotFound();
}
var courseToUpdate = await _context.Courses
.FirstOrDefaultAsync(c => c.CourseID == id);
if (await TryUpdateModelAsync<Course>(courseToUpdate,
"",
c => c.Credits, c => c.DepartmentID, c => c.Title))
{
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateException /* ex */)
{
//Log the error (uncomment ex variable name and write a log.)
ModelState.AddModelError("", "Unable to save changes. " +
"Try again, and if the problem persists, " +
"see your system administrator.");
}
return RedirectToAction(nameof(Index));
}
PopulateDepartmentsDropDownList(courseToUpdate.DepartmentID);
return View(courseToUpdate);
}Sau phương thức Edit HttpPost, tạo phương thức mới tải thông tin khoa cho dropdown list:
private void PopulateDepartmentsDropDownList(object selectedDepartment = null)
{
var departmentsQuery = from d in _context.Departments
orderby d.Name
select d;
ViewBag.DepartmentID = new SelectList(departmentsQuery.AsNoTracking(), "DepartmentID", "Name", selectedDepartment);
}Phương thức PopulateDepartmentsDropDownList lấy danh sách tất cả các khoa được sắp xếp theo tên, tạo collection SelectList cho dropdown list và truyền collection cho view trong ViewBag. Phương thức chấp nhận tham số tùy chọn selectedDepartment cho phép mã gọi chỉ định item sẽ được chọn khi dropdown list được render.
Phương thức HttpGet Create gọi phương thức PopulateDepartmentsDropDownList mà không đặt item được chọn, vì đối với một khóa học mới, khoa chưa được thiết lập:
public IActionResult Create()
{
PopulateDepartmentsDropDownList();
return View();
}Phương thức HttpGet Edit đặt item được chọn dựa trên ID của khoa đã được phân công cho khóa học đang được chỉnh sửa:
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var course = await _context.Courses
.AsNoTracking()
.FirstOrDefaultAsync(m => m.CourseID == id);
if (course == null)
{
return NotFound();
}
PopulateDepartmentsDropDownList(course.DepartmentID);
return View(course);
}Thêm .AsNoTracking vào các phương thức Details và Delete
Để tối ưu hóa hiệu năng của các trang Course Details và Delete, thêm lệnh gọi AsNoTracking trong các phương thức Details và HttpGet Delete:
public async Task<IActionResult> Details(int? id)
{
if (id == null)
{
return NotFound();
}
var course = await _context.Courses
.Include(c => c.Department)
.AsNoTracking()
.FirstOrDefaultAsync(m => m.CourseID == id);
if (course == null)
{
return NotFound();
}
return View(course);
}public async Task<IActionResult> Delete(int? id)
{
if (id == null)
{
return NotFound();
}
var course = await _context.Courses
.Include(c => c.Department)
.AsNoTracking()
.FirstOrDefaultAsync(m => m.CourseID == id);
if (course == null)
{
return NotFound();
}
return View(course);
}Sửa đổi các Course view
Trong Views/Courses/Create.cshtml, thêm tùy chọn "Select Department" vào dropdown list Department, thay đổi caption từ DepartmentID thành Department, và thêm thông báo validation (xác thực):
<div class="form-group">
<label asp-for="Department" class="control-label"></label>
<select asp-for="DepartmentID" class="form-control" asp-items="ViewBag.DepartmentID">
<option value="">-- Select Department --</option>
</select>
<span asp-validation-for="DepartmentID" class="text-danger" />
</div>Trong Views/Courses/Edit.cshtml, thực hiện cùng thay đổi cho trường Department như bạn vừa làm trong Create.cshtml.
Cũng trong Views/Courses/Edit.cshtml, thêm trường số khóa học trước trường Title. Vì số khóa học là primary key (khóa chính), nó được hiển thị nhưng không thể thay đổi.
<div class="form-group">
<label asp-for="CourseID" class="control-label"></label>
<div>@Html.DisplayFor(model => model.CourseID)</div>
</div>Trong Views/Courses/Delete.cshtml, thêm trường số khóa học ở đầu và thay đổi ID khoa thành tên khoa.
@model ContosoUniversity.Models.Course
@{
ViewData["Title"] = "Delete";
}
<h2>Delete</h2>
<h3>Are you sure you want to delete this?</h3>
<div>
<h4>Course</h4>
<hr />
<dl class="row">
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.CourseID)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.CourseID)
</dd>
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.Title)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.Title)
</dd>
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.Credits)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.Credits)
</dd>
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.Department)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.Department.Name)
</dd>
</dl>
<form asp-action="Delete">
<div class="form-actions no-color">
<input type="submit" value="Delete" class="btn btn-default" /> |
<a asp-action="Index">Back to List</a>
</div>
</form>
</div>Trong Views/Courses/Details.cshtml, thực hiện cùng thay đổi như bạn vừa làm cho Delete.cshtml.
Kiểm tra các trang Course
Chạy ứng dụng, chọn tab Courses, nhấp Create New và nhập dữ liệu cho một khóa học mới.
Nhấp Create. Trang Courses Index được hiển thị với khóa học mới được thêm vào danh sách. Tên khoa trong danh sách trang Index đến từ thuộc tính navigation, cho thấy mối quan hệ đã được thiết lập đúng cách.
Nhấp Edit trên một khóa học trong trang Courses Index.
Thay đổi dữ liệu trên trang và nhấp Save. Trang Courses Index được hiển thị với dữ liệu khóa học đã cập nhật.
Thêm trang Instructors Edit
Khi bạn chỉnh sửa một bản ghi instructor (giảng viên), bạn muốn có thể cập nhật phân công văn phòng của instructor. Entity Instructor có mối quan hệ một-đến-không-hoặc-một với entity OfficeAssignment, có nghĩa mã của bạn phải xử lý các tình huống sau:
- Nếu người dùng xóa phân công văn phòng và nó ban đầu có giá trị, xóa entity
OfficeAssignment. - Nếu người dùng nhập giá trị phân công văn phòng và ban đầu nó rỗng, tạo entity
OfficeAssignmentmới. - Nếu người dùng thay đổi giá trị phân công văn phòng, thay đổi giá trị trong entity
OfficeAssignmenthiện có.
Cập nhật Instructors controller
Trong InstructorsController.cs, thay đổi mã trong phương thức HttpGet Edit để nó tải thuộc tính navigation OfficeAssignment của entity Instructor và gọi AsNoTracking:
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var instructor = await _context.Instructors
.Include(i => i.OfficeAssignment)
.AsNoTracking()
.FirstOrDefaultAsync(m => m.ID == id);
if (instructor == null)
{
return NotFound();
}
return View(instructor);
}Thay thế phương thức HttpPost Edit bằng mã sau để xử lý cập nhật phân công văn phòng:
[HttpPost, ActionName("Edit")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> EditPost(int? id)
{
if (id == null)
{
return NotFound();
}
var instructorToUpdate = await _context.Instructors
.Include(i => i.OfficeAssignment)
.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;
}
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateException /* ex */)
{
//Log the error (uncomment ex variable name and write a log.)
ModelState.AddModelError("", "Unable to save changes. " +
"Try again, and if the problem persists, " +
"see your system administrator.");
}
return RedirectToAction(nameof(Index));
}
return View(instructorToUpdate);
}Mã này thực hiện những việc sau:
- Đổi tên phương thức thành
EditPostvì chữ ký bây giờ giống với phương thức HttpGetEdit(thuộc tínhActionNamechỉ định rằng URL/Edit/vẫn được sử dụng). - Lấy entity
Instructorhiện tại từ cơ sở dữ liệu sử dụng eager loading cho thuộc tính navigationOfficeAssignment. - Cập nhật entity
Instructorđã lấy với các giá trị từ model binder (trình gắn model). Tải trọngTryUpdateModelcho phép bạn khai báo các thuộc tính muốn bao gồm. Điều này ngăn over-posting (đăng quá mức). - Nếu vị trí văn phòng trống, đặt thuộc tính
Instructor.OfficeAssignmentthành null để hàng liên quan trong bảngOfficeAssignmentsẽ bị xóa. - Lưu các thay đổi vào cơ sở dữ liệu.
Cập nhật Instructor Edit view
Trong Views/Instructors/Edit.cshtml, thêm trường mới để chỉnh sửa vị trí văn phòng, ở cuối trước nút Save:
<div class="form-group">
<label asp-for="OfficeAssignment.Location" class="control-label"></label>
<input asp-for="OfficeAssignment.Location" class="form-control" />
<span asp-validation-for="OfficeAssignment.Location" class="text-danger" />
</div>Chạy ứng dụng, chọn tab Instructors và nhấp Edit trên một instructor. Thay đổi Office Location và nhấp Save.
Thêm khóa học vào trang Edit
Instructors có thể giảng dạy bất kỳ số lượng khóa học nào. Bây giờ bạn sẽ nâng cao trang Instructor Edit bằng cách thêm khả năng thay đổi phân công khóa học bằng cách sử dụng một nhóm checkbox (hộp kiểm).
Mối quan hệ giữa các entity Course và Instructor là nhiều-đến-nhiều. Để thêm và xóa các mối quan hệ, bạn thêm và xóa các entity vào và từ entity set join CourseAssignments.
UI (Giao diện người dùng) cho phép bạn thay đổi các khóa học một instructor được phân công là một nhóm checkbox. Một checkbox cho mỗi khóa học trong cơ sở dữ liệu được hiển thị, và những khóa học mà instructor hiện đang được phân công sẽ được chọn. Người dùng có thể chọn hoặc bỏ chọn các checkbox để thay đổi phân công khóa học.
Cập nhật Instructors controller
Để cung cấp dữ liệu cho view cho danh sách checkbox, bạn sẽ sử dụng một class view model.
Tạo AssignedCourseData.cs trong thư mục SchoolViewModels và thay thế mã hiện có bằng mã sau:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace ContosoUniversity.Models.SchoolViewModels
{
public class AssignedCourseData
{
public int CourseID { get; set; }
public string Title { get; set; }
public bool Assigned { get; set; }
}
}Trong InstructorsController.cs, thay thế phương thức HttpGet Edit bằng mã sau:
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);
}
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;
}Mã thêm eager loading cho thuộc tính navigation Courses và gọi phương thức PopulateAssignedCourseData mới để cung cấp thông tin cho mảng checkbox sử dụng class view model AssignedCourseData.
Mã trong phương thức PopulateAssignedCourseData đọc qua tất cả các entity Course để tải danh sách khóa học sử dụng class view model. Đối với mỗi khóa học, mã kiểm tra xem khóa học có tồn tại trong thuộc tính navigation Courses của instructor không. Để tạo tra cứu hiệu quả khi kiểm tra liệu một khóa học có được phân công cho instructor hay không, các khóa học được phân công cho instructor được đưa vào một collection HashSet. Thuộc tính Assigned được đặt thành true cho các khóa học instructor được phân công. View sẽ sử dụng thuộc tính này để xác định các checkbox nào phải được hiển thị là được chọn.
Tiếp theo, thêm mã được thực thi khi người dùng nhấp Save. Thay thế phương thức EditPost bằng mã sau, và thêm phương thức mới cập nhật thuộc tính navigation Courses của entity Instructor:
[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(m => m.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 /* ex */)
{
//Log the error (uncomment ex variable name and write a log.)
ModelState.AddModelError("", "Unable to save changes. " +
"Try again, and if the problem persists, " +
"see your system administrator.");
}
return RedirectToAction(nameof(Index));
}
UpdateInstructorCourses(selectedCourses, instructorToUpdate);
PopulateAssignedCourseData(instructorToUpdate);
return View(instructorToUpdate);
}private void UpdateInstructorCourses(string[] selectedCourses, Instructor instructorToUpdate)
{
if (selectedCourses == null)
{
instructorToUpdate.CourseAssignments = new List<CourseAssignment>();
return;
}
var selectedCoursesHS = new HashSet<string>(selectedCourses);
var instructorCourses = new HashSet<int>
(instructorToUpdate.CourseAssignments.Select(c => c.Course.CourseID));
foreach (var course in _context.Courses)
{
if (selectedCoursesHS.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);
}
}
}
}Chữ ký phương thức bây giờ khác với phương thức HttpGet Edit, vì vậy tên phương thức thay đổi từ EditPost trở lại Edit.
Vì view không có collection của các entity Course, model binder không thể tự động cập nhật thuộc tính navigation CourseAssignments. Thay vì sử dụng model binder để cập nhật thuộc tính navigation CourseAssignments, bạn thực hiện điều đó trong phương thức UpdateInstructorCourses mới.
Mã sau đó lặp qua tất cả các khóa học trong cơ sở dữ liệu và kiểm tra từng khóa học so với các khóa học hiện đang được phân công cho instructor với các khóa học được chọn trong view. Để tạo thuận lợi cho các tra cứu hiệu quả, hai collection sau được lưu trữ trong các đối tượng HashSet.
Nếu checkbox cho một khóa học được chọn nhưng khóa học đó không có trong thuộc tính navigation Instructor.CourseAssignments, khóa học được thêm vào collection trong thuộc tính navigation.
Nếu checkbox cho một khóa học không được chọn, nhưng khóa học đó có trong thuộc tính navigation Instructor.CourseAssignments, khóa học bị xóa khỏi thuộc tính navigation.
Cập nhật các Instructor view
Trong Views/Instructors/Edit.cshtml, thêm trường Courses với một mảng checkbox bằng cách thêm mã sau ngay sau các phần tử div cho trường Office và trước phần tử div cho nút Save:
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<table>
<tr>
@{
int cnt = 0;
List<ContosoUniversity.Models.SchoolViewModels.AssignedCourseData> courses = ViewBag.Courses;
foreach (var course in courses)
{
if (cnt++ % 3 == 0)
{
@:</tr><tr>
}
@:<td>
<input type="checkbox"
name="selectedCourses"
value="@course.CourseID"
@(Html.Raw(course.Assigned ? "checked=\"checked\"" : "")) />
@course.CourseID @: @course.Title
@:</td>
}
@:</tr>
}
</table>
</div>
</div>Mã này tạo một bảng HTML có ba cột. Trong mỗi cột là một checkbox theo sau là caption (chú thích) bao gồm số và tiêu đề khóa học. Tất cả các checkbox đều có cùng tên ("selectedCourses"), thông báo cho model binder rằng chúng được coi như một nhóm. Thuộc tính value của mỗi checkbox được đặt bằng giá trị CourseID. Khi trang được post, model binder truyền một mảng cho controller bao gồm các giá trị CourseID chỉ cho các checkbox được chọn.
Khi các checkbox được render ban đầu, những checkbox cho các khóa học được phân công cho instructor có thuộc tính checked, chọn chúng (hiển thị là đã được chọn).
Chạy ứng dụng, chọn tab Instructors và nhấp Edit trên một instructor để xem trang Edit.
Thay đổi một số phân công khóa học và nhấp Save. Các thay đổi bạn thực hiện được phản ánh trên trang Index.
Cập nhật trang Delete
Trong InstructorsController.cs, xóa phương thức DeleteConfirmed và chèn mã sau vào vị trí của nó:
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteConfirmed(int id)
{
Instructor instructor = await _context.Instructors
.Include(i => i.CourseAssignments)
.SingleAsync(i => i.ID == id);
var departments = await _context.Departments
.Where(d => d.InstructorID == id)
.ToListAsync();
departments.ForEach(d => d.InstructorID = null);
_context.Instructors.Remove(instructor);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}Mã này thực hiện các thay đổi sau:
- Thực hiện eager loading cho thuộc tính navigation
CourseAssignments. Bạn phải bao gồm điều này, nếu không EF sẽ không biết về các entityCourseAssignmentliên quan và sẽ không xóa chúng. Để tránh cần đọc chúng ở đây, bạn có thể cấu hình cascade delete (xóa theo tầng) trong cơ sở dữ liệu. - Nếu instructor được xóa được phân công là quản trị viên của bất kỳ khoa nào, xóa phân công instructor khỏi các khoa đó.
Thêm vị trí văn phòng và khóa học vào trang Create
Trong InstructorsController.cs, xóa các phương thức HttpGet và HttpPost Create, sau đó thêm mã sau vào vị trí của chúng:
public IActionResult Create()
{
var instructor = new Instructor();
instructor.CourseAssignments = new List<CourseAssignment>();
PopulateAssignedCourseData(instructor);
return View();
}
// POST: Instructors/Create
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("FirstMidName,HireDate,LastName,OfficeAssignment")] Instructor instructor, string[] selectedCourses)
{
if (selectedCourses != null)
{
instructor.CourseAssignments = new List<CourseAssignment>();
foreach (var course in selectedCourses)
{
var courseToAdd = new CourseAssignment { InstructorID = instructor.ID, CourseID = int.Parse(course) };
instructor.CourseAssignments.Add(courseToAdd);
}
}
if (ModelState.IsValid)
{
_context.Add(instructor);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
PopulateAssignedCourseData(instructor);
return View(instructor);
}Mã này tương tự với những gì bạn đã thấy cho các phương thức Edit ngoại trừ ban đầu không có khóa học nào được chọn. Phương thức HttpGet Create gọi phương thức PopulateAssignedCourseData không phải vì có thể có khóa học được chọn mà để cung cấp một collection rỗng cho vòng lặp foreach trong view (nếu không mã view sẽ ném ra ngoại lệ null reference).
Phương thức HttpPost Create thêm mỗi khóa học được chọn vào thuộc tính navigation CourseAssignments trước khi kiểm tra lỗi validation (xác thực) và thêm instructor mới vào cơ sở dữ liệu.
Trong Views/Instructor/Create.cshtml, thêm ô text vị trí văn phòng và checkbox cho khóa học trước nút Submit:
<div class="form-group">
<label asp-for="OfficeAssignment.Location" class="control-label"></label>
<input asp-for="OfficeAssignment.Location" class="form-control" />
<span asp-validation-for="OfficeAssignment.Location" class="text-danger" />
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<table>
<tr>
@{
int cnt = 0;
List<ContosoUniversity.Models.SchoolViewModels.AssignedCourseData> courses = ViewBag.Courses;
foreach (var course in courses)
{
if (cnt++ % 3 == 0)
{
@:</tr><tr>
}
@:<td>
<input type="checkbox"
name="selectedCourses"
value="@course.CourseID"
@(Html.Raw(course.Assigned ? "checked=\"checked\"" : "")) />
@course.CourseID @: @course.Title
@:</td>
}
@:</tr>
}
</table>
</div>
</div>Kiểm tra bằng cách chạy ứng dụng và tạo một instructor.
Xử lý Transactions
Như đã giải thích trong hướng dẫn CRUD, Entity Framework triển khai transactions (giao dịch) ngầm định. Đối với các tình huống mà bạn cần kiểm soát nhiều hơn -- ví dụ, nếu bạn muốn bao gồm các thao tác được thực hiện bên ngoài Entity Framework trong một transaction -- xem Transactions.