添加数据模型类
右键单击 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 类包含:
数据库需要 Id 字段以获取主键。
[DataType(DataType.Date)]:DataType 属性指定数据的类型 (Date)。 通过此特性:用户无需在数据字段中输入时间信息。仅显示日期,而非时间信息。 搭建“电影”模型的基架 基架工具将生成页面,用于对“电影”模型执行创建、读取、更新和删除 (CRUD) 操作。在解决方案资源管理器中,右键单击“Controllers”文件夹 >“添加”>“新搭建基架的项目”。在“添加基架”对话框中,选择“包含视图的 MVC 控制器(使用 Entity Framework)”>“添加”。填写“添加控制器”对话框:模型类:Movie (MvcMovie.Models)
数据上下文类:选择 + 图标并添加默认的 MvcMovie.Models.MvcMovieContext视图:将每个选项保持为默认选中状态控制器名称:保留默认的 MoviesController选择“添加”Visual Studio 将创建:
Entity Framework Core 数据库上下文类 (Data/MvcMovieContext.cs)电影控制器 (Controllers/MoviesController.cs)“创建”、“删除”、“详细信息”、“编辑”和“索引”页面的 Razor 视图文件 (Views/Movies/*.cshtml)自动创建数据库上下文和 CRUD(创建、读取、更新和删除)操作方法和视图的过程称为“搭建基架”。
初始迁移
添加初始迁移。使用初始迁移来更新数据库。1.从“工具”菜单中,选择“NuGet 包管理器” > “包管理器控制台”(PMC)。2.在 PMC 中,输入以下命令: Add-Migration InitialUpdate-Database
Add-Migration 命令生成用于创建初始数据库架构的代码。
数据库架构基于在 MvcMovieContext 类中(位于 Data/MvcMovieContext.cs 文件中)中指定的模型。 Initial 参数是迁移名称。 可以使用任何名称,但是按照惯例,会使用可说明迁移的名称。 有关更多信息,请参见教程:使用迁移功能 - ASP.NET MVC 和 EF Core。Update-Database 命令在用于创建数据库的 Migrations/{time-stamp}_InitialCreate.cs 文件中运行 Up 方法。
检查通过依赖关系注入注册的上下文
ASP.NET Core 通过依赖关系注入 (DI) 生成。 服务(例如 EF Core 数据库上下文)在应用程序启动期间通过 DI 注册。 需要这些服务(如 Razor 页面)的组件通过构造函数提供相应服务。基架工具自动创建数据库上下文并将其注册到 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 模型协调 EF Core 功能(创建、读取、更新、删除等)。 数据上下文 (MvcMovieContext) 派生自 Microsoft.EntityFrameworkCore.DbContext。 数据上下文指定数据模型中包含哪些实体: 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; }
}}
前面的代码为实体集创建 DbSet<Movie> 属性。 在实体框架术语中,实体集通常与数据表相对应。 实体对应表中的行。
通过调用 DbContextOptions 对象中的一个方法将连接字符串名称传递到上下文。 进行本地开发时, ASP.NET Core 配置系统 在 appsettings.json 文件中读取数据库连接字符串。
测试应用
运行应用并将 /Movies 追加到浏览器中的 URL (http://localhost:port/movies)。如果收到如下所示数据库异常:缺少迁移步骤。
SqlException: Cannot open database "MvcMovieContext-GUID" requested by the login. The login failed.Login failed for user 'User-name'.检查 Startup 类:ConfigureServices方法
services.AddDbContext<MvcMovieContext>(options => options.UseSqlServer(Configuration.GetConnectionString("MvcMovieContext"))); 上面突出显示的代码显示了要添加到依赖关系注入容器的电影数据库上下文: services.AddDbContext<MvcMovieContext>(options => 指定要使用的数据库和连接字符串。 => 是 lambda 运算符打开 Controllers/MoviesController.cs 文件并检查构造函数: public class MoviesController : Controller{ private readonly MvcMovieContext _context;public MoviesController(MvcMovieContext context)
{ _context = context; } 构造函数使用依赖关系注入将数据库上下文 (MvcMovieContext) 注入到控制器中。 数据库上下文将在控制器中的每个 CRUD 方法中使用。 强类型模型和 @model 关键词 MVC 还提供将强类型模型对象传递给视图的功能。 凭借此强类型方法可更好地对代码进行编译时检查。 基架机制在创建方法和视图时,通过 MoviesController 类和视图使用了此方法(即传递强类型模型)。检查 Controllers/MoviesController.cs 文件中生成的 Details 方法: // GET: Movies/Details/5public 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 值的情况下,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 语句包括在视图文件的顶端,可以指定视图期望的对象类型。
创建电影控制器时,会自动在 Details.cshtml 文件的顶端包括以下 @model 语句:@model MvcMovie.Models.Movie此 @model 指令使你能够使用强类型的 Model 对象访问控制器传递给视图的电影。 例如,在 Details.cshtml 视图中,代码通过强类型的 Model 对象将每个电影字段传递给 DisplayNameFor 和 DisplayForHTML 帮助程序。 Create 和 Edit 方法以及视图也传递一个 Movie 模型对象。检查电影控制器中的 Index.cshtml 视图和 Index 方法。 请注意代码在调用 View 方法时是如何创建 List 对象的。 代码将此 Movies 列表从 Index 操作方法传递给视图:// GET: Moviespublic async Task<IActionResult> Index(){ return View(await _context.Movie.ToListAsync());}
创建电影控制器时,基架会自动在 Index.cshtml 文件的顶端包含以下 @model 语句:
@model IEnumerable<MvcMovie.Models.Movie>@model 指令使你能够使用强类型的 Model 对象访问控制器传递给视图的电影列表。 例如,在 Index.cshtml 视图中,
代码使用 foreach 语句通过强类型 Model 对象对电影进行循环遍历:@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。
除其他优点之外,这意味着可对代码进行编译时检查: