共用方式為


第 4 部分,將模型新增到 ASP.NET Core MVC 應用程式

注意

這不是這篇文章的最新版本。 如需目前版本,請參閱本文的 .NET 8 版本

警告

不再支援此版本的 ASP.NET Core。 如需詳細資訊,請參閱 .NET 和 .NET Core 支援原則。 如需目前版本,請參閱本文的 .NET 8 版本

重要

這些發行前產品的相關資訊在產品正式發行前可能會有大幅修改。 Microsoft 對此處提供的資訊,不做任何明確或隱含的瑕疵擔保。

如需目前版本,請參閱本文的 .NET 8 版本

作者:Rick AndersonJon P Smith

在本教學課程中,您可以新增類別來管理資料庫中的電影。 這些類別是 MVC 應用程式的「模型」部分。

搭配 Entity Framework Core (EF Core) 使用這些模型類別,即可使用資料庫。 EF Core 是一種物件關聯式對應 (ORM) 架構,可簡化您必須撰寫的資料存取程式碼。

建立的模型類別稱為 POCO 類別,全名為 Plain Old CLR Objects。 POCO 類別與 EF Core 沒有任何相依性。 它們只會定義資料儲存在資料庫中的屬性。

在本教學課程中,會先建立模型類別,以及 EF Core 建立資料庫。

新增資料模型類別

以滑鼠右鍵按一下 Models 資料夾 >[新增]>[類別]。 將檔案命名為 Movie.cs

使用下列程式碼更新 Models/Movie.cs 檔案:

using System.ComponentModel.DataAnnotations;

namespace MvcMovie.Models;

public class Movie
{
    public int Id { get; set; }
    public string? Title { get; set; }
    [DataType(DataType.Date)]
    public DateTime ReleaseDate { get; set; }
    public string? Genre { get; set; }
    public decimal Price { get; set; }
}

Movie 類別包含 Id 欄位,該欄位是資料庫的必要欄位,將作為主索引鍵。

ReleaseDate 上的 DataType 屬性會指定資料的型別 (Date)。 使用此屬性:

  • 使用者不需要在日期欄位中輸入時間資訊。
  • 只會顯示日期,不會顯示時間資訊。

稍後的教學課程會涵蓋 DataAnnotations

string 後方的問號表示屬性可為 Null。 如需詳細資訊,請參閱可為 Null 的參考型別

新增 NuGet 套件

Visual Studio 會自動安裝必要的套件。

建置專案以檢查編譯器錯誤。

Scaffold 影片頁面

使用 Scaffolding 工具來為影片模型產生 CreateReadUpdateDelete (CRUD) 頁面。

在 [方案總管] 中,以滑鼠右鍵按一下 Controllers 資料夾,然後選取 [新增]> > [新增 Scaffold 項目]

上方步驟的檢視

在 [新增 Scaffolded 專案] 對話方塊中:

  • 在左窗格中,選取 已安裝>的通用>MVC
  • 選取 [使用 Entity Framework 執行檢視的 MVC 控制器]
  • 選取 [新增]。

[新增 Scaffold] 對話方塊

完成 [新增使用 Entity Framework 執行檢視的 MVC 控制器] 對話方塊:

  • 在 [模型類別] 下拉式清單中選取 [影片 (MvcMovie.Models)]
  • 在 [資料內容類別] 資料列中,選取 + (加號)。
    • 在 [新增資料內容] 對話方塊中,會產生類別名稱 MvcMovie.Data.MvcMovieCoNtext
    • 選取新增
  • 在 [資料庫提供者] 下拉式清單中,選取 [SQL Server]
  • [檢視] 和 [控制器名稱]:保留預設值。
  • 選取 [新增]。

新增資料內容:保留預設值

如果您收到錯誤訊息,請選取 [新增] 第二次再試一次。

Scaffolding 會新增下列套件:

  • Microsoft.EntityFrameworkCore.SqlServer
  • Microsoft.EntityFrameworkCore.Tools
  • Microsoft.VisualStudio.Web.CodeGeneration.Design

