共用方式為


教學課程:在 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 方法和 和 DeleteHttpGetEdit 方法中 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 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>

此程式碼會將標題從 [索引] 變更為 [部門]、將系統管理員名稱移至右側,並提供系統管理員的完整名稱。

在 [建立]、[刪除]、[詳細資料] 和 [編輯] 檢視中,將欄位的標題 InstructorID 變更為 「Administrator」,就像您在課程檢視中將部門名稱欄位變更為 「Department」。

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

<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 ,以查看建立 Insert、Update 和 Delete 預存程式的方法中的 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

Code First 會建立預設預存程式名稱。 如果您使用現有的資料庫,您可能需要自訂預存程式名稱,才能使用資料庫中已定義的預存程式。 如需如何執行此作業的資訊,請參閱 Entity Framework Code First Insert/Update/Delete 預存程式

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

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

部署至 Azure

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

  1. 在 Visual Studio 的 [方案總管] 中以滑鼠右鍵按一下專案,再選取內容功能表中的 [發佈]

  2. 按一下 [發佈]。

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

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

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

取得程式碼

下載已完成的專案

其他資源

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

後續步驟

在本教學課程中,您:

  • 瞭解非同步程式碼
  • 建立部門控制者
  • 使用的預存程式
  • 部署至 Azure

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