在本部分中將添加用于管理數(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ù)庫)。
右鍵單擊 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ù)庫需要 Id
字段以獲取主鍵。
[DataType(DataType.Date)]
:DataType 屬性指定數(shù)據(jù)的類型 (Date
)。 通過此特性:
DataAnnotations 會在后續(xù)教程中介紹。
在此部分,將搭建“電影”模型的基架。 確切地說,基架工具將生成頁面,用于對“電影”模型執(zhí)行創(chuàng)建、讀取、更新和刪除 (CRUD) 操作。
在解決方案資源管理器中,右鍵單擊“Controllers”文件夾 >“添加”>“新搭建基架的項目”。
在“添加基架”對話框中,選擇“包含視圖的 MVC 控制器(使用 Entity Framework)”>“添加”。
填寫“添加控制器”對話框:
Visual Studio 將創(chuàng)建:
自動創(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é)中,將完成以下任務:
從“工具”菜單中,選擇“NuGet 包管理器” > “包管理器控制臺”(PMC)。
在 PMC 中,輸入以下命令:
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
方法。 基架添加了突出顯示的行:
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ù)模型中包含哪些實體:
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ù)庫連接字符串。
如果收到如下所示數(shù)據(jù)庫異常:
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")));
}
上面突出顯示的代碼顯示了要添加到依賴關系注入容器的電影數(shù)據(jù)庫上下文:
打開 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 方法中使用。
在本教程之前的內(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 的設置如下:
還可以使用查詢字符串傳入 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。
更多建議: