這是微軟官方教程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平臺及工具團隊的高級程序員,做家。