小例子

学这个应该就不是初学者了,所以就没有太多注解和说明了
这次的例子是和基本的全栈教程类似,但是这个例子不使用数据库

  • 主页,查询所有数据
  • 添加页面,添加数据

使用 MVC 相关技术

  1. Controller
  2. Tag Helper
  3. Settings
  4. View Component
  5. 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>

效果图

.net core入门到精通 .net core 3.0教程_.net core入门到精通

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>

效果图

.net core入门到精通 .net core 3.0教程_Core_02

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>

效果图

.net core入门到精通 .net core 3.0教程_.net core入门到精通_03

添加后

.net core入门到精通 .net core 3.0教程_Core_04

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>

效果图

.net core入门到精通 .net core 3.0教程_css_05

添加后

.net core入门到精通 .net core 3.0教程_css_06

配置信息

ASP.NET Core 的配置信息

  1. 配置信息以 Key-Value 键值对的形式存在
  2. 配置信息可以从内存里、JSON、XML、INI等文件读取,或者系统环境变量
  3. 配置信息与配置系统是解耦的
  4. 具体使用,可以依赖注入

ASP.NET Core 的配置信息源

按顺序读取配置信息,相同的键将覆盖

  1. appsettings.json
    appsettings.{Environment}.json
    appsettings.Development.json
  2. Secret Manager
  3. 环境变量
  4. 命令行参数

如何使用?

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>

效果图

.net core入门到精通 .net core 3.0教程_.net core入门到精通_07

补充

若不想使用 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>();
    });

ASP.NET Core 3.x 入门(二)建立 Controller ,使用 Tag Helper 结束