[渣譯文] 使用 MVC 5 的 EF6 Code First 入門 系列:為ASP.NET MVC應用程序使用異步及存儲過程


這是微軟官方教程Getting Started with Entity Framework 6 Code First using MVC 5 系列的翻譯,這里是第九篇:為ASP.NET MVC應用程序使用異步及存儲過程

原文:Async and Stored Procedures with the Entity Framework in an ASP.NET MVC Application

譯文版權所有,謝絕全文轉載——但您可以在您的網站上添加到該教程的鏈接。

在之前的教程中,您已經學習了如何使用同步編程模型來讀取和更新數據。在本教程中您將看到如何實現異步編程模型。因為能夠更好地使用服務器資源,異步代碼可以幫助應用程序更好地執行。

在本教程中您還會看到如何使用存儲過程對實體進行插入、更新和刪除操作。

下面的插圖顯示了您將要編寫的頁面:

為什么要使用麻煩的異步代碼

一個WEB服務器只有很有限的可用線程,並且在高負載的情況下,可能所有的線程都會在使用中。當發生這種情況時,服務器將無法處理新的請求,直到有線程被釋放。在同步代碼的情況下,多個線程可能會關聯起來,但實際上它們並不作任何工作而只是在等待IO完成。使用異步代碼,當一個進程正在等待IO完成時,它的線程可以服務器騰出從而用於處理其他請求。因此,異步代碼可以更高效地使用服務器資源,並且服務器能夠在不延遲的情況下處理更多的流量。

在較早版本的.NET中,編寫和測試異步代碼是一件復雜、容易出錯且難以調試的工作。在.Net 4.5中,編寫、測試和調試異步代碼變得簡單起來,你應當總是使用異步代碼,除非有理由不允許你這樣做。異步代碼會花費很少量的開銷,但對於低流量情況下性能的損失是微不足道的。對於高流量的情況下,潛在的性能提示是巨大的。

有關異步編程的更多信息,請參閱 Use .NET 4.5’s async support to avoid blocking calls

創建系控制器

使用之前你創建其他控制器相同的方式來創建一個系控制器,但這次我們選擇使用異步控制器操作選項。

下面的代碼中,高亮部分顯示了異步方法和同步方法的不同之處:

        public async Task<ActionResult> Index()
        {
            var departments = db.Departments.Include(d => d.Administrator);
            return View(await departments.ToListAsync());
        }

 

我們應用了四個更改來啟用實體框架數據庫執行異步查詢:

  • 該方法使用了async關鍵字,它告訴編譯器生成回調方法體的部分,並自動創建Task<ActionResult>返回對象。
  • 返回類型從ActionResult更改為Task<ActionResult>。Task<T>類型表示正在進行的任務具有類型為T的結果。
  • await關鍵字被應用到web服務調用。當編譯器看到有此關鍵字時,在后台將該方法分為兩個部分。第一部分結束於異步操作啟動,第二部分被放入一個操作完成時的回調方法。
  • 調用了ToList擴展方法的異步版本。

為什么只修改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來使用存儲過程。

  1. 在DAL\SchoolContext.cs中,將高亮的代碼添加到OnModelCreating方法。
    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實體的插入、更新和刪除操作。

  2. 在程序包管理控制台中,輸入以下命令:
    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)"
                );
                
            }
  3. 在軟件包管理器控制台中,輸入以下命令:
    update-database 

     

  4. 在調試模型下運行該應用程序,單擊系選項卡,然后單擊創建。
  5. 為一個新系輸入相關數據然后單擊創建。
  6. 在VS中的輸出窗口查看日志。

Code First使用默認名創建了存儲過程。如果您正在使用現有的數據庫,您可能需要自定義存儲過程的名稱,有關如何操作的信息,請參閱Entity Framework Code First Insert/Update/Delete Stored Procedures

如果你想要自定義存儲過程,你可以編輯遷移中的腳手架代碼里的Up方法來創建存儲過程。使用這種方法時您的更改將在應用遷移時或部署到生產環境后自動進行。

如果你想修改一個在之前的遷移中已經創建的的存儲過程,你可以使用Add-Migration命令來生成一個空白的遷移,然后手動編寫代碼調用AlterStoredProcedure方法。

部署到Windows Azure

本章跳過……

總結

在本教程中,您看到了如何提高服務器效率。通過編寫異步執行代碼以及使用存儲過程來進行插入、更新和刪除操作。在接下來的教程中,您將看到當多個用戶試圖編輯同一條紀錄時如何防止數據丟失。

 

作者信息

 

  Tom Dykstra - Tom Dykstra是微軟Web平台及工具團隊的高級程序員,作家。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM