將模型添加到 ASP.NET Core MVC 應用

2019-04-17 08:57 更新

在本部分中將添加用于管理數(shù)據(jù)庫中的電影的類。 這些類將是 MVC 應用的“Model”部分。

可以結(jié)合 Entity Framework Core (EF Core) 使用這些類來處理數(shù)據(jù)庫。 EF Core 是對象關系映射 (ORM) 框架,可以簡化需要編寫的數(shù)據(jù)訪問代碼。

要創(chuàng)建的模型類稱為 POCO 類(源自“簡單傳統(tǒng) CLR 對象”),因為它們與 EF Core 沒有任何依賴關系。 它們只定義將存儲在數(shù)據(jù)庫中的數(shù)據(jù)的屬性。

在本教程中,首先要編寫模型類,然后 EF Core 將創(chuàng)建數(shù)據(jù)庫。 有一種備選方法(此處未介紹):從現(xiàn)有數(shù)據(jù)庫生成模型類。 有關此方法的信息,請參閱 ASP.NET Core - Existing Database(ASP.NET Core - 現(xiàn)有數(shù)據(jù)庫)。

添加數(shù)據(jù)模型類

右鍵單擊 Models 文件夾,然后單擊“添加” > “類”。 將類命名“Movie”。

向 Movie 類添加以下屬性:

C#
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 類包含:

  • 數(shù)據(jù)庫需要 Id 字段以獲取主鍵。

  • [DataType(DataType.Date)]DataType 屬性指定數(shù)據(jù)的類型 (Date)。 通過此特性:

    • 用戶無需在數(shù)據(jù)字段中輸入時間信息。
    • 僅顯示日期,而非時間信息。

DataAnnotations 會在后續(xù)教程中介紹。

搭建“電影”模型的基架

在此部分,將搭建“電影”模型的基架。 確切地說,基架工具將生成頁面,用于對“電影”模型執(zhí)行創(chuàng)建、讀取、更新和刪除 (CRUD) 操作。

在解決方案資源管理器中,右鍵單擊“Controllers”文件夾 >“添加”>“新搭建基架的項目”。

上述步驟的視圖

在“添加基架”對話框中,選擇“包含視圖的 MVC 控制器(使用 Entity Framework)”>“添加”。

“添加基架”對話框

填寫“添加控制器”對話框:

  • 模型類:Movie (MvcMovie.Models)
  • 數(shù)據(jù)上下文類:選擇 + 圖標并添加默認的 MvcMovie.Models.MvcMovieContext

“添加數(shù)據(jù)”上下文

  • 視圖:將每個選項保持為默認選中狀態(tài)
  • 控制器名稱:保留默認的 MoviesController
  • 選擇“添加”

“添加控制器”對話框

