在本部分中將添加用于管理數(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ù))。
右鍵單擊 Models 文件夾,然后單擊“添加” > “類”。 將類命名“Movie”。
向 Movie
類添加以下屬性:
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ò)此特性:
DataAnnotations 會(huì)在后續(xù)教程中介紹。
在此部分,將搭建“電影”模型的基架。 確切地說(shuō),基架工具將生成頁(yè)面,用于對(duì)“電影”模型執(zhí)行創(chuàng)建、讀取、更新和刪除 (CRUD) 操作。
在解決方案資源管理器中,右鍵單擊“Controllers”文件夾 >“添加”>“新搭建基架的項(xiàng)目”。
在“添加基架”對(duì)話框中,選擇“包含視圖的 MVC 控制器(使用 Entity Framework)”>“添加”。
填寫“添加控制器”對(duì)話框:
Visual Studio 將創(chuàng)建:
自動(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ù):
從“工具”菜單中,選擇“NuGet 包管理器” > “包管理器控制臺(tái)”(PMC)。
在 PMC 中,輸入以下命令:
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
方法。
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
方法。 基架添加了突出顯示的行:
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í)體:
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ù)連接字符串。
如果收到如下所示數(shù)據(jù)庫(kù)異常:
console
SqlException: Cannot open database "MvcMovieContext-GUID" requested by the login. The login failed.
Login failed for user 'User-name'.
缺少遷移步驟。
檢查 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ù)上下文:
打開(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 方法中使用。
在本教程之前的內(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è)置如下:
還可以使用查詢字符串傳入 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。
更多建議: