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

作者 :Rick AndersonJon P Smith

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

這些模型類別會與 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 欄位,該欄位是資料庫的必要欄位,將作為主索引鍵。

上的 DataTypeReleaseDate 屬性會指定資料 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 工具來產生 Create 電影模型的 、 ReadUpdateDelete (CRUD) 頁面。

方案總管中,以滑鼠右鍵按一下Controllers資料夾,然後選取 [新增 > Scaffolded 專案]。

上方步驟的檢視

在 [ 新增 Scaffold ] 對話方塊中, 使用 Entity Framework > Add 選取具有檢視的 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 頁面,因為資料庫不存在。 執行應用程式並選取 [電影應用程式 ] 連結會導致 無法開啟資料庫沒有這類資料表:電影 錯誤訊息。

建置應用程式

建置應用程式。 編譯器會產生數個關於如何處理值的警告 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:將資料庫更新至上一個命令所建立的最新移轉。 此命令會在 Up 檔案中 Migrations/{time-stamp}_InitialCreate.cs 執行 方法,以建立資料庫。

此命令 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-7dc5b790-765f-4381-988c-5167405bb107;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 方法都會使用資料庫內容。

測試 [ 建立] 頁面。 輸入並提交資料。

測試 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 會定義為 可為 Null 的類型 (int?) id

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 ,程式碼會將每個電影欄位傳遞給 DisplayNameFor 具有強型別 Model 物件的 和 DisplayFor HTML 協助程式。 CreateEdit 方法和檢視也會傳遞 Movie 模型物件。

Index.cshtml檢查 Movies 控制器中的檢視和 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 。 除了其他優點之外,編譯器會驗證程式代碼中使用的類型。

其他資源

新增資料模型類別

以滑鼠右鍵按一下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 欄位,該欄位是資料庫的必要欄位,將作為主索引鍵。

上的 DataTypeReleaseDate 屬性會指定資料 Date () 的類型。 使用此屬性:

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

稍後的教學課程會涵蓋 DataAnnotations

新增 NuGet 套件

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

PMC 功能表

在 PMC 中,執行下列命令:

Install-Package Microsoft.EntityFrameworkCore.Design

上述命令會新增:

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

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

Scaffold 影片頁面

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

方案總管中,以滑鼠右鍵按一下Controllers資料夾,然後選取[新增 > Scaffolded 專案]。

上方步驟的檢視

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

[新增 Scaffold] 對話方塊

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

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

新增資料內容保留預設值

Scaffolding 會更新下列各項:

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

Scaffolding 會建立下列專案:

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

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

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

初始移轉

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

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

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

Add-Migration InitialCreate
Update-Database

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

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

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 方法都會使用資料庫內容。

測試 [ 建立] 頁面。 輸入並提交資料。

測試 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 會定義為 可為 Null 的類型 (int?) id

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 ,程式碼會將每個電影欄位傳遞給 DisplayNameFor 具有強型別 Model 物件的 和 DisplayFor HTML 協助程式。 CreateEdit 方法和檢視也會傳遞 Movie 模型物件。

Index.cshtml檢查 Movies 控制器中的檢視和 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 記錄

記錄組態通常是由 Logging 檔案的 appsettings.{Environment}.json 區段所提供。 若要記錄 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": "*"
}

使用上述 JS ON 時,SQL 語句會顯示在命令列和 Visual Studio 輸出視窗中。

如需詳細資訊,請參閱登入 .NET Core 和 ASP.NET Core和此GitHub 問題

其他資源

新增資料模型類別

以滑鼠右鍵按一下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 欄位,該欄位是資料庫的必要欄位,將作為主索引鍵。

上的 DataTypeReleaseDate 屬性會指定資料 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 Pages) 。 取得資料庫內容執行個體的建構函式程式碼,本教學課程中稍後會示範。 在本節中,您會向 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) 頁面。

方案總管中,以滑鼠右鍵按一下[控制器] 資料夾> [新增 > Scaffolded 專案]。

上方步驟的檢視

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

[新增 Scaffold] 對話方塊

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

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

新增資料內容

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

Visual Studio 會建立:

  • 電影控制器 (Controllers/MoviesController.cs)
  • Razor 檢視建立、刪除、詳細資料、編輯和索引頁面的檔案, (*Views/Movies/'.cshtml')

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

您目前尚無法使用 scaffold 頁面,因為資料庫不存在。 如果您執行應用程式並按一下 [電影應用程式 ] 連結,您會收到 [無法開啟資料庫 ] 或 沒有這類資料表:電影 錯誤訊息。

初始移轉

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

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

在 PMC 中,輸入下列命令:

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

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

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

    沒有為實體型別 '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.

您可能遺漏了移轉步驟

  • 測試 [ 建立] 頁面。 輸入並提交資料。

    注意

    您可能無法在 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 ,程式碼會將每個電影欄位傳遞給 DisplayNameFor 具有強型別 Model 物件的 和 DisplayFor HTML 協助程式。 CreateEdit 方法和檢視也會傳遞 Movie 模型物件。

Index.cshtml檢查 Movies 控制器中的檢視和 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。 撇開其他優點,這表示您會進行程式碼編譯時期檢查。

其他資源