共用方式為


教學課程:在 ASP.NET MVC 應用程式中搭配 EF 使用異步和預存程式

在先前的教學課程中,您已瞭解如何使用同步程序設計模型來讀取和更新數據。 在本教學課程中,您將瞭解如何實作異步程序設計模型。 異步程式代碼可協助應用程式執行得更好,因為它能更妥善地使用伺服器資源。

在本教學課程中,您也會瞭解如何使用預存程式在實體上插入、更新和刪除作業。

最後,您會將應用程式重新部署至 Azure,以及自您第一次部署以來實作的所有資料庫變更。

下列圖例顯示了您將操作的一些頁面。

部門頁面

建立部門

在本教學課程中,您已:

  • 瞭解異步程序代碼
  • 建立部門控制器
  • 使用預存程式
  • 部署至 Azure

必要條件

為何使用異步程序代碼

網頁伺服器的可用執行緒數量有限,而且在高負載情況下,可能會使用所有可用的執行緒。 發生此情況時,伺服器將無法處理新的要求,直到執行緒空出來。 使用同步程式碼,許多執行緒可能在實際上並未執行任何工作時受到占用,原因是在等候 I/O 完成。 使用非同步程式碼,處理程序在等候 I/O 完成時,其執行緒將會空出來以讓伺服器處理其他要求。 因此,異步程式代碼可讓伺服器資源更有效率地使用,並啟用伺服器來處理更多流量,而不會延遲。

在舊版 .NET 中,撰寫和測試異步程式代碼很複雜、容易出錯,而且難以偵錯。 在 .NET 4.5 中,撰寫、測試及偵錯異步程式代碼會比較容易,除非您有理由不要撰寫異步程式代碼,否則您通常應該撰寫異步程序代碼。 異步程式代碼確實會造成少量的額外負荷,但對於低流量的情況而言,效能命中是微不足道的,而對於高流量情況而言,潛在的效能改善是實質性的。

如需異步程式設計的詳細資訊,請參閱 使用 .NET 4.5 的異步支援來避免封鎖呼叫

建立部門控制器

建立部門控制器的方式與您執行先前控制器的方式相同,但這次請選取 [ 使用異步控制器動作 ] 複選框。

下列醒目提示顯示新增至同步程式代碼 Index 的內容,讓 方法變得異步:

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

已套用四項變更,讓 Entity Framework 資料庫查詢以異步方式執行:

  • 方法會 async 以 關鍵詞標示,它會告訴編譯程式產生方法主體部分的回呼,並自動建立 Task<ActionResult> 傳回的物件。
  • 傳回型別已從 ActionResult 變更為 Task<ActionResult>。 此 Task<T> 類型表示持續使用 類型 T的結果。
  • 關鍵詞 await 已套用至 Web 服務呼叫。 當編譯程式看到這個關鍵詞時,它會在幕後將 方法分割成兩個部分。 第一個部分會以異步方式啟動的作業結束。 第二個部分會放入作業完成時所呼叫的回呼方法。
  • 呼叫擴充方法的 ToList 異步版本。

departments.ToList為什麼語句已修改,但不是 departments = db.Departments 語句? 原因是只有會導致查詢或命令傳送至資料庫的語句會以異步方式執行。 departments = db.Departments語句會設定查詢,但在呼叫 方法之前ToList,不會執行查詢。 因此,只會 ToList 以異步方式執行 方法。

Details 方法和 和 Delete HttpGet Edit 方法中,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);
}

CreateHttpPost EditDeleteConfirmed 方法中,它是 SaveChanges 會導致命令執行的方法呼叫,而不是語句,例如 db.Departments.Add(department) 只會修改記憶體中的實體。

