这是微软官方教程Getting Started with Entity Framework 6 Code First using MVC 5 系列的翻译,这里是第九篇:为ASP.NET MVC应用程序使用异步及存储过程html
原文:Async and Stored Procedures with the Entity Framework in an ASP.NET MVC Application 程序员
译文版权全部,谢绝全文转载——但您能够在您的网站上添加到该教程的连接。web
在以前的教程中,您已经学习了如何使用同步编程模型来读取和更新数据。在本教程中您将看到如何实现异步编程模型。由于可以更好地使用服务器资源,异步代码能够帮助应用程序更好地执行。数据库
在本教程中您还会看到如何使用存储过程对实体进行插入、更新和删除操做。编程
下面的插图显示了您将要编写的页面:windows
一个WEB服务器只有颇有限的可用线程,而且在高负载的状况下,可能全部的线程都会在使用中。当发生这种状况时,服务器将没法处理新的请求,直到有线程被释放。在同步代码的状况下,多个线程可能会关联起来,但实际上它们并不做任何工做而只是在等待IO完成。使用异步代码,当一个进程正在等待IO完成时,它的线程能够服务器腾出从而用于处理其余请求。所以,异步代码能够更高效地使用服务器资源,而且服务器可以在不延迟的状况下处理更多的流量。安全
在较早版本的.NET中,编写和测试异步代码是一件复杂、容易出错且难以调试的工做。在.Net 4.5中,编写、测试和调试异步代码变得简单起来,你应当老是使用异步代码,除非有理由不容许你这样作。异步代码会花费不多量的开销,但对于低流量状况下性能的损失是微不足道的。对于高流量的状况下,潜在的性能提示是巨大的。服务器
有关异步编程的更多信息,请参阅 Use .NET 4.5’s async support to avoid blocking calls。mvc
使用以前你建立其余控制器相同的方式来建立一个系控制器,但此次咱们选择使用异步控制器操做选项。app
下面的代码中,高亮部分显示了异步方法和同步方法的不一样之处:
public async Task<ActionResult> Index() { var departments = db.Departments.Include(d => d.Administrator); return View(await departments.ToListAsync()); }
咱们应用了四个更改来启用实体框架数据库执行异步查询:
为何只修改departments.ToList语句而不是departments= db.Departments语句?缘由是只有被发送的数据库执行的查询或语句才可以使用异步执行。departments=db.Departments语句设置了一个查询,但直到调用ToList方法时该查询都不会执行。所以,只有ToList方法是异步执行的。
在Details方法和Httpget的Edit和Delete方法中,Find方法是致使查询被发送到数据库进行检索的方法,因此该方法是能够异步执行的。
public async Task<ActionResult> Details(int? id) { if (id == null) { return new HttpStatusCodeResult(HttpStatusCode.BadRequest); } Department department = await db.Departments.FindAsync(id); if (department == null) { return HttpNotFound(); } return View(department); }
在Create,HttpPost的Edit和DeleteConfirmed方法中,是SaveChanges方法致使命令执行,而像db.Department.Add(department)方法只是致使实体在内存中的修改。
public async Task<ActionResult> Create([Bind(Include="DepartmentID,Name,Budget,StartDate,InstructorID")] Department department) { if (ModelState.IsValid) { db.Departments.Add(department); await db.SaveChangesAsync(); return RedirectToAction("Index"); } ViewBag.InstructorID = new SelectList(db.Instructors, "ID", "LastName", department.InstructorID); return View(department); }
打开Views\Department\Index.cshtml,并使用下面的代码替换原来的:
@model IEnumerable<ContosoUniversity.Models.Department> @{ ViewBag.Title = "Departments"; } <h2>Departments</h2> <p> @Html.ActionLink("Create New", "Create") </p> <table class="table"> <tr> <th> @Html.DisplayNameFor(model => model.Name) </th> <th> @Html.DisplayNameFor(model => model.Budget) </th> <th> @Html.DisplayNameFor(model => model.StartDate) </th> <th>Administrator</th> <th></th> </tr> @foreach (var item in Model) { <tr> <td> @Html.DisplayFor(modelItem => item.Name) </td> <td> @Html.DisplayFor(modelItem => item.Budget) </td> <td> @Html.DisplayFor(modelItem => item.StartDate) </td> <td> @Html.DisplayFor(modelItem => item.Administrator.FullName) </td> <td> @Html.ActionLink("Edit", "Edit", new { id = item.DepartmentID }) | @Html.ActionLink("Details", "Details", new { id = item.DepartmentID }) | @Html.ActionLink("Delete", "Delete", new { id = item.DepartmentID }) </td> </tr> } </table>
代码修改了标题,并将系主任列移动到右边,同时提供了系主任的姓名。
在建立、删除、详情和编辑视图中,将InstructorID字段的标题更改成"系主任",相似以前你在课程视图中将系名称字段更改成"系"中那样。
在建立和编辑视图使用下面的代码:
@Html.DisplayFor(model => model.Department.Name)
在删除和详细视图使用下面的代码:
<dt> Administrator </dt>
运行该应用程序,并点击系选项卡。
程序正常地运行,就跟其余的控制器同样。但在此控制器中,全部SQL查询都是异步执行的。
当您在实体框架中使用异步编程要注意的一些事情:
一些开发人员和DBA更愿意使用存储过程来访问数据库。早期版本的实体框架中,您可使用执行原始SQL查询的方式来检索数据来执行存储过程,但您不能使用存储过程来用于更新操做。在实体框架6中,您能够很容易地配置Code First来使用存储过程。
protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Conventions.Remove<PluralizingTableNameConvention>(); modelBuilder.Entity<Course>() .HasMany(c => c.Instructors).WithMany(i => i.Courses) .Map(t => t.MapLeftKey("CourseID") .MapRightKey("InstructorID") .ToTable("CourseInstructor")); modelBuilder.Entity<Department>().MapToStoredProcedures(); }
此代码指示实体框架使用存储过程来进行Department实体的插入、更新和删除操做。
add-migration DepartmentSP
打开Migrations\<时间戳>_DepartmentSP.cs,参阅Up方法中的代码,你会看到插入、更新和删除的存储过程:
public override void Up() { CreateStoredProcedure( "dbo.Department_Insert", p => new { Name = p.String(maxLength: 50), Budget = p.Decimal(precision: 19, scale: 4, storeType: "money"), StartDate = p.DateTime(), InstructorID = p.Int(), }, body: @"INSERT [dbo].[Department]([Name], [Budget], [StartDate], [InstructorID]) VALUES (@Name, @Budget, @StartDate, @InstructorID) DECLARE @DepartmentID int SELECT @DepartmentID = [DepartmentID] FROM [dbo].[Department] WHERE @@ROWCOUNT > 0 AND [DepartmentID] = scope_identity() SELECT t0.[DepartmentID] FROM [dbo].[Department] AS t0 WHERE @@ROWCOUNT > 0 AND t0.[DepartmentID] = @DepartmentID" ); CreateStoredProcedure( "dbo.Department_Update", p => new { DepartmentID = p.Int(), Name = p.String(maxLength: 50), Budget = p.Decimal(precision: 19, scale: 4, storeType: "money"), StartDate = p.DateTime(), InstructorID = p.Int(), }, body: @"UPDATE [dbo].[Department] SET [Name] = @Name, [Budget] = @Budget, [StartDate] = @StartDate, [InstructorID] = @InstructorID WHERE ([DepartmentID] = @DepartmentID)" ); CreateStoredProcedure( "dbo.Department_Delete", p => new { DepartmentID = p.Int(), }, body: @"DELETE [dbo].[Department] WHERE ([DepartmentID] = @DepartmentID)" ); }
update-database
Code First使用默认名建立了存储过程。若是您正在使用现有的数据库,您可能须要自定义存储过程的名称,有关如何操做的信息,请参阅Entity Framework Code First Insert/Update/Delete Stored Procedures 。
若是你想要自定义存储过程,你能够编辑迁移中的脚手架代码里的Up方法来建立存储过程。使用这种方法时您的更改将在应用迁移时或部署到生产环境后自动进行。
若是你想修改一个在以前的迁移中已经建立的的存储过程,你可使用Add-Migration命令来生成一个空白的迁移,而后手动编写代码调用AlterStoredProcedure方法。
本章跳过……
在本教程中,您看到了如何提升服务器效率。经过编写异步执行代码以及使用存储过程来进行插入、更新和删除操做。在接下来的教程中,您将看到当多个用户试图编辑同一条纪录时如何防止数据丢失。
Tom Dykstra - Tom Dykstra是微软Web平台及工具团队的高级程序员,做家。