ASP.NET Core 3.x 入门(二)建立 Controller ,使用 Tag Helper
此入门教程是记录下方参考资料视频的学习过程
开发工具:Visual Studio 2019
参考资料:https://www.bilibili.com/video/BV1c441167KQ
API文档:https://docs.microsoft.com/zh-cn/dotnet/api/?view=aspnetcore-3.1
目录
小例子
学这个应该就不是初学者了,所以就没有太多注解和说明了
这次的例子是和基本的全栈教程类似,但是这个例子不使用数据库
- 主页,查询所有数据
- 添加页面,添加数据
使用 MVC 相关技术
- Controller
- Tag Helper
- Settings
- View Component
- Razor Page
Models
项目下新建 Models 文件夹,在该目录中添加 Model
部门:Department.cs
员工:Employee.cs
一个部门有多个员工,是一对多的关系
public class Department
{
public int Id { get; set; }
public string Name { get; set; }
public string Location { get; set; }
public int EmployeeCount { get; set; }
}
public class Employee
{
public int Id { get; set; }
//相当于外键
public int DepartmentId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public Gender Gender { get; set; }
//是否被解雇
public bool Fired { get; set; }
}
public enum Gender
{
女 = 0,
男 = 1
}
公司情况:CompanySummary,统计数量
public class CompanySummary
{
public int EmployeeCount { get; set; }
public int AverageDepartmentEmployeeCount { get; set; }
}
Service
Service 在 Services 文件夹下建立
IDepartmentService.cs
public interface IDepartmentService
{
//获取所有部门
Task<IEnumerable<Department>> GetAll();
//根据Id获取部门
Task<Department> GetById(int id);
//获取公司情况
Task<CompanySummary> GetCompanySummary();
//添加部门
Task Add(Department department);
}
DepartmentService.cs
public class DepartmentService : IDepartmentService
{
private readonly List<Department> _departments = new List<Department>();
public DepartmentService()
{
this._departments.Add(new Department
{
Id = 1,
Name = "HR",
EmployeeCount = 16,
Location = "Beijing"
});
this._departments.Add(new Department
{
Id = 2,
Name = "R&D",
EmployeeCount = 52,
Location = "Shanghai"
});
this._departments.Add(new Department
{
Id = 3,
Name = "Sales",
EmployeeCount = 200,
Location = "China"
});
}
public Task<IEnumerable<Department>> GetAll()
{
return Task.Run(() => this._departments.AsEnumerable());
}
public Task<Department> GetById(int id)
{
return Task.Run(() => this._departments.FirstOrDefault(x => x.Id == id));
}
public Task<CompanySummary> GetCompanySummary()
{
return Task.Run(() =>
{
return new CompanySummary
{
EmployeeCount = this._departments.Sum(x => x.EmployeeCount),
AverageDepartmentEmployeeCount = (int)this._departments.Average(x => x.EmployeeCount)
};
});
}
public Task Add(Department department)
{
department.Id = this._departments.Max(x => x.Id) + 1;
this._departments.Add(department);
return Task.CompletedTask;
}
}
IEmployeeService.cs
public interface IEmployeeService
{
//添加员工
Task Add(Employee employee);
//根据部门Id获取该部门所有员工集合
Task<IEnumerable<Employee>> GetByDepartmentId(int departmentId);
//解雇员工
Task<Employee> Fire(int id);
}
EmployeeService.cs
public class EmployeeService : IEmployeeService
{
private readonly List<Employee> _employees = new List<Employee>();
public EmployeeService()
{
this._employees.Add(new Employee
{
Id = 1,
DepartmentId = 1,
FirstName = "Nick",
LastName = "Carter",
Gender = Gender.男
});
this._employees.Add(new Employee
{
Id = 2,
DepartmentId = 1,
FirstName = "Michael",
LastName = "Jackson",
Gender = Gender.男
});
this._employees.Add(new Employee
{
Id = 3,
DepartmentId = 1,
FirstName = "Mariah",
LastName = "Carey",
Gender = Gender.女
});
this._employees.Add(new Employee
{
Id = 4,
DepartmentId = 2,
FirstName = "Axl",
LastName = "Rose",
Gender = Gender.男
});
this._employees.Add(new Employee
{
Id = 5,
DepartmentId = 2,
FirstName = "Kate",
LastName = "Winslet",
Gender = Gender.女
});
this._employees.Add(new Employee
{
Id = 6,
DepartmentId = 3,
FirstName = "Rob",
LastName = "Thomas",
Gender = Gender.男
});
this._employees.Add(new Employee
{
Id = 7,
DepartmentId = 3,
FirstName = "Avril",
LastName = "Lavigne",
Gender = Gender.女
});
this._employees.Add(new Employee
{
Id = 8,
DepartmentId = 3,
FirstName = "Katy",
LastName = "Perry",
Gender = Gender.女
});
this._employees.Add(new Employee
{
Id = 9,
DepartmentId = 3,
FirstName = "Michelle",
LastName = "Monaghan",
Gender = Gender.女
});
}
public Task Add(Employee employee)
{
employee.Id = this._employees.Max(x => x.Id) + 1;
this._employees.Add(employee);
return Task.CompletedTask;
}
public Task<IEnumerable<Employee>> GetByDepartmentId(int departmentId)
{
return Task.Run(() => this._employees.Where(x => x.DepartmentId == departmentId));
}
public Task<Employee> Fire(int id)
{
return Task.Run(() =>
{
var employee = this._employees.FirstOrDefault(e => e.Id == id);
if (employee != null)
{
employee.Fired = true;
return employee;
}
return null;
});
}
}
注册服务
进入 Startup.cs ,ConfigureServices()
中添加
services.AddSingleton<IDepartmentService, DepartmentService>();
services.AddSingleton<IEmployeeService, EmployeeService>();
Controller
Controller 在 Controllers 文件夹下建立
DepartmentController.cs
public class DepartmentController : Controller
{
private readonly IDepartmentService _departmentService;
public DepartmentController(IDepartmentService departmentService)
{
this._departmentService = departmentService;
}
public async Task<IActionResult> Index()
{
ViewBag.Title = "Department Index";
var department = this._departmentService.GetAll();
return View(department);
}
//返回添加部门页面和空的Model,通过表单设置Model
[HttpGet]//默认就是Get,可以不写
public IActionResult Add()
{
ViewBag.Title = "Add Department";
return View(new Department());
}
//添加部门页面提交
[HttpPost]
public async Task<IActionResult> Add(Department model)
{
if(ModelState.IsValid)
{
await this._departmentService.Add(model);
}
//使用nameof利于重命名
return RedirectToAction(nameof(Index));
}
}
EmployeeController.cs
public class EmployeeController : Controller
{
private readonly IDepartmentService _departmentService;
private readonly IEmployeeService _employeeService;
public EmployeeController(IDepartmentService departmentService, IEmployeeService employeeService)
{
this._departmentService = departmentService;
this._employeeService = employeeService;
}
public async Task<IActionResult> Index(int departmentId)
{
var department = await this._departmentService.GetById(departmentId);
ViewBag.Title = $"Employees of {department.Name}";
ViewBag.DepartmentId = departmentId;
var employees = await this._employeeService.GetByDepartmentId(departmentId);
return View(employees);
}
public IActionResult Add(int departmentId)
{
ViewBag.Title = "Add Employee";
return View(new Employee
{
DepartmentId = departmentId
});
}
[HttpPost]
public async Task<IActionResult> Add(Employee model)
{
if (ModelState.IsValid)
{
await this._employeeService.Add(model);
}
return RedirectToAction(nameof(Index), new { departmentId = model.DepartmentId });
}
public async Task<IActionResult> Fire(int employeeId)
{
var employee = await this._employeeService.Fire(employeeId);
return RedirectToAction(nameof(Index), new { departmentId = employee.DepartmentId });
}
}
View
项目下新建一个 Views 文件夹,视图都放在这个路径
新建视图模板,选择 Razor Layout ,模板都放在 Views/Shared 目录下
_Layout.cshtml
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>@ViewBag.Title</title>
@* 开发环境加载完整的css *@
<environment include="Development">
<link rel="stylesheet" asp-href-include="css/*" asp-href-exclude="css/all.min.css" />
</environment>
@* 其它环境 *@
<environment exclude="Development">
<link rel="stylesheet" asp-href-include="css/all.min.css" />
</environment>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-2">
@* asp-append-version="true",不缓存图片 *@
<img asp-append-version="true" alt="Logo" src="~/images/logo.png" style="height60px;" />
</div>
<div class="col-md-10">
<span class="h2">@ViewBag.Title</span>
</div>
</div>
<div class="row">
<div class="col-md-12">
@RenderBody()
</div>
</div>
</div>
</body>
</html>
让 _Layout 页面作为其它所有页面默认的母页面
在 Views 目录下新建 Razor View Start
_ViewStart.cshtml
@{
Layout = "_Layout";
}
asp-append-version="true"
就是一个 Tag Helper
启用 Tag Helper
在 Views 文件夹下新建 Razor View Imports
_ViewImports.cshtml,这里写的代码会添加到所有 View 里
@addTagHelper "*,Microsoft.AspNetCore.Mvc.TagHelpers"
如果不想添加全局 Tag Helper 可以在对应的 View 里写
也可以创建自己的 Tag Helper ,创建一个类,继承 TagHelpers
具体视图
在 Views 目录下新建与控制器名一致的文件夹
在对应的文件夹下新建 Razor View ,视图名称对应控制器中的方法名称
建立列表和表单 View
Views/Department/Index.cshtml
@using My_ASP_NET_Core_Program.Models
@model IEnumerable<Department>
<div class="row">
<div class="col-md-10 offset-md-2">
<table class="table">
<tr>
<th>Name</th>
<th>Location</th>
<th>Employee</th>
<th>操作</th>
</tr>
@Html.DisplayForModel()
</table>
</div>
</div>
<div class="row">
<div class="col-md-4 offset-md-2">
<a asp-action="Add">Add</a>
</div>
</div>
@Html.DisplayForModel()
是一个模板,在 Views/控制器 目录下新建 DisplayTemplates 目录
再新建 Razor View Department.cshtml
@model My_ASP_NET_Core_Program.Models.Department
<tr>
<td>@Model.Name</td>
<td>@Model.Location</td>
<td>@Model.EmployeeCount</td>
<td>
<a asp-controller="Employee" asp-action="Index" asp-route-departmentId="@Model.Id">
Employees
</a>
</td>
</tr>
效果图
Views/Employee/Index.cshtml
@using My_ASP_NET_Core_Program.Models
@model IEnumerable<Department>
<div class="row">
<div class="col-md-10 offset-md-2">
<table class="table">
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Gender</th>
<th>Is Fired</th>
<th>操作</th>
</tr>
@Html.DisplayForModel()
</table>
</div>
</div>
<div class="row">
<div class="col-md-4 offset-md-2">
<a asp-action="Add">Add</a>
</div>
</div>
Views/DisplayTemplates/Employee.cshtml
@model My_ASP_NET_Core_Program.Models.Employee
<tr>
<td>@Model.FirstName</td>
<td>@Model.LastName</td>
<td>@Model.Gender</td>
<td>@(Model.Fired?"是":"")</td>
<td>
@if (!Model.Fired)
{
//EmpolyeeController 的 Fire(int employeeId) 方法
<a asp-action="Fire" asp-route-employeeId="@Model.Id">
Fire
</a>
}
</td>
</tr>
效果图
Add 视图
Views/Department/Add.cshtml
@using My_ASP_NET_Core_Program.Models
@model Department
<form asp-action="Add">
<div class="row form-group">
<div class="col-md-2 offset-md-2">
@* asp-for 的作用是将 Model 中使用 [Display(Name = "XX)] 的属性,在 label 中显示为 XX *@
<label asp-for="Name"></label>
</div>
<div class="col-md-6">
<input class="form-control" asp-for="Name" />
</div>
</div>
<div class="row form-group">
<div class="col-md-2 offset-md-2">
<label asp-for="Location"></label>
</div>
<div class="col-md-6">
<input class="form-control" asp-for="Location" />
</div>
</div>
<div class="row form-group">
<div class="col-md-2 offset-md-2">
<label asp-for="EmployeeCount"></label>
</div>
<div class="col-md-6">
<input class="form-control" asp-for="EmployeeCount" />
</div>
</div>
<div class="row">
<div class="col-md-2 offset-1-md-2">
<button type="submit" class="btn btn-primary">Add</button>
</div>
</div>
</form>
效果图
添加后
Views/Employees/Index.cshtml
@using My_ASP_NET_Core_Program.Models
@model IEnumerable<Employee>
<div class="row">
<div class="col-md-10 offset-md-2">
<table class="table">
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Gender</th>
<th>Is Fired</th>
<th>操作</th>
</tr>
@Html.DisplayForModel()
</table>
</div>
</div>
<div class="row">
<div class="col-md-4 offset-md-2">
@* 这里的 DepartmentId 是 EmployeeController 的 Index() 传入的 *@
<a asp-action="Add" asp-route-departmentId="@ViewBag.DepartmentId">Add</a>
</div>
</div>
Views/Employees/Add.cshtml
@using My_ASP_NET_Core_Program.Models
@model Employee
<form asp-action="Add">
@* 这里的 DepartmentId 在 Get 的 Add(int departmentId) 时就已经赋值,但是 Post 的 Add(Employee model) 还需要 DepartmentId *@
<input type="hidden" asp-for="DepartmentId" />
<div class="row form-group">
<div class="col-md-2 offset-md-2">
<label asp-for="FirstName"></label>
</div>
<div class="col-md-6">
<input class="form-control" asp-for="FirstName" />
</div>
</div>
<div class="row form-group">
<div class="col-md-2 offset-md-2">
<label asp-for="LastName"></label>
</div>
<div class="col-md-6">
<input class="form-control" asp-for="LastName" />
</div>
</div>
<div class="row form-group">
<div class="col-md-2 offset-md-2">
<label asp-for="Gender"></label>
</div>
@* 这里应该是一个下拉框 *@
@*<div class="col-md-6">
<input class="form-control" asp-for="Gender" />
</div>*@
<div class="col-md-6">
<select class="form-control" asp-for="Gender" asp-items="Html.GetEnumSelectList<Gender>()">
</select>
</div>
</div>
<div class="row">
<div class="col-md-2 offset-1-md-2">
<button type="submit" class="btn btn-primary">Add</button>
</div>
</div>
</form>
效果图
添加后
配置信息
ASP.NET Core 的配置信息
- 配置信息以 Key-Value 键值对的形式存在
- 配置信息可以从内存里、JSON、XML、INI等文件读取,或者系统环境变量
- 配置信息与配置系统是解耦的
- 具体使用,可以依赖注入
ASP.NET Core 的配置信息源
按顺序读取配置信息,相同的键将覆盖
- appsettings.json
appsettings.{Environment}.json
appsettings.Development.json - Secret Manager
- 环境变量
- 命令行参数
如何使用?
appsettings.json 添加一个对象
"My_ASP_NET_Core_Program": {
//部门人数大于 30 就加粗
"BoldDepartmentEmployeeCountThreshold": 30
}
Startup.cs 添加以下代码
private readonly IConfiguration _configuration;
public Startup(IConfiguration configuration)
{
this._configuration = configuration;
//读取的数据类型的是字符串
//字符串无法表达JSON对象,所以要使用冒号
var myValue = this._configuration["My_ASP_NET_Core_Program:BoldDepartmentEmployeeCountThreshold"];
}
因为 C# 是强类型语言,所以尽量使用强类型
所以我们创建一个类,属性名要与 JSON 配置对应
项目目录下新建一个类
public class My_ASP_NET_Core_ProgramOptions
{
public int BoldDepartmentEmployeeCountThreshold { get; set; }
}
注入
public void ConfigureServices(IServiceCollection services)
{
services.Configure<My_ASP_NET_Core_ProgramOptions>(this._configuration.GetSection("My_ASP_NET_Core_Program"));
}
若想在控制器中注入
private readonly IOptions<My_ASP_NET_Core_ProgramOptions> _myOptions;
public HomeController(IOptions<My_ASP_NET_Core_ProgramOptions> myOptions)
{
this._myOptions = myOptions;
}
若想在 Razor View 中使用
@using Microsoft.Extensions.Options
@using My_ASP_NET_Core_Program
@inject IOptions<My_ASP_NET_Core_ProgramOptions> options
@* inject 是依赖注入 *@
例子
Views/Department/DisplayTemplates/Department.cshtml
@using Microsoft.Extensions.Options
@using My_ASP_NET_Core_Program
@model My_ASP_NET_Core_Program.Models.Department
@inject IOptions<My_ASP_NET_Core_ProgramOptions> options
<tr>
@* 如果部门人数大于 30 就加粗 *@
@if (Model.EmployeeCount > options.Value.BoldDepartmentEmployeeCountThreshold)
{
<td><strong>@Model.Name</strong></td>
}
else
{
<td>@Model.Name</td>
}
<td>@Model.Location</td>
<td>@Model.EmployeeCount</td>
<td>
@* EmployeeController 的 Index(int departmentId) 方法*@
@* asp-route- 指定方法参数 *@
<a asp-controller="Employee" asp-action="Index" asp-route-departmentId="@Model.Id">
Employees
</a>
</td>
</tr>
效果图
补充
若不想使用 appsetting.json 作为配置源
需要在 Program.cs 中配置
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((context, configuBuilder) =>
{
//不想使用 appsettings.json ,需要先清理掉所有源或者指定源
configuBuilder.Sources.Clear();
//使用另外一个 JSON 文件
configuBuilder.AddJsonFile("myJSON.json");
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});