public async Task<ActionResult> Create(Department department)
{
    if (ModelState.IsValid)
    {
        db.Departments.Add(department);
    await db.SaveChangesAsync();
        return RedirectToAction("Index");
    }

開啟 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>

此程式代碼會將標題從 Index 變更為 Departments、將系統管理員名稱移至右側,並提供系統管理員的完整名稱。

在 [建立]、[刪除]、[詳細數據] 和 [編輯檢視] 中,將欄位的標題 InstructorID 變更為 「系統管理員」,就像您在課程檢視中將部門名稱欄位變更為 「部門」一樣。

在 [建立和編輯] 檢視中使用下列程序代碼:

<label class="control-label col-md-2" for="InstructorID">Administrator</label>

在 [刪除] 和 [詳細資料] 檢視中使用下列程式代碼:

<dt>
    Administrator
</dt>

執行應用程式,然後按兩下 [ 部門] 索引標籤

所有項目的運作方式與其他控制器相同,但在此控制器中,所有SQL查詢都會以異步方式執行。

當您搭配 Entity Framework 使用異步程式設計時,需要注意的一些事項:

  • 異步程式代碼不是安全線程。 換句話說,請勿嘗試使用相同的內容實例平行執行多個作業。
  • 若您想要充分利用非同步程式碼帶來的效能優點,請確保任何您正在使用的程式庫 (例如分頁) 也使用了非同步 (若它們有呼叫任何可能會傳送查詢到資料庫的 Entity Framework 方法的話)。

使用預存程式

某些開發人員和 DBA 偏好使用預存程式進行數據庫存取。 在舊版的 Entity Framework 中,您可以藉由 執行原始 SQL 查詢來擷取預存程式的數據,但無法指示 EF 使用預存程式進行更新作業。 在 EF 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();
    }
    

    此程式代碼會指示 Entity Framework 使用預存程式,在實體上 Department 插入、更新和刪除作業。

  2. 在 [套件管理控制台] 中,輸入下列命令:

    add-migration DepartmentSP

    開啟 Migrations\<timestamp>_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. 在 Visual Studio 中 ,查看 [輸出 ] 視窗中的記錄,以查看預存程式是用來插入新的 Department 數據列。

    部門插入SP

程式代碼首先會建立預設預存程式名稱。 如果您使用現有的資料庫,您可能需要自定義預存程式名稱,才能使用資料庫中已定義的預存程式。 如需如何執行此動作的詳細資訊,請參閱 Entity Framework 程式代碼第一次插入/更新/刪除預存程式

如果您想要自定義產生的預存程式,您可以編輯建立預存程式之移 Up 轉方法的 Scaffold 程式代碼。 如此一來,每當執行移轉時,您的變更就會反映,並在部署後於生產環境中自動執行移轉時套用至生產資料庫。

如果您想要變更在先前移轉中建立的現有預存程式,您可以使用 Add-Migration 命令產生空白移轉,然後手動撰寫呼叫 AlterStoredProcedure 方法的程式代碼。

部署至 Azure

本節要求您已完成本系列移轉和部署教學課程中的選擇性將應用程式部署至 Azure 一節。 如果您在本機專案中刪除資料庫來解決移轉錯誤,請略過本節。

  1. 在 Visual Studio 中,以滑鼠右鍵按兩下 方案總管 中的項目,然後從操作選單中選取 [發佈]。

  2. 按一下發行

    Visual Studio 會將應用程式部署至 Azure,而應用程式會在您的預設瀏覽器中開啟,並在 Azure 中執行。

  3. 測試應用程式以確認其運作正常。

    當您第一次執行存取資料庫的頁面時,Entity Framework 會執行所有 Up 移轉方法,讓資料庫以目前數據模型處於最新狀態。 您現在可以使用自上次部署后新增的所有網頁,包括您在本教學課程中新增的部門頁面。

取得程式碼

下載已完成的專案

其他資源

您可以在 ASP.NET 數據存取 - 建議的資源中找到 其他 Entity Framework 資源的連結。

下一步

在本教學課程中,您已:

  • 瞭解異步程序代碼
  • 已建立部門控制器
  • 已使用預存程式
  • 部署至 Azure

請前進到下一篇文章,以瞭解如何在多個用戶同時更新相同的實體時處理衝突。