將模型添加到 ASP.NET Core MVC 應(yīng)用

2019-04-17 08:57 更新

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

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

要?jiǎng)?chuàng)建的模型類稱為 POCO 類(源自“簡(jiǎn)單傳統(tǒng) CLR 對(duì)象”),因?yàn)樗鼈兣c EF Core 沒(méi)有任何依賴關(guān)系。 它們只定義將存儲(chǔ)在數(shù)據(jù)庫(kù)中的數(shù)據(jù)的屬性。

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

添加數(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ù)庫(kù)需要 Id 字段以獲取主鍵。

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

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

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

搭建“電影”模型的基架

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

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

上述步驟的視圖

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

“添加基架”對(duì)話框

填寫“添加控制器”對(duì)話框:

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

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

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

“添加控制器”對(duì)話框

Visual Studio 將創(chuàng)建:

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

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

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

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

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

初始遷移

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

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

    PMC 菜單

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

    console
    Add-Migration Initial
    Update-Database
    

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

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

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

檢查通過(guò)依賴關(guān)系注入注冊(cè)的上下文

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

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

我們來(lái)研究以下 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ù)模型中包含哪些實(shí)體:

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; }
    }
}

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

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

測(cè)試應(yīng)用

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

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

console

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

缺少遷移步驟。

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

檢查 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")));
}

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

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

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

C#

public class MoviesController : Controller
{
    private readonly MvcMovieContext _context;

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

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

強(qiáng)類型模型和 @model 關(guān)鍵詞

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

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

檢查 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 的設(shè)置如下:

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

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

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

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

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

C#

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

如果找到了電影,Movie 模型的實(shí)例則會(huì)被傳遞到 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>

通過(guò)將 @model 語(yǔ)句包括在視圖文件的頂端,可以指定視圖期望的對(duì)象類型。 創(chuàng)建電影控制器時(shí),會(huì)自動(dòng)在 Details.cshtml 文件的頂端包括以下 @model 語(yǔ)句:

HTML

@model MvcMovie.Models.Movie

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

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

C#

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

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

HTML

@model IEnumerable<MvcMovie.Models.Movie>

@model 指令使你能夠使用強(qiáng)類型的 Model 對(duì)象訪問(wèn)控制器傳遞給視圖的電影列表。 例如,在 Index.cshtml 視圖中,代碼使用 foreach 語(yǔ)句通過(guò)強(qiáng)類型 Model 對(duì)象對(duì)電影進(jìn)行循環(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>

因?yàn)?nbsp;Model 對(duì)象為強(qiáng)類型(作為 IEnumerable<Movie> 對(duì)象),因此循環(huán)中的每個(gè)項(xiàng)都被類型化為 Movie。


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

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)