Visual Studio 將創(chuàng)建:

  • Entity Framework Core 數(shù)據(jù)庫上下文類 (Data/MvcMovieContext.cs)
  • 電影控制器 (Controllers/MoviesController.cs)
  • “創(chuàng)建”、“刪除”、“詳細信息”、“編輯”和“索引”頁面的 Razor 視圖文件 (Views/Movies/*.cshtml)

自動創(chuàng)建數(shù)據(jù)庫上下文和 CRUD(創(chuàng)建、讀取、更新和刪除)操作方法和視圖的過程稱為“搭建基架”。

如果運行應用并單擊“Mvc 電影”鏈接,則會出現(xiàn)以下類似的錯誤:

error

An unhandled exception occurred while processing the request.

SqlException: Cannot open database "MvcMovieContext-<GUID removed>" requested by the login. The login failed.
Login failed for user 'Rick'.

System.Data.SqlClient.SqlInternalConnectionTds..ctor(DbConnectionPoolIdentity identity, SqlConnectionString

你需要創(chuàng)建數(shù)據(jù)庫,并且使用 EF Core 遷移功能來執(zhí)行此操作。 通過遷移可創(chuàng)建與數(shù)據(jù)模型匹配的數(shù)據(jù)庫,并在數(shù)據(jù)模型更改時更新數(shù)據(jù)庫架構(gòu)。

初始遷移

在本節(jié)中,將完成以下任務:

  • 添加初始遷移。
  • 使用初始遷移來更新數(shù)據(jù)庫。
  1. 從“工具”菜單中,選擇“NuGet 包管理器” > “包管理器控制臺”(PMC)。

    PMC 菜單

  2. 在 PMC 中,輸入以下命令:

    console
    Add-Migration Initial
    Update-Database
    

    Add-Migration 命令生成用于創(chuàng)建初始數(shù)據(jù)庫架構(gòu)的代碼。

    數(shù)據(jù)庫架構(gòu)基于在 MvcMovieContext 類中(位于 Data/MvcMovieContext.cs 文件中)中指定的模型。 Initial 參數(shù)是遷移名稱。 可以使用任何名稱,但是按照慣例,會使用可說明遷移的名稱。 有關更多信息,請參見教程:使用遷移功能 - ASP.NET MVC 和 EF Core

    Update-Database 命令在用于創(chuàng)建數(shù)據(jù)庫的 Migrations/{time-stamp}_InitialCreate.cs 文件中運行 Up 方法。

檢查通過依賴關系注入注冊的上下文

ASP.NET Core 通過依賴關系注入 (DI) 生成。 服務(例如 EF Core 數(shù)據(jù)庫上下文)在應用程序啟動期間通過 DI 注冊。 需要這些服務(如 Razor 頁面)的組件通過構(gòu)造函數(shù)提供相應服務。 本教程的后續(xù)部分介紹了用于獲取 DB 上下文實例的構(gòu)造函數(shù)代碼。

基架工具自動創(chuàng)建數(shù)據(jù)庫上下文并將其注冊到 DI 容器。

我們來研究以下 Startup.ConfigureServices 方法。 基架添加了突出顯示的行:

C#
public void ConfigureServices(IServiceCollection services)
{
    services.Configure<CookiePolicyOptions>(options =>
    {
        // This lambda determines whether user consent for non-essential cookies 
        // is needed for a given request.
        options.CheckConsentNeeded = context => true;
        options.MinimumSameSitePolicy = SameSiteMode.None;
    });


    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

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

MvcMovieContext 為 Movie 模型協(xié)調(diào) EF Core 功能(創(chuàng)建、讀取、更新、刪除等)。 數(shù)據(jù)上下文 (MvcMovieContext) 派生自 Microsoft.EntityFrameworkCore.DbContext。 數(shù)據(jù)上下文指定數(shù)據(jù)模型中包含哪些實體:

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;

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

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

前面的代碼為實體集創(chuàng)建 DbSet<Movie> 屬性。 在實體框架術語中,實體集通常與數(shù)據(jù)表相對應。 實體對應表中的行。

通過調(diào)用 DbContextOptions 對象中的一個方法將連接字符串名稱傳遞到上下文。 進行本地開發(fā)時, ASP.NET Core 配置系統(tǒng) 在 appsettings.json 文件中讀取數(shù)據(jù)庫連接字符串。

測試應用

  • 運行應用并將 /Movies 追加到瀏覽器中的 URL (http://localhost:port/movies)。

如果收到如下所示數(shù)據(jù)庫異常:

console

SqlException: Cannot open database "MvcMovieContext-GUID" requested by the login. The login failed.
Login failed for user 'User-name'.

缺少遷移步驟

  • 測試“創(chuàng)建”鏈接。 備注可能無法在 Price 字段中輸入十進制逗號。 若要使 jQuery 驗證支持使用逗號(“,”)表示小數(shù)點的非英語區(qū)域設置,以及支持非美國英語日期格式,應用必須進行全球化。 有關全球化的說明,請參閱此 GitHub 問題。
  • 測試“編輯”、“詳細信息”和“刪除”鏈接。

檢查 Startup 類:

C#

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<CookiePolicyOptions>(options =>
    {
        // This lambda determines whether user consent for non-essential cookies 
        // is needed for a given request.
        options.CheckConsentNeeded = context => true;
        options.MinimumSameSitePolicy = SameSiteMode.None;
    });


    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

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

上面突出顯示的代碼顯示了要添加到依賴關系注入容器的電影數(shù)據(jù)庫上下文:

  • services.AddDbContext<MvcMovieContext>(options => 指定要使用的數(shù)據(jù)庫和連接字符串。
  • => 是 lambda 運算符

打開 Controllers/MoviesController.cs 文件并檢查構(gòu)造函數(shù):

C#

public class MoviesController : Controller
{
    private readonly MvcMovieContext _context;

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

構(gòu)造函數(shù)使用依賴關系注入將數(shù)據(jù)庫上下文 (MvcMovieContext) 注入到控制器中。 數(shù)據(jù)庫上下文將在控制器中的每個 CRUD 方法中使用。

強類型模型和 @model 關鍵詞

在本教程之前的內(nèi)容中,已經(jīng)介紹了控制器如何使用 ViewData 字典將數(shù)據(jù)或?qū)ο髠鬟f給視圖。 ViewData 字典是一個動態(tài)對象,提供了將信息傳遞給視圖的方便的后期綁定方法。

MVC 還提供將強類型模型對象傳遞給視圖的功能。 憑借此強類型方法可更好地對代碼進行編譯時檢查。 基架機制在創(chuàng)建方法和視圖時,通過 MoviesController 類和視圖使用了此方法(即傳遞強類型模型)。

檢查 Controllers/MoviesController.cs 文件中生成的 Details 方法:

C#

// 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 參數(shù)通常作為路由數(shù)據(jù)傳遞。 例如 https://localhost:5001/movies/details/1 的設置如下:

  • 控制器被設置為 movies 控制器(第一個 URL 段)。
  • 操作被設置為 details(第二個 URL 段)。
  • ID 被設置為 1(最后一個 URL 段)。

還可以使用查詢字符串傳入 id,如下所示:

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

在未提供 ID 值的情況下,id 參數(shù)可定義為可以為 null 的類型 (int?)。

Lambda 表達式會被傳入 FirstOrDefaultAsync 以選擇與路由數(shù)據(jù)或查詢字符串值相匹配的電影實體。

C#

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

如果找到了電影,Movie 模型的實例則會被傳遞到 Details 視圖:

C#

return View(movie);

檢查 Views/Movies/Details.cshtml 文件的內(nèi)容:

HTML

@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 語句包括在視圖文件的頂端,可以指定視圖期望的對象類型。 創(chuàng)建電影控制器時,會自動在 Details.cshtml 文件的頂端包括以下 @model 語句:

HTML

@model MvcMovie.Models.Movie

此 @model 指令使你能夠使用強類型的 Model 對象訪問控制器傳遞給視圖的電影。 例如,在 Details.cshtml 視圖中,代碼通過強類型的 Model 對象將每個電影字段傳遞給 DisplayNameFor 和 DisplayForHTML 幫助程序。 Create 和 Edit 方法以及視圖也傳遞一個 Movie 模型對象。

檢查電影控制器中的 Index.cshtml 視圖和 Index 方法。 請注意代碼在調(diào)用 View 方法時是如何創(chuàng)建 List 對象的。 代碼將此 Movies 列表從 Index 操作方法傳遞給視圖:

C#

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

創(chuàng)建電影控制器時,基架會自動在 Index.cshtml 文件的頂端包含以下 @model 語句:

HTML

@model IEnumerable<MvcMovie.Models.Movie>

@model 指令使你能夠使用強類型的 Model 對象訪問控制器傳遞給視圖的電影列表。 例如,在 Index.cshtml 視圖中,代碼使用 foreach 語句通過強類型 Model 對象對電影進行循環(huán)遍歷:

HTML

@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> 對象),因此循環(huán)中的每個項都被類型化為 Movie。


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號