Scaffolding 會建立下列項目:

  • 電影控制器:Controllers/MoviesController.cs
  • Razor檢視 [建立]、[刪除]、[詳細資料]、[編輯] 和 [索引] 頁面的檔案:Views/Movies/*.cshtml
  • 資料庫內容類別:Data/MvcMovieContext.cs

Scaffolding 會更新下列項目:

  • MvcMovie.csproj 專案檔中插入必要的套件參考。
  • Program.cs 檔案中註冊資料庫內容。
  • 將資料庫連接字串新增至 appsettings.json 檔案。

自動建立這些檔案和檔案更新的流程稱為 Scaffolding

仍無法使用 scaffold 頁面,因為資料庫不存在。 執行應用程式並選取 [電影應用程式] 連結會導致 無法開啟資料庫沒有這類資料表:Movie 錯誤訊息。

建置應用程式以確認沒有任何錯誤。

初始移轉

使用 EF Core移轉 功能來建立資料庫。 移轉是一組工具,可建立和更新資料庫,使其與資料模型相符。

從 [工具] 功能表中,選取 [NuGet 套件管理員]> [套件管理員主控台]

請在套件管理員主控台 (PMC) 中輸入下列命令:

Add-Migration InitialCreate

  • Add-Migration InitialCreate:產生 Migrations/{timestamp}_InitialCreate.cs 移轉檔案。 InitialCreate 引數是移轉名稱。 您可以使用任何名稱,但依照慣例,會選取描述移轉的名稱。 因為這是第一次移轉,所產生類別會包含建立資料庫結構描述的程式碼。 資料庫結構描述是以 MvcMovieContext 類別為基礎。

畫面上顯示下列警告,在稍後的步驟中會解決此問題:

實體類型 'Movie' 上的十進位屬性 'Price' 未指定任何存放區類型。 如果它們不符合預設的有效位數和小數位數,會導致以無訊息模式截斷這些值。 使用 'HasColumnType' 明確指定可容納 'OnModelCreating' 中所有值的 SQL Server 資料行類型、使用 'HasPrecision' 指定精確度和小數位數,或使用 'HasConversion' 設定值轉換器。

在 PMC 中,輸入下列命令:

Update-Database

  • Update-Database:將資料庫更新到最新的移轉,即上一個命令建立的移轉。 此命令會在 Migrations/{time-stamp}_InitialCreate.cs 檔案中執行 Up 方法,以建立資料庫。

如需 EF Core PMC 工具的詳細資訊,請參閱 EF Core 工具參考 - Visual Studio 中的 PMC

測試應用程式

執行應用程式,然後選取 [電影應用程式] 連結。

如果您收到類似下列的例外狀況,您可能錯過了 移轉步驟 中的 Update-Database 命令:

SqlException: Cannot open database "MvcMovieContext-1" requested by the login. The login failed.

注意

您可能無法在 Price 欄位中輸入小數逗號。 若要對使用逗號 (",") 作為小數點的非英文地區設定和非英文日期格式支援 jQuery 驗證,則必須將應用程式全球化。 如需全球化指示,請參閱此 GitHub 問題 \(英文\)。

檢查產生的資料庫內容類別和註冊

運用 EF Core,使用模型來執行資料存取。 模型是由實體類別和內容物件所組成,其內容物件代表具有資料庫的工作階段。 內容物件可讓您查詢和儲存資料。 資料庫內容衍生自 Microsoft.EntityFrameworkCore.DbContext,並指定要包含在資料模型中的實體。

Scaffolding 會建立 Data/MvcMovieContext.cs 資料庫內容類別:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using MvcMovie.Models;

namespace MvcMovie.Data
{
    public class MvcMovieContext : DbContext
    {
        public MvcMovieContext (DbContextOptions<MvcMovieContext> options)
            : base(options)
        {
        }

        public DbSet<MvcMovie.Models.Movie> Movie { get; set; } = default!;
    }
}

上述程式碼會建立 DbSet<Movie> 屬性,代表資料庫中的電影。

相依性插入

ASP.NET Core 內建相依性插入 (DI)。 服務,例如資料庫內容,會在 Program.cs 中向 DI 註冊。 這些服務會透過建構函式參數提供給要求它們的元件。

Controllers/MoviesController.cs 檔案中,建構函式會使用相依性插入MvcMovieContext 資料庫內容插入到控制器中。 控制器中的每一個 CRUD 方法都會使用資料庫內容。

Scaffolding 在 Program.cs 中產生下列醒目提示的程式碼:

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<MvcMovieContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("MvcMovieContext") ?? throw new InvalidOperationException("Connection string 'MvcMovieContext' not found.")));

ASP.NET Core 設定系統 會讀取「MvcMovieCoNtext」資料庫連接字串。

檢查產生的資料庫連接字串

Scaffolding 已將連接字串新增至 appsettings.json 檔案:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "MvcMovieContext": "Server=(localdb)\\mssqllocaldb;Database=MvcMovieContext-4ebefa10-de29-4dea-b2ad-8a8dc6bcf374;Trusted_Connection=True;MultipleActiveResultSets=true"
  }
}

針對本機開發,ASP.NET Core 設定系統 會從 appsettings.json 檔案讀取 ConnectionString 索引碼。

InitialCreate 類別

檢查 Migrations/{timestamp}_InitialCreate.cs 移轉檔案:

using System;
using Microsoft.EntityFrameworkCore.Migrations;

#nullable disable

namespace MvcMovie.Migrations
{
    /// <inheritdoc />
    public partial class InitialCreate : Migration
    {
        /// <inheritdoc />
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.CreateTable(
                name: "Movie",
                columns: table => new
                {
                    Id = table.Column<int>(type: "int", nullable: false)
                        .Annotation("SqlServer:Identity", "1, 1"),
                    Title = table.Column<string>(type: "nvarchar(max)", nullable: true),
                    ReleaseDate = table.Column<DateTime>(type: "datetime2", nullable: false),
                    Genre = table.Column<string>(type: "nvarchar(max)", nullable: true),
                    Price = table.Column<decimal>(type: "decimal(18,2)", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Movie", x => x.Id);
                });
        }

        /// <inheritdoc />
        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.DropTable(
                name: "Movie");
        }
    }
}

在上述程式碼中:

  • InitialCreate.Up 會建立 Movie 資料表,並將 Id 設為主索引鍵。
  • InitialCreate.Down 會還原 Up 移轉所做的結構描述變更。

控制器中的相依性插入

請開啟 Controllers/MoviesController.cs 檔案並檢查建構函式:

public class MoviesController : Controller
{
    private readonly MvcMovieContext _context;

    public MoviesController(MvcMovieContext context)
    {
        _context = context;
    }

建構函式會使用相依性插入將資料庫內容 (MvcMovieContext) 插入到控制器中。 控制器中的每一個 CRUD 方法都會使用資料庫內容。

測試 Create 頁面。 輸入並提交資料。

測試 EditDetailsDelete 頁面。

強型別模型和 @model 指示詞

稍早在本教學課程中,您已看到控制器如何使用 ViewData 字典傳遞資料或物件給檢視。 ViewData 字典是一種動態物件,提供便利的晚期繫結方式,將資訊傳遞至檢視。

MVC 提供將強型別模型物件傳遞至檢視的能力。 這種強型別的方法可在編譯時檢查程式碼。 Scaffolding 機制在 MoviesController 類別和檢視中傳遞了強型別模型。

檢查 Controllers/MoviesController.cs 檔案中產生的 Details 方法:

// GET: Movies/Details/5
public async Task<IActionResult> Details(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var movie = await _context.Movie
        .FirstOrDefaultAsync(m => m.Id == id);
    if (movie == null)
    {
        return NotFound();
    }

    return View(movie);
}

id 參數通常會傳遞為路由資料。 例如,https://localhost:5001/movies/details/1 設定:

  • 控制器為 movies 控制器,第一個 URL 區段。
  • 動作為 details,第二個 URL 區段。
  • id 為 1,最後一個 URL 區段。

id 可以使用查詢字串傳入,如下列範例所示:

https://localhost:5001/movies/details?id=1

若未提供 id 值,id 參數會定義為可為 Null 的型別 (int?)。

Lambda 運算式會傳遞至 FirstOrDefaultAsync 方法,以選取符合路由資料或查詢字串值的電影實體。

var movie = await _context.Movie
    .FirstOrDefaultAsync(m => m.Id == id);

如果找到電影,則 Movie 模型的執行個體會傳遞至 Details 檢視:

return View(movie);

檢查 Views/Movies/Details.cshtml 檔案的內容:

@model MvcMovie.Models.Movie

@{
    ViewData["Title"] = "Details";
}

<h1>Details</h1>

<div>
    <h4>Movie</h4>
    <hr />
    <dl class="row">
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Title)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Title)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.ReleaseDate)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.ReleaseDate)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Genre)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Genre)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Price)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Price)
        </dd>
    </dl>
</div>
<div>
    <a asp-action="Edit" asp-route-id="@Model.Id">Edit</a> |
    <a asp-action="Index">Back to List</a>
</div>

檢視檔案頂端的 @model 陳述式會指定檢視所預期物件型別。 建立影片控制器時,會包含下列 @model 陳述式:

@model MvcMovie.Models.Movie

@model 指示詞會允許存取控制器傳遞給檢視的影片。 Model 物件為強型別物件。 例如,在 Details.cshtml檢視中,程式碼會使用強型別的 Model 物件,將每個電影欄位傳遞至 DisplayNameForDisplayFor HTML 協助程式。 CreateEdit 方法和檢視也會傳遞 Movie 模型物件。

檢查 Movies 控制器中的 Index.cshtml 檢視和 Index 方法。 請注意程式碼如何在呼叫 View 方法時建立 List 物件。 此程式碼會從 Index 動作方法將 Movies 清單傳遞至檢視:

// GET: Movies
public async Task<IActionResult> Index()
{
    return View(await _context.Movie.ToListAsync());
}

如果資料內容的 Movie 屬性為 Null, 程式碼會傳回 問題詳細資料

建立電影控制器時,Scaffolding 會在 Index.cshtml 檔案的頂端包含下列 @model 陳述式:

@model IEnumerable<MvcMovie.Models.Movie>

@model 指示詞可使用強型別的 Model 物件,存取控制器傳遞至檢視的電影清單。 例如,在 Index.cshtml 檢視中,程式碼會透過強型別 Model 物件的 foreach 陳述式循環存取電影:

@model IEnumerable<MvcMovie.Models.Movie>

@{
    ViewData["Title"] = "Index";
}

<h1>Index</h1>

<p>
    <a asp-action="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Title)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.ReleaseDate)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Genre)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Price)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Title)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ReleaseDate)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Genre)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Price)
            </td>
            <td>
                <a asp-action="Edit" asp-route-id="@item.Id">Edit</a> |
                <a asp-action="Details" asp-route-id="@item.Id">Details</a> |
                <a asp-action="Delete" asp-route-id="@item.Id">Delete</a>
            </td>
        </tr>
}
    </tbody>
</table>

因為 Model 物件是強型別作為 IEnumerable<Movie> 物件,所以迴圈中每個項目的型別為 Movie。 除了其他優點之外,編譯器會驗證程式碼中使用的型別。

其他資源

在本教學課程中,您可以新增類別來管理資料庫中的電影。 這些類別是 MVC 應用程式的「模型」部分。

搭配 Entity Framework Core (EF Core) 使用這些模型類別,即可使用資料庫。 EF Core 是一種物件關聯式對應 (ORM) 架構,可簡化您必須撰寫的資料存取程式碼。

建立的模型類別稱為 POCO 類別,全名為 Plain Old CLR Objects。 POCO 類別與 EF Core 沒有任何相依性。 它們只會定義資料儲存在資料庫中的屬性。

在本教學課程中,會先建立模型類別,以及 EF Core 建立資料庫。

新增資料模型類別

以滑鼠右鍵按一下 Models 資料夾 >[新增]>[類別]。 將檔案命名為 Movie.cs

使用下列程式碼更新 Models/Movie.cs 檔案:

using System.ComponentModel.DataAnnotations;

namespace MvcMovie.Models;

public class Movie
{
    public int Id { get; set; }
    public string? Title { get; set; }
    [DataType(DataType.Date)]
    public DateTime ReleaseDate { get; set; }
    public string? Genre { get; set; }
    public decimal Price { get; set; }
}

Movie 類別包含 Id 欄位,該欄位是資料庫的必要欄位,將作為主索引鍵。

ReleaseDate 上的 DataType 屬性會指定資料的型別 (Date)。 使用此屬性:

  • 使用者不需要在日期欄位中輸入時間資訊。
  • 只會顯示日期,不會顯示時間資訊。

稍後的教學課程會涵蓋 DataAnnotations

string 後方的問號表示屬性可為 Null。 如需詳細資訊,請參閱可為 Null 的參考型別

新增 NuGet 套件

Visual Studio 會自動安裝必要的套件。

建置專案以檢查編譯器錯誤。

Scaffold 影片頁面

使用 Scaffolding 工具來為影片模型產生 CreateReadUpdateDelete (CRUD) 頁面。

在 [方案總管] 中,以滑鼠右鍵按一下 Controllers 資料夾,然後選取 [新增]> > [新增 Scaffold 項目]

上方步驟的檢視

在 [新增 Scaffolded 專案] 對話方塊中:

  • 在左窗格中,選取 已安裝>的通用>MVC
  • 選取 [使用 Entity Framework 執行檢視的 MVC 控制器]
  • 選取 [新增]。

[新增 Scaffold] 對話方塊

完成 [新增使用 Entity Framework 執行檢視的 MVC 控制器] 對話方塊:

  • 在 [模型類別] 下拉式清單中選取 [影片 (MvcMovie.Models)]
  • 在 [資料內容類別] 資料列中,選取 + (加號)。
    • 在 [新增資料內容] 對話方塊中,會產生類別名稱 MvcMovie.Data.MvcMovieCoNtext
    • 選取新增
  • 在 [資料庫提供者] 下拉式清單中,選取 [SQL Server]
  • [檢視] 和 [控制器名稱]:保留預設值。
  • 選取 [新增]。

新增資料內容:保留預設值

如果您收到錯誤訊息,請選取 [新增] 第二次再試一次。

Scaffolding 會新增下列套件:

  • Microsoft.EntityFrameworkCore.SqlServer
  • Microsoft.EntityFrameworkCore.Tools
  • Microsoft.VisualStudio.Web.CodeGeneration.Design

Scaffolding 會建立下列項目:

  • 電影控制器:Controllers/MoviesController.cs
  • Razor檢視 [建立]、[刪除]、[詳細資料]、[編輯] 和 [索引] 頁面的檔案:Views/Movies/*.cshtml
  • 資料庫內容類別:Data/MvcMovieContext.cs

Scaffolding 會更新下列項目:

  • MvcMovie.csproj 專案檔中插入必要的套件參考。
  • Program.cs 檔案中註冊資料庫內容。
  • 將資料庫連接字串新增至 appsettings.json 檔案。

自動建立這些檔案和檔案更新的流程稱為 Scaffolding

仍無法使用 scaffold 頁面,因為資料庫不存在。 執行應用程式並選取 [電影應用程式] 連結會導致 無法開啟資料庫沒有這類資料表:Movie 錯誤訊息。

建置應用程式以確認沒有任何錯誤。

初始移轉

使用 EF Core移轉 功能來建立資料庫。 移轉是一組工具,可建立和更新資料庫,使其與資料模型相符。

從 [工具] 功能表中,選取 [NuGet 套件管理員]> [套件管理員主控台]

請在套件管理員主控台 (PMC) 中輸入下列命令:

Add-Migration InitialCreate
Update-Database

  • Add-Migration InitialCreate:產生 Migrations/{timestamp}_InitialCreate.cs 移轉檔案。 InitialCreate 引數是移轉名稱。 您可以使用任何名稱,但依照慣例,會選取描述移轉的名稱。 因為這是第一次移轉,所產生類別會包含建立資料庫結構描述的程式碼。 資料庫結構描述是以 MvcMovieContext 類別為基礎。

  • Update-Database:將資料庫更新到最新的移轉,即上一個命令建立的移轉。 此命令會在 Migrations/{time-stamp}_InitialCreate.cs 檔案中執行 Up 方法,以建立資料庫。

Update-Database 命令會產生下列警告:

實體類型 'Movie' 上的十進位屬性 'Price' 未指定任何存放區類型。 如果它們不符合預設的有效位數和小數位數,會導致以無訊息模式截斷這些值。 使用 'HasColumnType' 明確指定可容納 'OnModelCreating' 中所有值的 SQL Server 資料行類型、使用 'HasPrecision' 指定精確度和小數位數,或使用 'HasConversion' 設定值轉換器。

忽略上述警告,在稍後的教學課程中已修正。

如需 EF Core PMC 工具的詳細資訊,請參閱 EF Core 工具參考 - Visual Studio 中的 PMC

測試應用程式

執行應用程式,然後選取 [電影應用程式] 連結。

如果您收到類似下列的例外狀況,您可能錯過了 移轉步驟 中的 Update-Database 命令:

SqlException: Cannot open database "MvcMovieContext-1" requested by the login. The login failed.

注意

您可能無法在 Price 欄位中輸入小數逗號。 若要對使用逗號 (",") 作為小數點的非英文地區設定和非英文日期格式支援 jQuery 驗證,則必須將應用程式全球化。 如需全球化指示,請參閱此 GitHub 問題 \(英文\)。

檢查產生的資料庫內容類別和註冊

運用 EF Core,使用模型來執行資料存取。 模型是由實體類別和內容物件所組成,其內容物件代表具有資料庫的工作階段。 內容物件可讓您查詢和儲存資料。 資料庫內容衍生自 Microsoft.EntityFrameworkCore.DbContext,並指定要包含在資料模型中的實體。

Scaffolding 會建立 Data/MvcMovieContext.cs 資料庫內容類別:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using MvcMovie.Models;

namespace MvcMovie.Data
{
    public class MvcMovieContext : DbContext
    {
        public MvcMovieContext (DbContextOptions<MvcMovieContext> options)
            : base(options)
        {
        }

        public DbSet<MvcMovie.Models.Movie> Movie { get; set; }
    }
}

上述程式碼會建立 DbSet<Movie> 屬性,代表資料庫中的電影。

相依性插入

ASP.NET Core 內建相依性插入 (DI)。 服務,例如資料庫內容,會在 Program.cs 中向 DI 註冊。 這些服務會透過建構函式參數提供給要求它們的元件。

Controllers/MoviesController.cs 檔案中,建構函式會使用相依性插入MvcMovieContext 資料庫內容插入到控制器中。 控制器中的每一個 CRUD 方法都會使用資料庫內容。

Scaffolding 在 Program.cs 中產生下列醒目提示的程式碼:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDbContext<MvcMovieContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("MvcMovieContext")));

ASP.NET Core 設定系統 會讀取「MvcMovieCoNtext」資料庫連接字串。

檢查產生的資料庫連接字串

Scaffolding 已將連接字串新增至 appsettings.json 檔案:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "MvcMovieContext": "Data Source=MvcMovieContext-ea7a4069-f366-4742-bd1c-3f753a804ce1.db"
  }
}

針對本機開發,ASP.NET Core 設定系統 會從 appsettings.json 檔案讀取 ConnectionString 索引碼。

InitialCreate 類別

檢查 Migrations/{timestamp}_InitialCreate.cs 移轉檔案:

using System;
using Microsoft.EntityFrameworkCore.Migrations;

#nullable disable

namespace MvcMovie.Migrations
{
    public partial class InitialCreate : Migration
    {
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.CreateTable(
                name: "Movie",
                columns: table => new
                {
                    Id = table.Column<int>(type: "int", nullable: false)
                        .Annotation("SqlServer:Identity", "1, 1"),
                    Title = table.Column<string>(type: "nvarchar(max)", nullable: true),
                    ReleaseDate = table.Column<DateTime>(type: "datetime2", nullable: false),
                    Genre = table.Column<string>(type: "nvarchar(max)", nullable: true),
                    Price = table.Column<decimal>(type: "decimal(18,2)", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Movie", x => x.Id);
                });
        }

        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.DropTable(
                name: "Movie");
        }
    }
}

在上述程式碼中:

  • InitialCreate.Up 會建立 Movie 資料表,並將 Id 設為主索引鍵。
  • InitialCreate.Down 會還原 Up 移轉所做的結構描述變更。

控制器中的相依性插入

請開啟 Controllers/MoviesController.cs 檔案並檢查建構函式:

public class MoviesController : Controller
{
    private readonly MvcMovieContext _context;

    public MoviesController(MvcMovieContext context)
    {
        _context = context;
    }

建構函式會使用相依性插入將資料庫內容 (MvcMovieContext) 插入到控制器中。 控制器中的每一個 CRUD 方法都會使用資料庫內容。

測試 Create 頁面。 輸入並提交資料。

測試 EditDetailsDelete 頁面。

強型別模型和 @model 指示詞

稍早在本教學課程中,您已看到控制器如何使用 ViewData 字典傳遞資料或物件給檢視。 ViewData 字典是一種動態物件,提供便利的晚期繫結方式,將資訊傳遞至檢視。

MVC 提供將強型別模型物件傳遞至檢視的能力。 這種強型別的方法可在編譯時檢查程式碼。 Scaffolding 機制在 MoviesController 類別和檢視中傳遞了強型別模型。

檢查 Controllers/MoviesController.cs 檔案中產生的 Details 方法:

// GET: Movies/Details/5
public async Task<IActionResult> Details(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var movie = await _context.Movie
        .FirstOrDefaultAsync(m => m.Id == id);
    if (movie == null)
    {
        return NotFound();
    }

    return View(movie);
}

id 參數通常會傳遞為路由資料。 例如,https://localhost:5001/movies/details/1 設定:

  • 控制器為 movies 控制器,第一個 URL 區段。
  • 動作為 details,第二個 URL 區段。
  • id 為 1,最後一個 URL 區段。

id 可以使用查詢字串傳入,如下列範例所示:

https://localhost:5001/movies/details?id=1

若未提供 id 值,id 參數會定義為可為 Null 的型別 (int?)。

Lambda 運算式會傳遞至 FirstOrDefaultAsync 方法,以選取符合路由資料或查詢字串值的電影實體。

var movie = await _context.Movie
    .FirstOrDefaultAsync(m => m.Id == id);

如果找到電影,則 Movie 模型的執行個體會傳遞至 Details 檢視:

return View(movie);

檢查 Views/Movies/Details.cshtml 檔案的內容:

@model MvcMovie.Models.Movie

@{
    ViewData["Title"] = "Details";
}

<h1>Details</h1>

<div>
    <h4>Movie</h4>
    <hr />
    <dl class="row">
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Title)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Title)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.ReleaseDate)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.ReleaseDate)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Genre)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Genre)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Price)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Price)
        </dd>
    </dl>
</div>
<div>
    <a asp-action="Edit" asp-route-id="@Model.Id">Edit</a> |
    <a asp-action="Index">Back to List</a>
</div>

檢視檔案頂端的 @model 陳述式會指定檢視所預期物件型別。 建立影片控制器時,會包含下列 @model 陳述式:

@model MvcMovie.Models.Movie

@model 指示詞會允許存取控制器傳遞給檢視的影片。 Model 物件為強型別物件。 例如,在 Details.cshtml檢視中,程式碼會使用強型別的 Model 物件,將每個電影欄位傳遞至 DisplayNameForDisplayFor HTML 協助程式。 CreateEdit 方法和檢視也會傳遞 Movie 模型物件。

檢查 Movies 控制器中的 Index.cshtml 檢視和 Index 方法。 請注意程式碼如何在呼叫 View 方法時建立 List 物件。 此程式碼會從 Index 動作方法將 Movies 清單傳遞至檢視:

// GET: Movies
public async Task<IActionResult> Index()
{
    return View(await _context.Movie.ToListAsync());
}

如果資料內容的 Movie 屬性為 Null, 程式碼會傳回 問題詳細資料

建立電影控制器時,Scaffolding 會在 Index.cshtml 檔案的頂端包含下列 @model 陳述式:

@model IEnumerable<MvcMovie.Models.Movie>

@model 指示詞可使用強型別的 Model 物件,存取控制器傳遞至檢視的電影清單。 例如,在 Index.cshtml 檢視中,程式碼會透過強型別 Model 物件的 foreach 陳述式循環存取電影:

@model IEnumerable<MvcMovie.Models.Movie>

@{
    ViewData["Title"] = "Index";
}

<h1>Index</h1>

<p>
    <a asp-action="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Title)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.ReleaseDate)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Genre)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Price)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Title)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ReleaseDate)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Genre)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Price)
            </td>
            <td>
                <a asp-action="Edit" asp-route-id="@item.Id">Edit</a> |
                <a asp-action="Details" asp-route-id="@item.Id">Details</a> |
                <a asp-action="Delete" asp-route-id="@item.Id">Delete</a>
            </td>
        </tr>
}
    </tbody>
</table>

因為 Model 物件是強型別作為 IEnumerable<Movie> 物件,所以迴圈中每個項目的型別為 Movie。 除了其他優點之外,編譯器會驗證程式碼中使用的型別。

其他資源

在本教學課程中,您可以新增類別來管理資料庫中的電影。 這些類別是 MVC 應用程式的「模型」部分。

搭配 Entity Framework Core (EF Core) 使用這些模型類別,即可使用資料庫。 EF Core 是一種物件關聯式對應 (ORM) 架構,可簡化您必須撰寫的資料存取程式碼。

建立的模型類別稱為 POCO 類別,全名為 Plain Old CLR Objects。 POCO 類別與 EF Core 沒有任何相依性。 它們只會定義資料儲存在資料庫中的屬性。

在本教學課程中,會先建立模型類別,以及 EF Core 建立資料庫。

新增資料模型類別

以滑鼠右鍵按一下 Models 資料夾 >[新增]>[類別]。 將檔案命名為 Movie.cs

使用下列程式碼更新 Models/Movie.cs 檔案:

using System.ComponentModel.DataAnnotations;

namespace MvcMovie.Models;

public class Movie
{
    public int Id { get; set; }
    public string? Title { get; set; }
    [DataType(DataType.Date)]
    public DateTime ReleaseDate { get; set; }
    public string? Genre { get; set; }
    public decimal Price { get; set; }
}

Movie 類別包含 Id 欄位,該欄位是資料庫的必要欄位,將作為主索引鍵。

ReleaseDate 上的 DataType 屬性會指定資料的型別 (Date)。 使用此屬性:

  • 使用者不需要在日期欄位中輸入時間資訊。
  • 只會顯示日期,不會顯示時間資訊。

稍後的教學課程會涵蓋 DataAnnotations

string 後方的問號表示屬性可為 Null。 如需詳細資訊,請參閱可為 Null 的參考型別

新增 NuGet 套件

Visual Studio 會自動安裝必要的套件。

建置專案以檢查編譯器錯誤。

Scaffold 影片頁面

使用 Scaffolding 工具來為影片模型產生 CreateReadUpdateDelete (CRUD) 頁面。

在 [方案總管] 中,以滑鼠右鍵按一下 Controllers 資料夾,然後選取 [新增]> > [新增 Scaffold 項目]

上方步驟的檢視

在 [新增 Scaffolded 專案] 對話方塊中:

  • 在左窗格中,選取 已安裝>的通用>MVC
  • 選取 [使用 Entity Framework 執行檢視的 MVC 控制器]
  • 選取 [新增]。

[新增 Scaffold] 對話方塊

完成 [新增使用 Entity Framework 執行檢視的 MVC 控制器] 對話方塊:

  • 在 [模型類別] 下拉式清單中選取 [影片 (MvcMovie.Models)]
  • 在 [資料內容類別] 資料列中,選取 + (加號)。
    • 在 [新增資料內容] 對話方塊中,會產生類別名稱 MvcMovie.Data.MvcMovieCoNtext
    • 選取新增
  • 在 [資料庫提供者] 下拉式清單中,選取 [SQL Server]
  • [檢視] 和 [控制器名稱]:保留預設值。
  • 選取新增

新增資料內容:保留預設值 如果您收到錯誤訊息,請選取 [新增] 第二次再試一次。

Scaffolding 會新增下列套件:

  • Microsoft.EntityFrameworkCore.SqlServer
  • Microsoft.EntityFrameworkCore.Tools
  • Microsoft.VisualStudio.Web.CodeGeneration.Design

Scaffolding 會建立下列項目:

  • 電影控制器:Controllers/MoviesController.cs
  • Razor檢視 [建立]、[刪除]、[詳細資料]、[編輯] 和 [索引] 頁面的檔案:Views/Movies/*.cshtml
  • 資料庫內容類別:Data/MvcMovieContext.cs

Scaffolding 會更新下列項目:

  • MvcMovie.csproj 專案檔中插入必要的套件參考。
  • Program.cs 檔案中註冊資料庫內容。
  • 將資料庫連接字串新增至 appsettings.json 檔案。

自動建立這些檔案和檔案更新的流程稱為 Scaffolding

仍無法使用 scaffold 頁面,因為資料庫不存在。 執行應用程式並選取 [電影應用程式] 連結會導致 無法開啟資料庫沒有這類資料表:Movie 錯誤訊息。

建置應用程式以確認沒有任何錯誤。

初始移轉

使用 EF Core移轉 功能來建立資料庫。 移轉是一組工具,可建立和更新資料庫,使其與資料模型相符。

從 [工具] 功能表中,選取 [NuGet 套件管理員]> [套件管理員主控台]

請在套件管理員主控台 (PMC) 中輸入下列命令:

Add-Migration InitialCreate
Update-Database

  • Add-Migration InitialCreate:產生 Migrations/{timestamp}_InitialCreate.cs 移轉檔案。 InitialCreate 引數是移轉名稱。 您可以使用任何名稱,但依照慣例,會選取描述移轉的名稱。 因為這是第一次移轉,所產生類別會包含建立資料庫結構描述的程式碼。 資料庫結構描述是以 MvcMovieContext 類別為基礎。

  • Update-Database:將資料庫更新到最新的移轉,即上一個命令建立的移轉。 此命令會在 Migrations/{time-stamp}_InitialCreate.cs 檔案中執行 Up 方法,以建立資料庫。

Update-Database 命令會產生下列警告:

沒有為實體型別 'Movie' 上的十進位資料行 'Price' 指定型別。 如果它們不符合預設的有效位數和小數位數,會導致以無訊息模式截斷這些值。 使用 'HasColumnType()' 明確指定可容納所有值的 SQL 伺服器資料行型別。

忽略上述警告,在稍後的教學課程中已修正。

如需 EF Core PMC 工具的詳細資訊,請參閱 EF Core 工具參考 - Visual Studio 中的 PMC

測試應用程式

執行應用程式,然後選取 [電影應用程式] 連結。

如果您收到類似下列的例外狀況,您可能錯過了 移轉步驟 中的 Update-Database 命令:

SqlException: Cannot open database "MvcMovieContext-1" requested by the login. The login failed.

注意

您可能無法在 Price 欄位中輸入小數逗號。 若要對使用逗號 (",") 作為小數點的非英文地區設定和非英文日期格式支援 jQuery 驗證,則必須將應用程式全球化。 如需全球化指示,請參閱此 GitHub 問題 \(英文\)。

檢查產生的資料庫內容類別和註冊

運用 EF Core,使用模型來執行資料存取。 模型是由實體類別和內容物件所組成,其內容物件代表具有資料庫的工作階段。 內容物件可讓您查詢和儲存資料。 資料庫內容衍生自 Microsoft.EntityFrameworkCore.DbContext,並指定要包含在資料模型中的實體。

Scaffolding 會建立 Data/MvcMovieContext.cs 資料庫內容類別:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using MvcMovie.Models;

namespace MvcMovie.Data
{
    public class MvcMovieContext : DbContext
    {
        public MvcMovieContext (DbContextOptions<MvcMovieContext> options)
            : base(options)
        {
        }

        public DbSet<MvcMovie.Models.Movie> Movie { get; set; }
    }
}

上述程式碼會建立 DbSet<Movie> 屬性,代表資料庫中的電影。

相依性插入

ASP.NET Core 內建相依性插入 (DI)。 服務,例如資料庫內容,會在 Program.cs 中向 DI 註冊。 這些服務會透過建構函式參數提供給要求它們的元件。

Controllers/MoviesController.cs 檔案中,建構函式會使用相依性插入MvcMovieContext 資料庫內容插入到控制器中。 控制器中的每一個 CRUD 方法都會使用資料庫內容。

Scaffolding 在 Program.cs 中產生下列醒目提示的程式碼:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDbContext<MvcMovieContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("MvcMovieContext")));

ASP.NET Core 設定系統 會讀取「MvcMovieCoNtext」資料庫連接字串。

檢查產生的資料庫連接字串

Scaffolding 已將連接字串新增至 appsettings.json 檔案:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "MvcMovieContext": "Data Source=MvcMovieContext-ea7a4069-f366-4742-bd1c-3f753a804ce1.db"
  }
}

針對本機開發,ASP.NET Core 設定系統 會從 appsettings.json 檔案讀取 ConnectionString 索引碼。

InitialCreate 類別

檢查 Migrations/{timestamp}_InitialCreate.cs 移轉檔案:

using System;
using Microsoft.EntityFrameworkCore.Migrations;

#nullable disable

namespace MvcMovie.Migrations
{
    public partial class InitialCreate : Migration
    {
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.CreateTable(
                name: "Movie",
                columns: table => new
                {
                    Id = table.Column<int>(type: "int", nullable: false)
                        .Annotation("SqlServer:Identity", "1, 1"),
                    Title = table.Column<string>(type: "nvarchar(max)", nullable: true),
                    ReleaseDate = table.Column<DateTime>(type: "datetime2", nullable: false),
                    Genre = table.Column<string>(type: "nvarchar(max)", nullable: true),
                    Price = table.Column<decimal>(type: "decimal(18,2)", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Movie", x => x.Id);
                });
        }

        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.DropTable(
                name: "Movie");
        }
    }
}

在上述程式碼中:

  • InitialCreate.Up 會建立 Movie 資料表,並將 Id 設為主索引鍵。
  • InitialCreate.Down 會還原 Up 移轉所做的結構描述變更。

控制器中的相依性插入

請開啟 Controllers/MoviesController.cs 檔案並檢查建構函式:

public class MoviesController : Controller
{
    private readonly MvcMovieContext _context;

    public MoviesController(MvcMovieContext context)
    {
        _context = context;
    }

建構函式會使用相依性插入將資料庫內容 (MvcMovieContext) 插入到控制器中。 控制器中的每一個 CRUD 方法都會使用資料庫內容。

測試 Create 頁面。 輸入並提交資料。

測試 EditDetailsDelete 頁面。

強型別模型和 @model 指示詞

稍早在本教學課程中,您已看到控制器如何使用 ViewData 字典傳遞資料或物件給檢視。 ViewData 字典是一種動態物件,提供便利的晚期繫結方式,將資訊傳遞至檢視。

MVC 提供將強型別模型物件傳遞至檢視的能力。 這種強型別的方法可在編譯時檢查程式碼。 Scaffolding 機制在 MoviesController 類別和檢視中傳遞了強型別模型。

檢查 Controllers/MoviesController.cs 檔案中產生的 Details 方法:

// GET: Movies/Details/5
public async Task<IActionResult> Details(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var movie = await _context.Movie
        .FirstOrDefaultAsync(m => m.Id == id);
    if (movie == null)
    {
        return NotFound();
    }

    return View(movie);
}

id 參數通常會傳遞為路由資料。 例如,https://localhost:5001/movies/details/1 設定:

  • 控制器為 movies 控制器,第一個 URL 區段。
  • 動作為 details,第二個 URL 區段。
  • id 為 1,最後一個 URL 區段。

id 可以使用查詢字串傳入,如下列範例所示:

https://localhost:5001/movies/details?id=1

若未提供 id 值,id 參數會定義為可為 Null 的型別 (int?)。

Lambda 運算式會傳遞至 FirstOrDefaultAsync 方法,以選取符合路由資料或查詢字串值的電影實體。

var movie = await _context.Movie
    .FirstOrDefaultAsync(m => m.Id == id);

如果找到電影,則 Movie 模型的執行個體會傳遞至 Details 檢視:

return View(movie);

檢查 Views/Movies/Details.cshtml 檔案的內容:

@model MvcMovie.Models.Movie

@{
    ViewData["Title"] = "Details";
}

<h1>Details</h1>

<div>
    <h4>Movie</h4>
    <hr />
    <dl class="row">
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Title)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Title)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.ReleaseDate)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.ReleaseDate)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Genre)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Genre)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Price)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Price)
        </dd>
    </dl>
</div>
<div>
    <a asp-action="Edit" asp-route-id="@Model.Id">Edit</a> |
    <a asp-action="Index">Back to List</a>
</div>

檢視檔案頂端的 @model 陳述式會指定檢視所預期物件型別。 建立影片控制器時,會包含下列 @model 陳述式:

@model MvcMovie.Models.Movie

@model 指示詞會允許存取控制器傳遞給檢視的影片。 Model 物件為強型別物件。 例如,在 Details.cshtml檢視中,程式碼會使用強型別的 Model 物件,將每個電影欄位傳遞至 DisplayNameForDisplayFor HTML 協助程式。 CreateEdit 方法和檢視也會傳遞 Movie 模型物件。

檢查 Movies 控制器中的 Index.cshtml 檢視和 Index 方法。 請注意程式碼如何在呼叫 View 方法時建立 List 物件。 此程式碼會從 Index 動作方法將 Movies 清單傳遞至檢視:

// GET: Movies
public async Task<IActionResult> Index()
{
    return View(await _context.Movie.ToListAsync());
}

如果資料內容的 Movie 屬性為 Null, 程式碼會傳回 問題詳細資料

建立電影控制器時,Scaffolding 會在 Index.cshtml 檔案的頂端包含下列 @model 陳述式:

@model IEnumerable<MvcMovie.Models.Movie>

@model 指示詞可使用強型別的 Model 物件,存取控制器傳遞至檢視的電影清單。 例如,在 Index.cshtml 檢視中,程式碼會透過強型別 Model 物件的 foreach 陳述式循環存取電影:

@model IEnumerable<MvcMovie.Models.Movie>

@{
    ViewData["Title"] = "Index";
}

<h1>Index</h1>

<p>
    <a asp-action="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Title)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.ReleaseDate)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Genre)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Price)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Title)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ReleaseDate)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Genre)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Price)
            </td>
            <td>
                <a asp-action="Edit" asp-route-id="@item.Id">Edit</a> |
                <a asp-action="Details" asp-route-id="@item.Id">Details</a> |
                <a asp-action="Delete" asp-route-id="@item.Id">Delete</a>
            </td>
        </tr>
}
    </tbody>
</table>

因為 Model 物件是強型別作為 IEnumerable<Movie> 物件,所以迴圈中每個項目的型別為 Movie。 除了其他優點之外,編譯器會驗證程式碼中使用的型別。

其他資源

在本教學課程中,您可以新增類別來管理資料庫中的電影。 這些類別是 MVC 應用程式的「模型」部分。

搭配 Entity Framework Core (EF Core) 使用這些模型類別,即可使用資料庫。 EF Core 是一種物件關聯式對應 (ORM) 架構,可簡化您必須撰寫的資料存取程式碼。

建立的模型類別稱為 POCO 類別,全名為 Plain Old CLR Objects。 POCO 類別與 EF Core 沒有任何相依性。 它們只會定義資料儲存在資料庫中的屬性。

在本教學課程中,會先建立模型類別,以及 EF Core 建立資料庫。

新增資料模型類別

以滑鼠右鍵按一下 Models 資料夾 >[新增]>[類別]。 將檔案命名為 Movie.cs

使用下列程式碼更新 Models/Movie.cs 檔案:

using System.ComponentModel.DataAnnotations;

namespace MvcMovie.Models
{
    public class Movie
    {
        public int Id { get; set; }
        public string? Title { get; set; }

        [DataType(DataType.Date)]
        public DateTime ReleaseDate { get; set; }
        public string? Genre { get; set; }
        public decimal Price { get; set; }
    }
}

Movie 類別包含 Id 欄位,該欄位是資料庫的必要欄位,將作為主索引鍵。

ReleaseDate 上的 DataType 屬性會指定資料的型別 (Date)。 使用此屬性:

  • 使用者不需要在日期欄位中輸入時間資訊。
  • 只會顯示日期,不會顯示時間資訊。

稍後的教學課程會涵蓋 DataAnnotations

string 後方的問號表示屬性可為 Null。 如需詳細資訊,請參閱可為 Null 的參考型別

新增 NuGet 套件

從 [工具] 功能表中,選取 [NuGet 套件管理員]> [套件管理器主控台] (PMC)。

PMC 功能表

在 PMC 中,執行下列命令:

Install-Package Microsoft.EntityFrameworkCore.Design
Install-Package Microsoft.EntityFrameworkCore.SqlServer

上述命令會新增:

  • EF Core SQL Server 提供者。 提供者套件會將 EF Core 套件作為相依性安裝。
  • 套件所使用的公用程式會在教學課程稍後的 Scaffolding 步驟中自動安裝。

建置專案以檢查編譯器錯誤。

Scaffold 影片頁面

使用 Scaffolding 工具來為影片模型產生 CreateReadUpdateDelete (CRUD) 頁面。

在 [方案總管] 中,以滑鼠右鍵按一下 Controllers 資料夾,然後選取 [新增]> > [新增 Scaffold 項目]

上方步驟的檢視

在 [新增 Scaffold] 對話方塊中,選取 [使用 Entity Framework 執行檢視的 MVC 控制器] > [新增]

[新增 Scaffold] 對話方塊

完成 [新增使用 Entity Framework 執行檢視的 MVC 控制器] 對話方塊:

  • 在 [模型類別] 下拉式清單中選取 [影片 (MvcMovie.Models)]
  • 在 [資料內容類別] 資料列中,選取 + (加號)。
    • 在 [新增資料內容] 對話方塊中,會產生類別名稱 MvcMovie.Data.MvcMovieCoNtext
    • 選取新增
  • [檢視] 和 [控制器名稱]:保留預設值。
  • 選取 [新增]。

新增資料內容:保留預設值

如果您收到錯誤訊息,請選取 [新增] 第二次再試一次。

Scaffolding 會更新下列項目:

  • MvcMovie.csproj 專案檔中插入必要的套件參考。
  • Program.cs 檔案中註冊資料庫內容。
  • 將資料庫連接字串新增至 appsettings.json 檔案。

Scaffolding 會建立下列項目:

  • 電影控制器:Controllers/MoviesController.cs
  • Razor檢視 [建立]、[刪除]、[詳細資料]、[編輯] 和 [索引] 頁面的檔案:Views/Movies/*.cshtml
  • 資料庫內容類別:Data/MvcMovieContext.cs

自動建立這些檔案和檔案更新的流程稱為 Scaffolding

仍無法使用 scaffold 頁面,因為資料庫不存在。 執行應用程式並選取 [電影應用程式] 連結會導致 無法開啟資料庫沒有這類資料表:Movie 錯誤訊息。

建置應用程式

建置應用程式。 編譯器會產生數個關於如何處理 null 值的警告。 如需詳細資訊,請參閱此 GitHub 問題可為 Null 的參考型別

若要排除可為 Null 參考型別的警告,請從 MvcMovie.csproj 檔案中移除下列這行:

<Nullable>enable</Nullable>

我們希望下一個版本能修正這個問題。

初始移轉

使用 EF Core移轉 功能來建立資料庫。 移轉是一組工具,可建立和更新資料庫,使其與資料模型相符。

從 [工具] 功能表中,選取 [NuGet 套件管理員]> [套件管理員主控台]

請在套件管理員主控台 (PMC) 中輸入下列命令:

Add-Migration InitialCreate
Update-Database

  • Add-Migration InitialCreate:產生 Migrations/{timestamp}_InitialCreate.cs 移轉檔案。 InitialCreate 引數是移轉名稱。 您可以使用任何名稱,但依照慣例,會選取描述移轉的名稱。 因為這是第一次移轉,所產生類別會包含建立資料庫結構描述的程式碼。 資料庫結構描述是以 MvcMovieContext 類別為基礎。

  • Update-Database:將資料庫更新到最新的移轉,即上一個命令建立的移轉。 此命令會在 Migrations/{time-stamp}_InitialCreate.cs 檔案中執行 Up 方法,以建立資料庫。

Update-Database 命令會產生下列警告:

沒有為實體型別 'Movie' 上的十進位資料行 'Price' 指定型別。 如果它們不符合預設的有效位數和小數位數,會導致以無訊息模式截斷這些值。 使用 'HasColumnType()' 明確指定可容納所有值的 SQL 伺服器資料行型別。

忽略上述警告,在稍後的教學課程中已修正。

如需 EF Core PMC 工具的詳細資訊,請參閱 EF Core 工具參考 - Visual Studio 中的 PMC

測試應用程式

執行應用程式,然後選取 [電影應用程式] 連結。

如果您收到類似下列的例外狀況,您可能錯過了 移轉步驟

SqlException: Cannot open database "MvcMovieContext-1" requested by the login. The login failed.

注意

您可能無法在 Price 欄位中輸入小數逗號。 若要對使用逗號 (",") 作為小數點的非英文地區設定和非英文日期格式支援 jQuery 驗證,則必須將應用程式全球化。 如需全球化指示,請參閱此 GitHub 問題 \(英文\)。

檢查產生的資料庫內容類別和註冊

運用 EF Core,使用模型來執行資料存取。 模型是由實體類別和內容物件所組成,其內容物件代表具有資料庫的工作階段。 內容物件可讓您查詢和儲存資料。 資料庫內容衍生自 Microsoft.EntityFrameworkCore.DbContext,並指定要包含在資料模型中的實體。

Scaffolding 會建立 Data/MvcMovieContext.cs 資料庫內容類別:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using MvcMovie.Models;

namespace MvcMovie.Data
{
    public class MvcMovieContext : DbContext
    {
        public MvcMovieContext (DbContextOptions<MvcMovieContext> options)
            : base(options)
        {
        }

        public DbSet<MvcMovie.Models.Movie> Movie { get; set; }
    }
}

上述程式碼會建立 DbSet<Movie> 屬性,代表資料庫中的電影。

相依性插入

ASP.NET Core 內建相依性插入 (DI)。 服務,例如資料庫內容,會在 Program.cs 中向 DI 註冊。 這些服務會透過建構函式參數提供給要求它們的元件。

Controllers/MoviesController.cs 檔案中,建構函式會使用相依性插入MvcMovieContext 資料庫內容插入到控制器中。 控制器中的每一個 CRUD 方法都會使用資料庫內容。

Scaffolding 在 Program.cs 中產生下列醒目提示的程式碼:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDbContext<MvcMovieContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("MvcMovieContext")));

ASP.NET Core 設定系統 會讀取「MvcMovieCoNtext」資料庫連接字串。

檢查產生的資料庫連接字串

Scaffolding 已將連接字串新增至 appsettings.json 檔案:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "MvcMovieContext": "Server=(localdb)\\mssqllocaldb;Database=MvcMovieContext-7dc5;Trusted_Connection=True;MultipleActiveResultSets=true"
  }
}

針對本機開發,ASP.NET Core 設定系統 會從 appsettings.json 檔案讀取 ConnectionString 索引碼。

InitialCreate 類別

檢查 Migrations/{timestamp}_InitialCreate.cs 移轉檔案:

using System;
using Microsoft.EntityFrameworkCore.Migrations;

#nullable disable

namespace MvcMovie.Migrations
{
    public partial class InitialCreate : Migration
    {
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.CreateTable(
                name: "Movie",
                columns: table => new
                {
                    Id = table.Column<int>(type: "int", nullable: false)
                        .Annotation("SqlServer:Identity", "1, 1"),
                    Title = table.Column<string>(type: "nvarchar(max)", nullable: true),
                    ReleaseDate = table.Column<DateTime>(type: "datetime2", nullable: false),
                    Genre = table.Column<string>(type: "nvarchar(max)", nullable: true),
                    Price = table.Column<decimal>(type: "decimal(18,2)", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Movie", x => x.Id);
                });
        }

        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.DropTable(
                name: "Movie");
        }
    }
}

在上述程式碼中:

  • InitialCreate.Up 會建立 Movie 資料表,並將 Id 設為主索引鍵。
  • InitialCreate.Down 會還原 Up 移轉所做的結構描述變更。

控制器中的相依性插入

請開啟 Controllers/MoviesController.cs 檔案並檢查建構函式:

public class MoviesController : Controller
{
    private readonly MvcMovieContext _context;

    public MoviesController(MvcMovieContext context)
    {
        _context = context;
    }

建構函式會使用相依性插入將資料庫內容 (MvcMovieContext) 插入到控制器中。 控制器中的每一個 CRUD 方法都會使用資料庫內容。

測試 Create 頁面。 輸入並提交資料。

測試 EditDetailsDelete 頁面。

強型別模型和 @model 指示詞

稍早在本教學課程中,您已看到控制器如何使用 ViewData 字典傳遞資料或物件給檢視。 ViewData 字典是一種動態物件,提供便利的晚期繫結方式,將資訊傳遞至檢視。

MVC 提供將強型別模型物件傳遞至檢視的能力。 這種強型別的方法可在編譯時檢查程式碼。 Scaffolding 機制在 MoviesController 類別和檢視中傳遞了強型別模型。

檢查 Controllers/MoviesController.cs 檔案中產生的 Details 方法:

// GET: Movies/Details/5
public async Task<IActionResult> Details(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var movie = await _context.Movie
        .FirstOrDefaultAsync(m => m.Id == id);
    if (movie == null)
    {
        return NotFound();
    }

    return View(movie);
}

id 參數通常會傳遞為路由資料。 例如,https://localhost:5001/movies/details/1 設定:

  • 控制器為 movies 控制器,第一個 URL 區段。
  • 動作為 details,第二個 URL 區段。
  • id 為 1,最後一個 URL 區段。

id 可以使用查詢字串傳入,如下列範例所示:

https://localhost:5001/movies/details?id=1

若未提供 id 值,id 參數會定義為可為 Null 的型別 (int?)。

Lambda 運算式會傳遞至 FirstOrDefaultAsync 方法,以選取符合路由資料或查詢字串值的電影實體。

var movie = await _context.Movie
    .FirstOrDefaultAsync(m => m.Id == id);

如果找到電影,則 Movie 模型的執行個體會傳遞至 Details 檢視:

return View(movie);

檢查 Views/Movies/Details.cshtml 檔案的內容:

@model MvcMovie.Models.Movie

@{
    ViewData["Title"] = "Details";
}

<h1>Details</h1>

<div>
    <h4>Movie</h4>
    <hr />
    <dl class="row">
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Title)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Title)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.ReleaseDate)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.ReleaseDate)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Genre)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Genre)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Price)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Price)
        </dd>
    </dl>
</div>
<div>
    <a asp-action="Edit" asp-route-id="@Model.Id">Edit</a> |
    <a asp-action="Index">Back to List</a>
</div>

檢視檔案頂端的 @model 陳述式會指定檢視所預期物件型別。 建立影片控制器時,會包含下列 @model 陳述式:

@model MvcMovie.Models.Movie

@model 指示詞會允許存取控制器傳遞給檢視的影片。 Model 物件為強型別物件。 例如,在 Details.cshtml檢視中,程式碼會使用強型別的 Model 物件,將每個電影欄位傳遞至 DisplayNameForDisplayFor HTML 協助程式。 CreateEdit 方法和檢視也會傳遞 Movie 模型物件。

檢查 Movies 控制器中的 Index.cshtml 檢視和 Index 方法。 請注意程式碼如何在呼叫 View 方法時建立 List 物件。 此程式碼會從 Index 動作方法將 Movies 清單傳遞至檢視:

// GET: Movies
public async Task<IActionResult> Index()
{
    return View(await _context.Movie.ToListAsync());
}

建立電影控制器時,Scaffolding 會在 Index.cshtml 檔案的頂端包含下列 @model 陳述式:

@model IEnumerable<MvcMovie.Models.Movie>

@model 指示詞可使用強型別的 Model 物件,存取控制器傳遞至檢視的電影清單。 例如,在 Index.cshtml 檢視中,程式碼會透過強型別 Model 物件的 foreach 陳述式循環存取電影:

@model IEnumerable<MvcMovie.Models.Movie>

@{
    ViewData["Title"] = "Index";
}

<h1>Index</h1>

<p>
    <a asp-action="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Title)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.ReleaseDate)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Genre)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Price)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Title)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ReleaseDate)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Genre)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Price)
            </td>
            <td>
                <a asp-action="Edit" asp-route-id="@item.Id">Edit</a> |
                <a asp-action="Details" asp-route-id="@item.Id">Details</a> |
                <a asp-action="Delete" asp-route-id="@item.Id">Delete</a>
            </td>
        </tr>
}
    </tbody>
</table>

因為 Model 物件是強型別作為 IEnumerable<Movie> 物件,所以迴圈中每個項目的型別為 Movie。 除了其他優點之外,編譯器會驗證程式碼中使用的型別。

其他資源

在本教學課程中,您可以新增類別來管理資料庫中的電影。 這些類別是 MVC 應用程式的「模型」部分。

搭配 Entity Framework Core (EF Core) 使用這些模型類別,即可使用資料庫。 EF Core 是一種物件關聯式對應 (ORM) 架構,可簡化您必須撰寫的資料存取程式碼。

建立的模型類別稱為 POCO 類別,全名為 Plain Old CLR Objects。 POCO 類別與 EF Core 沒有任何相依性。 它們只會定義資料儲存在資料庫中的屬性。

在本教學課程中,會先建立模型類別,以及 EF Core 建立資料庫。

新增資料模型類別

以滑鼠右鍵按一下 Models 資料夾 >[新增]>[類別]。 將檔案命名為 Movie.cs

使用下列程式碼更新 Models/Movie.cs 檔案:

using System;
using System.ComponentModel.DataAnnotations;

namespace MvcMovie.Models
{
    public class Movie
    {
        public int Id { get; set; }
        public string Title { get; set; }

        [DataType(DataType.Date)]
        public DateTime ReleaseDate { get; set; }
        public string Genre { get; set; }
        public decimal Price { get; set; }
    }
}

Movie 類別包含 Id 欄位,該欄位是資料庫的必要欄位,將作為主索引鍵。

ReleaseDate 上的 DataType 屬性會指定資料的型別 (Date)。 使用此屬性:

  • 使用者不需要在日期欄位中輸入時間資訊。
  • 只會顯示日期,不會顯示時間資訊。

稍後的教學課程會涵蓋 DataAnnotations

新增 NuGet 套件

從 [工具] 功能表中,選取 [NuGet 套件管理員]> [套件管理器主控台] (PMC)。

PMC 功能表

在 PMC 中,執行下列命令:

Install-Package Microsoft.EntityFrameworkCore.Design

上述命令會新增:

  • EF Core SQL Server 提供者。 提供者套件會將 EF Core 套件作為相依性安裝。
  • 套件所使用的公用程式會在教學課程稍後的 Scaffolding 步驟中自動安裝。

建置專案以檢查編譯器錯誤。

Scaffold 影片頁面

使用 Scaffolding 工具來為影片模型產生 CreateReadUpdateDelete (CRUD) 頁面。

在 [方案總管] 中,以滑鼠右鍵按一下 Controllers 資料夾,然後選取 [新增]> > [新增 Scaffold 項目]

上方步驟的檢視

在 [新增 Scaffold] 對話方塊中,選取 [使用 Entity Framework 執行檢視的 MVC 控制器] > [新增]

[新增 Scaffold] 對話方塊

完成 [新增使用 Entity Framework 執行檢視的 MVC 控制器] 對話方塊:

  • 在 [模型類別] 下拉式清單中選取 [影片 (MvcMovie.Models)]
  • 在 [資料內容類別] 資料列中,選取 + (加號)。
    • 在 [新增資料內容] 對話方塊中,會產生類別名稱 MvcMovie.Data.MvcMovieCoNtext
    • 選取新增
  • [檢視] 和 [控制器名稱]:保留預設值。
  • 選取 [新增]。

新增資料內容:保留預設值

Scaffolding 會更新下列項目:

  • MvcMovie.csproj 專案檔中插入必要的套件參考。
  • Startup.csStartup.ConfigureServices 檔案中註冊資料庫內容。
  • 將資料庫連接字串新增至 appsettings.json 檔案。

Scaffolding 會建立下列項目:

  • 電影控制器:Controllers/MoviesController.cs
  • Razor檢視 [建立]、[刪除]、[詳細資料]、[編輯] 和 [索引] 頁面的檔案:Views/Movies/*.cshtml
  • 資料庫內容類別:Data/MvcMovieContext.cs

自動建立這些檔案和檔案更新稱為 Scaffolding

仍無法使用 scaffold 頁面,因為資料庫不存在。 執行應用程式並選取 [電影應用程式] 連結會導致 無法開啟資料庫沒有這類資料表:Movie 錯誤訊息。

初始移轉

使用 EF Core移轉 功能來建立資料庫。 移轉是一組工具,可建立及更新資料庫以符合資料模型。

從 [工具] 功能表中,選取 [NuGet 套件管理員]> [套件管理員主控台]

請在套件管理員主控台 (PMC) 中輸入下列命令:

Add-Migration InitialCreate
Update-Database

  • Add-Migration InitialCreate:產生 Migrations/{timestamp}_InitialCreate.cs 移轉檔案。 InitialCreate 引數是移轉名稱。 您可以使用任何名稱,但依照慣例,會選取描述移轉的名稱。 因為這是第一次移轉,所產生類別會包含建立資料庫結構描述的程式碼。 資料庫結構描述是以 MvcMovieContext 類別為基礎。

  • Update-Database:將資料庫更新到最新的移轉,即上一個命令建立的移轉。 此命令會在 Migrations/{time-stamp}_InitialCreate.cs 檔案中執行 Up 方法,以建立資料庫。

Update-Database 命令會產生下列警告:

沒有為實體型別 'Movie' 上的十進位資料行 'Price' 指定型別。 如果它們不符合預設的有效位數和小數位數,會導致以無訊息模式截斷這些值。 使用 'HasColumnType()' 明確指定可容納所有值的 SQL 伺服器資料行型別。

忽略上述警告,在稍後的教學課程中已修正。

如需 EF Core PMC 工具的詳細資訊,請參閱 EF Core 工具參考 - Visual Studio 中的 PMC

測試應用程式

執行應用程式,然後選取 [電影應用程式] 連結。

如果您收到類似下列的例外狀況,您可能錯過了 移轉步驟

SqlException: Cannot open database "MvcMovieContext-1" requested by the login. The login failed.

注意

您可能無法在 Price 欄位中輸入小數逗號。 若要對使用逗號 (",") 作為小數點的非英文地區設定和非英文日期格式支援 jQuery 驗證,則必須將應用程式全球化。 如需全球化指示,請參閱此 GitHub 問題 \(英文\)。

檢查產生的資料庫內容類別和註冊

運用 EF Core,使用模型來執行資料存取。 模型是由實體類別和內容物件所組成,其內容物件代表具有資料庫的工作階段。 內容物件可讓您查詢和儲存資料。 資料庫內容衍生自 Microsoft.EntityFrameworkCore.DbContext,並指定要包含在資料模型中的實體。

Scaffolding 會建立 Data/MvcMovieContext.cs 資料庫內容類別:

using Microsoft.EntityFrameworkCore;
using MvcMovie.Models;

namespace MvcMovie.Data
{
    public class MvcMovieContext : DbContext
    {
        public MvcMovieContext (DbContextOptions<MvcMovieContext> options)
            : base(options)
        {
        }

        public DbSet<Movie> Movie { get; set; }
    }
}

上述程式碼會建立 DbSet<Movie> 屬性,代表資料庫中的電影。

ASP.NET Core 內建相依性插入 (DI)。 服務,例如資料庫內容,必須在 Startup 中向 DI 註冊。 需要這些服務的元件會透過建構函式參數提供。

Controllers/MoviesController.cs 檔案中,建構函式會使用相依性插入MvcMovieContext 資料庫內容插入到控制器中。 控制器中的每一個 CRUD 方法都會使用資料庫內容。

Scaffolding 在 Startup.ConfigureServices 中產生下列醒目提示的程式碼:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews();

    services.AddDbContext<MvcMovieContext>(options =>
    options.UseSqlServer(Configuration.GetConnectionString("MvcMovieContext")));
}

ASP.NET Core 設定系統 會讀取「MvcMovieCoNtext」資料庫連接字串。

檢查產生的資料庫連接字串

Scaffolding 已將連接字串新增至 appsettings.json 檔案:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "MvcMovieContext": "Server=(localdb)\\mssqllocaldb;Database=MvcMovieContext-1;Trusted_Connection=True;MultipleActiveResultSets=true"
  }
}

針對本機開發,ASP.NET Core 設定系統 會從 appsettings.json 檔案讀取 ConnectionString 索引碼。

InitialCreate 類別

檢查 Migrations/{timestamp}_InitialCreate.cs 移轉檔案:

public partial class InitialCreate : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.CreateTable(
            name: "Movie",
            columns: table => new
            {
                Id = table.Column<int>(type: "int", nullable: false)
                    .Annotation("SqlServer:Identity", "1, 1"),
                Title = table.Column<string>(type: "nvarchar(max)", nullable: true),
                ReleaseDate = table.Column<DateTime>(type: "datetime2", nullable: false),
                Genre = table.Column<string>(type: "nvarchar(max)", nullable: true),
                Price = table.Column<decimal>(type: "decimal(18,2)", nullable: false)
            },
            constraints: table =>
            {
                table.PrimaryKey("PK_Movie", x => x.Id);
            });
    }

    protected override void Down(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.DropTable(
            name: "Movie");
    }
}

在上述程式碼中:

  • InitialCreate.Up 會建立 Movie 資料表,並將 Id 設為主索引鍵。
  • InitialCreate.Down 會還原 Up 移轉所做的結構描述變更。

控制器中的相依性插入

請開啟 Controllers/MoviesController.cs 檔案並檢查建構函式:

public class MoviesController : Controller
{
    private readonly MvcMovieContext _context;

    public MoviesController(MvcMovieContext context)
    {
        _context = context;
    }

建構函式會使用相依性插入將資料庫內容 (MvcMovieContext) 插入到控制器中。 控制器中的每一個 CRUD 方法都會使用資料庫內容。

測試 Create 頁面。 輸入並提交資料。

測試 EditDetailsDelete 頁面。

強型別模型和 @model 指示詞

稍早在本教學課程中,您已看到控制器如何使用 ViewData 字典傳遞資料或物件給檢視。 ViewData 字典是一種動態物件,提供便利的晚期繫結方式,將資訊傳遞至檢視。

MVC 提供將強型別模型物件傳遞至檢視的能力。 這種強型別的方法可在編譯時檢查程式碼。 Scaffolding 機制在 MoviesController 類別和檢視中傳遞了強型別模型。

檢查 Controllers/MoviesController.cs 檔案中產生的 Details 方法:

// GET: Movies/Details/5
public async Task<IActionResult> Details(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var movie = await _context.Movie
        .FirstOrDefaultAsync(m => m.Id == id);
    if (movie == null)
    {
        return NotFound();
    }

    return View(movie);
}

id 參數通常會傳遞為路由資料。 例如,https://localhost:5001/movies/details/1 設定:

  • 控制器為 movies 控制器,第一個 URL 區段。
  • 動作為 details,第二個 URL 區段。
  • id 為 1,最後一個 URL 區段。

id 可以使用查詢字串傳入,如下列範例所示:

https://localhost:5001/movies/details?id=1

若未提供 id 值,id 參數會定義為可為 Null 的型別 (int?)。

Lambda 運算式會傳遞至 FirstOrDefaultAsync 方法,以選取符合路由資料或查詢字串值的電影實體。

var movie = await _context.Movie
    .FirstOrDefaultAsync(m => m.Id == id);

如果找到電影,則 Movie 模型的執行個體會傳遞至 Details 檢視:

return View(movie);

檢查 Views/Movies/Details.cshtml 檔案的內容:

@model MvcMovie.Models.Movie

@{
    ViewData["Title"] = "Details";
}

<h1>Details</h1>

<div>
    <h4>Movie</h4>
    <hr />
    <dl class="row">
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Title)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Title)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.ReleaseDate)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.ReleaseDate)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Genre)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Genre)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Price)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Price)
        </dd>
    </dl>
</div>
<div>
    <a asp-action="Edit" asp-route-id="@Model.Id">Edit</a> |
    <a asp-action="Index">Back to List</a>
</div>

檢視檔案頂端的 @model 陳述式會指定檢視所預期物件型別。 建立影片控制器時,會包含下列 @model 陳述式:

@model MvcMovie.Models.Movie

@model 指示詞會允許存取控制器傳遞給檢視的影片。 Model 物件為強型別物件。 例如,在 Details.cshtml檢視中,程式碼會使用強型別的 Model 物件,將每個電影欄位傳遞至 DisplayNameForDisplayFor HTML 協助程式。 CreateEdit 方法和檢視也會傳遞 Movie 模型物件。

檢查 Movies 控制器中的 Index.cshtml 檢視和 Index 方法。 請注意程式碼如何在呼叫 View 方法時建立 List 物件。 此程式碼會從 Index 動作方法將 Movies 清單傳遞至檢視:

// GET: Movies
public async Task<IActionResult> Index()
{
    return View(await _context.Movie.ToListAsync());
}

建立電影控制器時,Scaffolding 會在 Index.cshtml 檔案的頂端包含下列 @model 陳述式:

@model IEnumerable<MvcMovie.Models.Movie>

@model 指示詞可使用強型別的 Model 物件,存取控制器傳遞至檢視的電影清單。 例如,在 Index.cshtml 檢視中,程式碼會透過強型別 Model 物件的 foreach 陳述式循環存取電影:

@model IEnumerable<MvcMovie.Models.Movie>

@{
    ViewData["Title"] = "Index";
}

<h1>Index</h1>

<p>
    <a asp-action="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Title)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.ReleaseDate)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Genre)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Price)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Title)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ReleaseDate)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Genre)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Price)
            </td>
            <td>
                <a asp-action="Edit" asp-route-id="@item.Id">Edit</a> |
                <a asp-action="Details" asp-route-id="@item.Id">Details</a> |
                <a asp-action="Delete" asp-route-id="@item.Id">Delete</a>
            </td>
        </tr>
}
    </tbody>
</table>

因為 Model 物件是強型別作為 IEnumerable<Movie> 物件,所以迴圈中每個項目的型別為 Movie。 除了其他優點之外,編譯器會驗證程式碼中使用的型別。

Entity Framework Core 的 SQL 記錄

記錄組態通常是由 appsettings.{Environment}.json 檔案的 Logging 區段所提供。 若要記錄 SQL 陳述式,請將 "Microsoft.EntityFrameworkCore.Database.Command": "Information" 新增至 appsettings.Development.json 檔案:

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=MyDB-2;Trusted_Connection=True;MultipleActiveResultSets=true"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
     ,"Microsoft.EntityFrameworkCore.Database.Command": "Information"
    }
  },
  "AllowedHosts": "*"
}

使用上述 JSON 時,SQL 陳述式會顯示在命令列和 Visual Studio 輸出視窗中。

如需詳細資訊,請參閱在 .NET Core 和 ASP.NET Core 中記錄和此 GitHub 問題

其他資源

在本教學課程中,您可以新增類別來管理資料庫中的電影。 這些類別是 MVC 應用程式的「模型」部分。

搭配 Entity Framework Core (EF Core) 使用這些模型類別,即可使用資料庫。 EF Core 是一種物件關聯式對應 (ORM) 架構,可簡化您必須撰寫的資料存取程式碼。

建立的模型類別稱為 POCO 類別,全名為 Plain Old CLR Objects。 POCO 類別與 EF Core 沒有任何相依性。 它們只會定義資料儲存在資料庫中的屬性。

在本教學課程中,會先建立模型類別,以及 EF Core 建立資料庫。

新增資料模型類別

以滑鼠右鍵按一下 Models 資料夾 >[新增]>[類別]。 將檔案命名為 Movie.cs

使用下列程式碼更新 Movie.cs 檔案:

using System;
using System.ComponentModel.DataAnnotations;

namespace MvcMovie.Models
{
    public class Movie
    {
        public int Id { get; set; }
        public string Title { get; set; }

        [DataType(DataType.Date)]
        public DateTime ReleaseDate { get; set; }
        public string Genre { get; set; }
        public decimal Price { get; set; }
    }
}

Movie 類別包含 Id 欄位,該欄位是資料庫的必要欄位,將作為主索引鍵。

ReleaseDate 上的 DataType 屬性會指定資料的型別 (Date)。 使用此屬性:

  • 使用者不需要在日期欄位中輸入時間資訊。
  • 只會顯示日期,不會顯示時間資訊。

稍後的教學課程會涵蓋 DataAnnotations

新增 NuGet 套件

從 [工具] 功能表中,選取 [NuGet 套件管理員]> [套件管理器主控台] (PMC)。

PMC 功能表

在 PMC 中,執行下列命令:

Install-Package Microsoft.EntityFrameworkCore.SqlServer

上述命令會新增 EF Core SQL Server 提供者。 提供者套件會將 EF Core 套件作為相依性安裝。 其他套件會在本教學課程中稍後的 scaffolding 步驟內自動安裝。

建立資料庫內容類別

資料庫內容類別是協調 Movie 模型 EF Core 功能 (建立、讀取、更新、刪除) 的必要項目。 資料庫內容衍生自 Microsoft.EntityFrameworkCore.DbContext,並指定要包含在資料模型中的實體。

建立 Data 資料夾。

使用下列程式碼新增 Data/MvcMovieContext.cs 檔案:

using Microsoft.EntityFrameworkCore;
using MvcMovie.Models;

namespace MvcMovie.Data
{
    public class MvcMovieContext : DbContext
    {
        public MvcMovieContext (DbContextOptions<MvcMovieContext> options)
            : base(options)
        {
        }

        public DbSet<Movie> Movie { get; set; }
    }
}

上述程式碼會建立實體集的 DbSet<Movie> 屬性。 在 Entity Framework 詞彙中,實體集通常會對應至資料庫資料表。 實體會對應至資料表中的資料列。

登錄資料庫內容

ASP.NET Core 內建相依性插入 (DI)。 服務 (例如 EF Core 資料庫內容) 必須在應用程式啟動期間向 DI 進行註冊。 需要這些服務的元件 (例如 Razor 頁面) 會透過建構函式參數提供。 取得資料庫內容執行個體的建構函式程式碼,本教學課程中稍後會示範。 在本節中,您會向 DI 容器註冊資料庫內容。

Startup.cs 最上方加入下列 using 陳述式:

using MvcMovie.Data;
using Microsoft.EntityFrameworkCore;

Startup.ConfigureServices 中新增下列醒目提示的程式碼:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews();

    services.AddDbContext<MvcMovieContext>(options =>
    options.UseSqlServer(Configuration.GetConnectionString("MvcMovieContext")));
}

連接字串的名稱,會透過呼叫 DbContextOptions 物件上的方法來傳遞至內容。 作為本機開發之用,ASP.NET Core 設定系統會從 appsettings.json 檔案讀取連接字串。

檢查資料庫連線字串

將連接字串新增至 appsettings.json 檔案:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "MvcMovieContext": "Server=(localdb)\\mssqllocaldb;Database=MvcMovieContext-1;Trusted_Connection=True;MultipleActiveResultSets=true"
  }
}

建置專案以檢查編譯器錯誤。

Scaffold 影片頁面

使用 scaffolding 工具來為影片模型產生建立、讀取、更新和刪除 (CRUD) 頁面。

在 [方案總管] 中以滑鼠右鍵按一下 Controllers 資料夾 > [新增] > [新增 Scaffold 項目]

上方步驟的檢視

在 [新增 Scaffold] 對話方塊中,選取 [使用 Entity Framework 執行檢視的 MVC 控制器] > [新增]

[新增 Scaffold] 對話方塊

完成 [新增控制器] 對話方塊:

  • 模型類別: Movie (MvcMovie.Models)
  • 資料內容類別: MvcMovieContext (MvcMovie.Data)

新增資料內容

  • 檢視:保持核取預設的每一個選項
  • 控制器名稱:保留預設值 MoviesController
  • 選擇新增

Visual Studio 會建立:

  • 電影控制器 (Controllers/MoviesController.cs)
  • Create、Delete、Details、Edit 和 Index 頁面的 Razor 檢視檔案 (*Views/Movies/`.cshtml`)

自動建立這些檔案的流程稱為 scaffolding

您目前尚無法使用 scaffold 頁面,因為資料庫不存在。 若您執行應用程式並按一下 [影片應用程式] 連結,您會收到無法開啟資料庫找不到資料表:Movie錯誤訊息。

初始移轉

使用 EF Core移轉 功能來建立資料庫。 移轉是一組工具,可讓您建立和更新資料庫,使其與您的資料模型相符。

從 [工具] 功能表中,選取 [NuGet 套件管理員]> [套件管理器主控台] (PMC)。

在 PMC 中,輸入下列命令:

Add-Migration InitialCreate
Update-Database
  • Add-Migration InitialCreate:產生 Migrations/{timestamp}_InitialCreate.cs 移轉檔案。 InitialCreate 引數是移轉名稱。 您可以使用任何名稱,但依照慣例,會選取描述移轉的名稱。 因為這是第一次移轉,所產生類別會包含建立資料庫結構描述的程式碼。 資料庫結構描述是以 MvcMovieContext 類別為基礎。

  • Update-Database:將資料庫更新到最新的移轉,即上一個命令建立的移轉。 此命令會在 Migrations/{time-stamp}_InitialCreate.cs 檔案中執行 Up 方法,以建立資料庫。

    資料庫更新命令會產生下列警告:

    沒有為實體型別 'Movie' 上的十進位資料行 'Price' 指定型別。 如果它們不符合預設的有效位數和小數位數,會導致以無訊息模式截斷這些值。 使用 'HasColumnType()' 明確指定可容納所有值的 SQL 伺服器資料行型別。

    您可以忽略該警告,稍後的教學課程中將修正此問題。

如需 EF Core PMC 工具的詳細資訊,請參閱 EF Core 工具參考 - Visual Studio 中的 PMC

InitialCreate 類別

檢查 Migrations/{timestamp}_InitialCreate.cs 移轉檔案:

public partial class InitialCreate : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.CreateTable(
            name: "Movie",
            columns: table => new
            {
                Id = table.Column<int>(nullable: false)
                    .Annotation("SqlServer:ValueGenerationStrategy", 
                                 SqlServerValueGenerationStrategy.IdentityColumn),
                Title = table.Column<string>(nullable: true),
                ReleaseDate = table.Column<DateTime>(nullable: false),
                Genre = table.Column<string>(nullable: true),
                Price = table.Column<decimal>(nullable: false)
            },
            constraints: table =>
            {
                table.PrimaryKey("PK_Movie", x => x.Id);
            });
    }

    protected override void Down(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.DropTable(
            name: "Movie");
    }
}

Up 方法會建立 Movie 資料表,並將 Id 設為主索引鍵。 Down 方法會還原 Up 移轉所做的結構描述變更。

測試應用程式

  • 執行應用程式,然後按一下 [影片應用程式] 連結。

    若您收到與下列內容相似的例外狀況:

SqlException: Cannot open database "MvcMovieContext-1" requested by the login. The login failed.

您可能遺漏了移轉步驟

  • 測試 Create 頁面。 輸入並提交資料。

    注意

    您可能無法在 Price 欄位中輸入小數逗號。 若要對使用逗號 (",") 作為小數點的非英文地區設定和非英文日期格式支援 jQuery 驗證,則必須將應用程式全球化。 如需全球化指示,請參閱此 GitHub 問題 \(英文\)。

  • 測試 EditDetailsDelete 頁面。

控制器中的相依性插入

請開啟 Controllers/MoviesController.cs 檔案並檢查建構函式:

public class MoviesController : Controller
{
    private readonly MvcMovieContext _context;

    public MoviesController(MvcMovieContext context)
    {
        _context = context;
    }

建構函式會使用相依性插入將資料庫內容 (MvcMovieContext) 插入到控制器中。 控制器中的每一個 CRUD 方法都會使用資料庫內容。

強型別模型和 @model 關鍵字

稍早在本教學課程中,您已看到控制器如何使用 ViewData 字典傳遞資料或物件給檢視。 ViewData 字典是一種動態物件,提供便利的晚期繫結方式,將資訊傳遞至檢視。

MVC 也提供將強型別模型物件傳遞至檢視的能力。 這種強型別的方法可在編譯時檢查程式碼。 此方法所使用的 scaffolding 機制 (即傳遞強型別模型),包括 MoviesController 類別和檢視。

檢查 Controllers/MoviesController.cs 檔案中產生的 Details 方法:

// GET: Movies/Details/5
public async Task<IActionResult> Details(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var movie = await _context.Movie
        .FirstOrDefaultAsync(m => m.Id == id);
    if (movie == null)
    {
        return NotFound();
    }

    return View(movie);
}

id 參數通常會傳遞為路由資料。 例如,https://localhost:5001/movies/details/1 設定:

  • 控制器為 movies 控制器 (第一個 URL 區段)。
  • 動作為 details (第二個 URL 區段)。
  • 識別碼為 1 (最後一個 URL 區段)。

您也可以在 id 中使用查詢字串傳遞,如下所示:

https://localhost:5001/movies/details?id=1

若未提供識別碼值,id 參數會定義為可為 Null 的型別 (int?)。

Lambda 運算式會傳遞至 FirstOrDefaultAsync,以選取符合路由資料或查詢字串值的電影實體。

var movie = await _context.Movie
    .FirstOrDefaultAsync(m => m.Id == id);

如果找到電影,則 Movie 模型的執行個體會傳遞至 Details 檢視:

return View(movie);

檢查 Views/Movies/Details.cshtml 檔案的內容:

@model MvcMovie.Models.Movie

@{
    ViewData["Title"] = "Details";
}

<h1>Details</h1>

<div>
    <h4>Movie</h4>
    <hr />
    <dl class="row">
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Title)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Title)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.ReleaseDate)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.ReleaseDate)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Genre)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Genre)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Price)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Price)
        </dd>
    </dl>
</div>
<div>
    <a asp-action="Edit" asp-route-id="@Model.Id">Edit</a> |
    <a asp-action="Index">Back to List</a>
</div>

檢視檔案頂端的 @model 陳述式會指定檢視所預期物件型別。 建立影片控制器時,會包含下列 @model 陳述式:

@model MvcMovie.Models.Movie

@model 指示詞會允許存取控制器傳遞給檢視的影片。 Model 物件為強型別物件。 例如,在 Details.cshtml檢視中,程式碼會使用強型別的 Model 物件,將每個電影欄位傳遞至 DisplayNameForDisplayFor HTML 協助程式。 CreateEdit 方法和檢視也會傳遞 Movie 模型物件。

檢查 Movies 控制器中的 Index.cshtml 檢視和 Index 方法。 請注意程式碼如何在呼叫 View 方法時建立 List 物件。 此程式碼會從 Index 動作方法將 Movies 清單傳遞至檢視:

// GET: Movies
public async Task<IActionResult> Index()
{
    return View(await _context.Movie.ToListAsync());
}

建立電影控制器時,Scaffolding 會在 Index.cshtml 檔案的頂端包含下列 @model 陳述式:

@model IEnumerable<MvcMovie.Models.Movie>

@model 指示詞可讓您使用強型別的 Model 物件,存取控制器傳遞至檢視的電影清單。 例如,在 Index.cshtml 檢視中,程式碼會透過強型別 Model 物件的 foreach 陳述式循環存取電影:

@model IEnumerable<MvcMovie.Models.Movie>

@{
    ViewData["Title"] = "Index";
}

<h1>Index</h1>

<p>
    <a asp-action="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Title)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.ReleaseDate)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Genre)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Price)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Title)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ReleaseDate)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Genre)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Price)
            </td>
            <td>
                <a asp-action="Edit" asp-route-id="@item.Id">Edit</a> |
                <a asp-action="Details" asp-route-id="@item.Id">Details</a> |
                <a asp-action="Delete" asp-route-id="@item.Id">Delete</a>
            </td>
        </tr>
}
    </tbody>
</table>

因為 Model 物件是強型別 (作為 IEnumerable<Movie> 物件),所以迴圈中每個項目的類型為 Movie。 撇開其他優點,這表示您會進行程式碼編譯時期檢查。

其他資源