vscode 開發MVC項目記録
需要:
dotnet new mvc -o MvcMovie 新建MVC項目
code -r MvcMovie 加載csproj項目
dotnet dev-certs https --trust 信任證書
为了在开发环境中使用HTTPS,必须要生成加密证书。.net core提供了一个全局工具dotnet-dev-certs,使用该工具可在本地环境中创建自签名的证书。可以通过下面命令行安装该工具:
dotnet tool install -global dotnet-dev-certs
安装成功后,就可以用他自签名的证书。
-ep标志表示将生成证书导出的存储目录;
-p标志表示生成证书的密码。
可以使用-trust选项来信任生成的证书。
- 启动 Kestrel
- 启动浏览器。
- 导航到
https://localhost:5001
。
Kestrel 是一个跨平台的适用于 Kestrel。 Kestrel 是包含在 ASP.NET Core 项目模板中的 Web 服务器,默认处于启用状态。
Kestrel 支持以下方案:
添加 NuGet 包
运行以下 .NET CLI 命令:
dotnet tool uninstall --global dotnet-aspnet-codegenerator
dotnet tool install --global dotnet-aspnet-codegenerator
dotnet tool uninstall --global dotnet-ef
dotnet tool install --global dotnet-ef
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet add package Microsoft.EntityFrameworkCore.SQLite
dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
上述命令添加:
- EF Core 命令行接口 (CLI) 工具
- aspnet-codegenerator 基架工具。
- EF Core 的设计时工具
- EF Core SQLite 提供程序将 EF Core 包作为依赖项进行安装。
- 基架需要的包:
Microsoft.VisualStudio.Web.CodeGeneration.Design
和Microsoft.EntityFrameworkCore.SqlServer
。
使用基架工具为电影模型生成 Create
、Read
、Update
和 Delete
(CRUD) 页面。
在项目目录中打开命令窗口。 项目目录是包含 Program.cs
和 .csproj
文件的目录。
在 macOS 和 Linux 上,导出基架工具路径:
export PATH=$HOME/.dotnet/tools:$PATH
运行下面的命令:
dotnet-aspnet-codegenerator controller -name MoviesController -m Movie -dc MvcMovieContext --relativeFolderPath Controllers --useDefaultLayout --referenceScriptLibraries -sqlite
下表详细说明了 ASP.NET Core 代码生成器参数:
参数 | 说明 |
---|---|
-M | 模型的名称。 |
-dc | 数据上下文。 |
--relativeFolderPath | 用于创建文件的相对输出文件夹路径。 |
--useDefaultLayout|-udl | 应为视图使用默认布局。 |
--referenceScriptLibraries | 向“编辑”和“创建”页面添加 _ValidationScriptsPartial 。 |
-sqlite | 标记来指定 DbContext 是否应使用 SQLite,而不是 SQL Server。 |
使用 h
开关获取 aspnet-codegenerator controller
命令方面的帮助:
dotnet aspnet-codegenerator controller -h
将 SQLite 用于开发,将 SQL Server 用于生产
Program.cs
中突出显示的以下代码展示了如何在开发中使用 SQLite,以及在生产中使用 SQL Server。
var builder = WebApplication.CreateBuilder(args);
if (builder.Environment.IsDevelopment())
{
builder.Services.AddDbContext<MvcMovieContext>(options =>
options.UseSqlite(builder.Configuration.GetConnectionString("MvcMovieContext")));
}
else
{
builder.Services.AddDbContext<MvcMovieContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("ProductionMvcMovieContext")));
}
基架更新以下内容:
- 在
Program.cs
文件中注册数据库上下文 - 将数据库连接字符串添加到
appsettings.json
文件。
基架创建以下内容:
- 电影控制器:
Controllers/MoviesController.cs
- “创建”、“删除”、“详细信息”、“编辑”和“索引”页面的 Razor 视图文件:
Views/Movies/*.cshtml
- 数据库上下文类:
Data/MvcMovieContext.cs
自动创建这些文件和文件更新被称为“基架”。
尚且不能使用基架页面,因为该数据库不存在。 运行应用并选择“Movie App”链接会导致“无法打开数据库”或“无此类表: Movie”错误消息。
构建应用程序。 编译器会生成几个有关如何处理 null
值的警告。
若要消除可为空引用类型的警告,请从 MvcMovie.csproj
文件中删除以下行:
<Nullable>enable</Nullable>
使用 EF Core 迁移功能来创建数据库。 迁移是可用于创建和更新数据库以匹配数据模型的一组工具。
如果尚未安装 dotnet ef
,请安装它作为全局工具:
dotnet tool install --global dotnet-ef
有关 EF Core 的 CLI 的详细信息,请参阅 .Net CLI 的 EF Core 工具引用。
运行以下 .NET CLI 命令:
dotnet ef migrations add InitialCreate
dotnet ef database update
ef migrations add InitialCreate
:生成Migrations/{timestamp}_InitialCreate.cs
迁移文件。InitialCreate
参数是迁移名称。 可以使用任何名称,但是按照惯例,会选择可说明迁移的名称。 因为这是首次迁移,所以生成的类包含用于创建数据库架构的代码。 数据库架构基于在MvcMovieContext
类(位于Data/MvcMovieContext.cs
文件中)中指定的模型。ef database update
:将数据库更新到上一个命令创建的最新迁移。 此命令在用于创建数据库的Migrations/{time-stamp}_InitialCreate.cs
文件中运行Up
方法。
检查生成的数据库上下文类和注册
对于 EF Core,使用模型执行数据访问。 模型由实体类和表示数据库会话的上下文对象构成。 上下文对象允许查询并保存数据。 数据库上下文派生自 Microsoft.EntityFrameworkCore.DbContext 并指定要包含在数据模型中的实体。
基架创建 Data/MvcMovieContext.cs
数据库上下文类:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using MvcMovie.Models;
namespace MvcMovie.Data
{
public class MvcMovieContext : DbContext
{
public MvcMovieContext (DbContextOptions<MvcMovieContext> options)
: base(options)
{
}
public DbSet<MvcMovie.Models.Movie> Movie { get; set; }
}
}
前面的代码创建一个 DbSet<Movie> 属性,该属性表示数据库中的电影。
依赖项注入
ASP.NET Core 通过依赖关系注入 (DI) 生成。 服务(如数据库上下文)在 Program.cs
中向 DI 注册。 这些服务通过构造函数参数提供给需要它们的组件。
在 Controllers/MoviesController.cs
文件中,构造函数使用依赖关系注入将 MvcMovieContext
数据库上下文注入到控制器中。 数据库上下文将在控制器中的每个 CRUD 方法中使用。
基架在 Program.cs
中生成了以下突出显示的代码:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<MvcMovieContext>(options =>
options.UseSqlite(builder.Configuration.GetConnectionString("MvcMovieContext")));
ASP.NET Core 配置系统读取“MvcMovieContext”数据库连接字符串。
检查生成的数据库连接字符串
基架向 appsettings.json
文件添加了一个连接字符串:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"MvcMovieContext": "Data Source=MvcMovie.db"
}
}}
进行本地开发时,ASP.NET Core 配置系统在 appsettings.json
文件中读取 ConnectionString
键。
InitialCreate
类
检查 Migrations/{timestamp}_InitialCreate.cs
迁移文件:
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace MvcMovie.Migrations
{
public partial class InitialCreate : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Movie",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Title = table.Column<string>(type: "nvarchar(max)", nullable: true),
ReleaseDate = table.Column<DateTime>(type: "datetime2", nullable: false),
Genre = table.Column<string>(type: "nvarchar(max)", nullable: true),
Price = table.Column<decimal>(type: "decimal(18,2)", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Movie", x => x.Id);
});
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Movie");
}
}
}
在上述代码中:
InitialCreate.Up
创建 Movie 表,并将Id
配置为主键。InitialCreate.Down
还原Up
迁移所做的架构更改。
控制器中的依赖项注入
打开 Controllers/MoviesController.cs
文件并检查构造函数:
public class MoviesController : Controller
{
private readonly MvcMovieContext _context;
public MoviesController(MvcMovieContext context)
{
_context = context;
}
构造函数使用依赖关系注入将数据库上下文 (MvcMovieContext
) 注入到控制器中。 数据库上下文将在控制器中的每个 CRUD 方法中使用。
测试“创建”页。 输入并提交数据。
测试“编辑”、“详细信息”和“删除”页 。
强类型模型和 @model
指令
在本教程之前的内容中,已经介绍了控制器如何使用 ViewData
字典将数据或对象传递给视图。 ViewData
字典是一个动态对象,提供了将信息传递给视图的方便的后期绑定方法。
MVC 提供将强类型模型对象传递给视图的功能。 此强类型方法启用编译时代码检查。 基架机制在 MoviesController
类和视图中传递了一个强类型模型。
检查 Controllers/MoviesController.cs
文件中生成的 Details
方法:
// 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
参数通常作为路由数据传递。 例如,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
语句可指定视图期望的对象类型。 创建影片控制器时,将包含以下 @model
语句:
@model MvcMovie.Models.Movie
此 @model
指令允许访问控制器传递给视图的影片。 Model
对象为强类型对象。 例如,在 Details.cshtml
视图中,代码通过强类型的 Model
对象将每个电影字段传递给 DisplayNameFor
和 DisplayFor
HTML 帮助程序。 Create
和 Edit
方法以及视图也传递一个 Movie
模型对象。
检查电影控制器中的 Index.cshtml
视图和 Index
方法。 请注意代码在调用 View
方法时是如何创建 List
对象的。 代码将此 Movies
列表从 Index
操作方法传递给视图:
// GET: Movies
public async Task<IActionResult> Index()
{
return View(await _context.Movie.ToListAsync());
}
创建电影控制器后,基架将以下 @model
语句包含在 Index.cshtml
文件的顶部:
@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
。 编译器还有一些其他优势,比如验证代码中使用的类型。