创建ASP.NET Core MVC应用程序(4)-添加CRUD动作方法和视图
创建ASP.NET Core MVC应用程序(4)-添加CRUD动作方法和视图
创建CRUD动作方法及视图
参照VS自带的基架(Scaffold)系统-MVC Controller with views, using Entity Framework我们来创建CRUD方法。
① 将上一篇的Models/UserContext.cs文件中的用来指定使用的数据库逻辑的OnConfiguring方法删除,将逻辑移到Startup.cs文件中的ConfigureServices方法中。
public void ConfigureServices(IServiceCollection services)
{
string connectionString = Configuration.GetConnectionString("MyConnection");
services.AddDbContext<UserContext>(options =>
options.UseMySQL(connectionString));
// Add framework services.
services.AddMvc();
}
② 在UserController.cs 构造函数中采用依赖注入来注入一个数据库上下文到该控制器。数据库上下文将被应用到控制器中的每一个CRUD方法。
private readonly UserContext _context;
public UserController(UserContext context)
{
_context = context;
}
③ 在UserController.cs中添加基本的CRUD方法:
// GET: /<controller>/
public async Task<IActionResult> Index()
{
return View(await _context.Users.ToListAsync());
}
// GET: User/Details/1
public async Task<IActionResult> Details(int? id)
{
if (id == null)
{
return NotFound();
}
var user = await _context.Users.SingleOrDefaultAsync(u => u.ID == id);
if (user == null)
{
return NotFound();
}
return View(user);
}
// GET: User/Create
public IActionResult Create()
{
return View();
}
// POST: User/Create
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("ID,Name,Email,Bio")]User user)
{
if (ModelState.IsValid)
{
_context.Add(user);
await _context.SaveChangesAsync();
return RedirectToAction("Index");
}
return View(user);
}
//GET: User/Edit/1
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var user = await _context.Users.SingleOrDefaultAsync(u => u.ID == id);
if (user == null)
{
return NotFound();
}
return View(user);
}
// POST: User/Edit/1
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("ID,Name,Email,Bio")]User user)
{
if (id != user.ID)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(user);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!UserExists(user.ID))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction("Index");
}
return View(user);
}
//// GET: User/Delete/5
public async Task<IActionResult> Delete(int? id)
{
if (id == null)
{
return NotFound();
}
var user = await _context.Users.SingleOrDefaultAsync(u => u.ID == id);
if (user == null)
{
return NotFound();
}
return View(user);
}
// POST: User/Delete/1
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteConfirmed(int id)
{
var user = await _context.Users.SingleOrDefaultAsync(u => u.ID == id);
_context.Users.Remove(user);
await _context.SaveChangesAsync();
return RedirectToAction("Index");
}
private bool UserExists(int id)
{
return _context.Users.Any(e => e.ID == id);
}
一个http://localhost:5000/User 这样的请求到达User控制器后,将会从User表返回所有的数据,将将这些数据传递到Index视图:

④ 在Views/User文件夹中添加与上述Action方法名称相对应的Index.cshtml文件、Create.cshtml文件、Details.cshtml文件、Edit.cshtml文件、Delete.cshtml文件。
Create.cshtml运行效果:

Details.cshtml运行效果:

Edit.cshtml运行效果:

Delete.cshtml运行效果:

强类型模型和@model关键字
MVC提供了传递强类型对象给视图的能力,这样为你的代码提供了更好的编译时检查,并在VS中提供了更丰富的智能感知功能。
查看UserController/Details方法:
// GET: User/Details/1
public async Task<IActionResult> Details(int? id)
{
if (id == null)
{
return NotFound();
}
var user = await _context.Users.SingleOrDefaultAsync(u => u.ID == id);
if (user == null)
{
return NotFound();
}
return View(user);
}
id参数通常作为路由数据来传递,比如 http://localhost:5000/user/details/1 会:
- Controller设置为
user(第一个URL段) - Action设置为
details(第二个URL段) - id设置为1(第三个URL段)
你也可以通过查询字符串来传递id:
http://localhost:5000/user/details?id=1
如果指定的User被找到,则User Model实例将被传递到Details视图:
return View(user);
查看Views/User/Details.cshtml文件:
@model IEnumerable<MyFirstApp.Models.User>
@{
ViewData["Title"] = "Index - User List";
}
<h2>Index - User List</h2>
<p>
<a asp-action="Create">Create New</a>
</p>
<table class="table">
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.Name)
</th>
<th>
@Html.DisplayNameFor(model => model.Email)
</th>
<th>
@Html.DisplayNameFor(model => model.Bio)
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.Name)
</td>
<td>
@Html.DisplayFor(modelItem => item.Email)
</td>
<td>
@Html.DisplayFor(modelItem => item.Bio)
</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语句,你可以指定视图所期望的对象类型。
@model MyFirstApp.Models.User
@model指令允许你通过使用强类型的Model对象来访问从控制器传递到视图的User对象。例如,在Details.cshtml视图中,通过使用强类型的Model对象传递User的每一个字段到DisplayNameFor和DisplayFor HTML Helper。
再来查看Index.cshtml文件和User控制器中的Index方法。注意在调用View方法时,是如何创建一个List对象的。下面的代码将从Index Action方法传递整个User到视图中。
User控制器中的Index方法:
public async Task<IActionResult> Index()
{
return View(await _context.Users.ToListAsync());
}
Index.cshtml文件最顶部:
@model IEnumerable<MyFirstApp.Models.User>
@model指令允许你访问通过强类型的Model从控制器传递到视图的User列表。例如,在Index.cshtml视图中,在强类型的Model对象上通过foreach语句遍历了整个User列表:
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.Name)
</td>
<td>
@Html.DisplayFor(modelItem => item.Email)
</td>
<td>
@Html.DisplayFor(modelItem => item.Bio)
</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>
}
添加仓储类
首先,新建一个Repositories文件夹。在该文件夹下定义一个IUserRepository接口。
namespace MyFirstApp.Repositories
{
public interface IUserRepository
{
Task<IEnumerable<User>> GetAll();
Task<User> Get(int id);
void Add(User user);
void Update(User user);
void Delete(int id);
bool UserExists(int id);
}
}
接着再添加一个UserRepository来实现IUserRepository接口。将之前定义的UserContext.cs逻辑移到该类中,在UserRepository.cs 构造函数中采用依赖注入来注入一个数据库上下文(UserContext)到该仓储类。数据库上下文将被应用到仓储类中的每一个CRUD方法。
public class UserRepository : IUserRepository
{
private readonly UserContext _context;
public UserRepository(UserContext context)
{
_context = context;
}
public async Task<IEnumerable<User>> GetAll()
{
return await _context.Users.ToListAsync();
}
public async Task<User> Get(int id)
{
return await _context.Users.SingleOrDefaultAsync(u => u.ID == id);
}
public async void Add(User user)
{
//_context.Users.Add(user);
_context.Add(user);
await _context.SaveChangesAsync();
}
public async void Update(User user)
{
//_context.Users.Update(user);
_context.Update(user);
await _context.SaveChangesAsync();
}
public async void Delete(int id)
{
var user = _context.Users.SingleOrDefault(u => u.ID == id);
_context.Users.Remove(user);
await _context.SaveChangesAsync();
}
public bool UserExists(int id)
{
return _context.Users.Any(e => e.ID == id);
}
}
在Controller构造函数中依赖注入UserRepository
再修改Controllers/UserController.cs文件,将private readonly的UserContext变量删除:
private readonly UserContext _context;
添加IUserRepository变量:
private readonly IUserRepository _userRepository;
public UserController(IUserRepository userRepository)
{
_userRepository = userRepository;
}
将所有方法中的_context操作删除,替换成_userRepository。例如,将Index方法中的_context.Users.ToListAsync()删除:
return View(await _context.Users.ToListAsync());
替换成
return View(await _context.Users.ToListAsync());
最终的UserController.cs如下:
public class UserController : Controller
{
private readonly IUserRepository _userRepository;
public UserController(IUserRepository userRepository)
{
_userRepository = userRepository;
}
// GET: /<controller>/
public async Task<IActionResult> Index()
{
return View(await _userRepository.GetAll());
}
// GET: User/Details/1
public async Task<IActionResult> Details(int? id)
{
if (id == null)
{
return NotFound();
}
var user = await _userRepository.Get(id.Value);
if (user == null)
{
return NotFound();
}
return View(user);
}
// GET: User/Create
public IActionResult Create()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Create([Bind("ID,Name,Email,Bio")]User user)
{
if (ModelState.IsValid)
{
_userRepository.Add(user);
return RedirectToAction("Index");
}
return View(user);
}
//GET: User/Edit/1
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var user = await _userRepository.Get(id.Value);
if (user == null)
{
return NotFound();
}
return View(user);
}
// POST: User/Edit/1
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Edit(int id, [Bind("ID,Name,Email,Bio")]User user)
{
if (id != user.ID)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_userRepository.Update(user);
}
catch (DbUpdateConcurrencyException)
{
if (!_userRepository.UserExists(user.ID))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction("Index");
}
return View(user);
}
//// GET: User/Delete/5
public async Task<IActionResult> Delete(int? id)
{
if (id == null)
{
return NotFound();
}
var user = await _userRepository.Get(id.Value);
if (user == null)
{
return NotFound();
}
return View(user);
}
// POST: User/Delete/1
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public IActionResult DeleteConfirmed(int id)
{
_userRepository.Delete(id);
return RedirectToAction("Index");
}
}
注册仓储
通过定义Repository接口,从MVC Controller中解耦该repository类。通过注入一个UserRepository来代替直接在Controller里面实例化一个UserRepository类。
为了注入一个Repository到Controller,我们必须通过DI容器来注册它,打开Startup.cs文件,在ConfigureServices方法添加如下代码:
// Add our repository type
services.AddScoped<IUserRepository, UserRepository>();
DataAnnotations & Tag Helpers
我们为Models/User.cs文件添加Display和DataType注解,首先要添加必要的命名空间using System.ComponentModel.DataAnnotations;:

再将属性在视图上显示成中文:

Display Attribute指定字段的显示名,DataTypeAttribute指定数据类型。
最终的显示效果如下:

打开Views/User/Index.cshtml,你会发现Edit,Details,Delete链接是由MVC Core Anchor Tag Helper生成的。
<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>
Tag Helpers允许服务器代码在Razor文件中参与创建和渲染HTML元素。在上述代码中,AnchorTagHelper从Controller Action动作方法和路由ID动态生成HTMLhref属性值。

查看Startup.cs中的Configure方法:
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
ASP.NET Core会将http://localhost:5000/User/Edit/4 转换成发送给User控制器的Edit方法(带有值为4的Id参数)的请求。
查看UserController.cs中的[HttpPost]版本的Edit方法:
// POST: User/Edit/1
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Edit(int id, [Bind("ID,Name,Email,Bio")]User user)
{
if (id != user.ID)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(user);
await _context.SaveChangesAsync();
//_userRepository.Update(user);
}
catch (DbUpdateConcurrencyException)
{
if (!_userRepository.UserExists(user.ID))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction("Index");
}
return View(user);
}
[Bind] Attribute是一种防止over-posting(过度提交)的方式。应该只把你需要改变的属性包含到[Bind] Attribute中。
[ValidateAntiForgeryToken] Attribute是用来防止伪造请求的,会与Views/User/Edit.cshtml视图文件生成的反伪造标记(Token)进行配对。Views/User/Edit.cshtml视图文件通过Form Tag Helper来生成反伪造标记(Token)。
<form asp-action="Edit">
Form Tag Helper生成一个隐藏的防伪标记必须和User控制器中的Eidt方法的[ValidateAntiForgeryToken]产生的防伪标记相匹配。
查看Edit.cshtml,会发现基架系统(Scaffolding System)会为User类的每一个属性生成用来呈现的<label>和<input>元素。
<form asp-action="Edit">
<div class="form-group">
<label asp-for="Email" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="Email" class="form-control" />
<span asp-validation-for="Email" class="text-danger" />
</div>
</div>
</form>
基架代码使用了多个Tag Helper方法来简化HTML标记。
- Label Tag Helper用来显示字段的名字。
- Input Tag Helper用来呈现HTML
<input>元素。 - Validation Tag Helper用来显示关联属性的验证信息。
最终在浏览器中为<form>元素所生成的HTML如下:

HTML<form>中的actionAttribute设置成POST到/User/Edit/idURL(所有<input>元素都在该<form>元素中)。当点击Save按钮时,表单数据会被发送(POST)到服务器。在</form>元素的上面显示了Form Tag Helper所生成的隐藏的XSRF反伪造标记。
处理POST请求
查看[HttpPost]版本的Edit方法:

[ValidateAntiForgeryToken]验证Form Tag Helper中的反伪造标记生成器所生成的隐藏的XSRF反伪造标记。
模型绑定(Model Binding)机制接受POST过来的表单数据并创建一个User对象并作为user参数。ModelState.IsValid方法验证从表单提交过来的数据可以用来修改一个User对象。如果数据有效,就可以进行保存。被更新的数据通过调用数据库的上下文(Database Context)的SaveChangesAsync方法来保存到数据库中。数据保存之后,代码将用户重定向到UserController类的Index方法。该页面会显示刚刚被改动后的最新的用户集合。
在表单被POST到服务器之前,客户端验证会检查所有字段上的验证规则,如果有任何验证错误,则会显示该错误信息,并且表单不会被发送到服务器。如果禁用了JS,将不会有客户端验证,但服务器会检测POST过来的数据是无效的,表单会重新显示错误信息。
参考文档
个人博客
创建ASP.NET Core MVC应用程序(4)-添加CRUD动作方法和视图的更多相关文章
- 创建ASP.NET Core MVC应用程序(6)-添加验证
创建ASP.NET Core MVC应用程序(6)-添加验证 DRY原则 DRY("Don't Repeat Yourself")是MVC的设计原则之一.ASP.NET MVC鼓励 ...
- 创建ASP.NET Core MVC应用程序(5)-添加查询功能 & 新字段
创建ASP.NET Core MVC应用程序(5)-添加查询功能 & 新字段 添加查询功能 本文将实现通过Name查询用户信息. 首先更新GetAll方法以启用查询: public async ...
- 创建ASP.NET Core MVC应用程序(1)-添加Controller和View
创建ASP.NET Core MVC应用程序(1)-添加Controller和View 参考文档:Getting started with ASP.NET Core MVC and Visual St ...
- 创建ASP.NET Core MVC应用程序(3)-基于Entity Framework Core(Code First)创建MySQL数据库表
创建ASP.NET Core MVC应用程序(3)-基于Entity Framework Core(Code First)创建MySQL数据库表 创建数据模型类(POCO类) 在Models文件夹下添 ...
- 创建ASP.NET Core MVC应用程序(2)-利用MySQL Connector NET连接到MySQL
创建ASP.NET Core MVC应用程序(2)-利用MySQL Connector NET连接到MySQL 用惯.NET的研发人员都习惯性地使用SQLServer作为数据库.然而.NET Core ...
- 跨平台应用集成(在ASP.NET Core MVC 应用程序中集成 Microsoft Graph)
作者:陈希章 发表于 2017年6月25日 谈一谈.NET 的跨平台 终于要写到这一篇了.跨平台的支持可以说是 Office 365 平台在设计伊始就考虑的目标.我在前面的文章已经提到过了,Micro ...
- 【翻译】使用Visual Studio创建Asp.Net Core MVC (一)
This tutorial will teach you the basics of building an ASP.NET Core MVC web app using Visual Studio ...
- ASP.NET Core MVC应用程序中的后台工作任务
在应用程序的内存中缓存常见数据(如查找)可以显着提高您的MVC Web应用程序性能和响应时间.当然,这些数据必须定期刷新. 当然你可以使用任何方法来更新数据,例如Redis中就提供了设定缓存对象的生命 ...
- Entity Framework Core系列之实战(ASP.NET Core MVC应用程序)
本示例演示在ASP.NET 应用程序中使用EF CORE创建数据库并对其做基本的增删改查操作.当然我们默认你的机器上已经安装了.NET CORE SDK以及合适的IDE.本例使用的是Visual St ...
随机推荐
- angularjs provider 供应商服务
今天学习了angularjs的provider的供应商服务,写了个例子(自定义供应商服务,也可使用angularjs内部提供的服务) var starterApp = angular.module(' ...
- 《寒江独钓_Windows内核安全编程》中修改类驱动分发函数
最近在阅读<寒江独钓_Windows内核安全编程>一书的过程中,发现修改类驱动分发函数这一技术点,书中只给出了具体思路和部分代码,没有完整的例子. 按照作者的思路和代码,将例子补充完整,发 ...
- python字符串的使用
之前在网上看了关于python最基础的一些教程,看着都通俗易懂,但是在写的过程中却感觉还是很生涩.关于字符串的使用还是应该多写多练!如何将“teacher_id = 123 #老师ID”转换成字典或者 ...
- JavaScript异步编程原理
众所周知,JavaScript 的执行环境是单线程的,所谓的单线程就是一次只能完成一个任务,其任务的调度方式就是排队,这就和火车站洗手间门口的等待一样,前面的那个人没有搞定,你就只能站在后面排队等着. ...
- Microsoft Azure Web Sites应用与实践【1】—— 打造你的第一个Microsoft Azure Website
Microsoft Azure Web Sites应用与实践 系列: [1]—— 打造你的第一个Microsoft Azure Website [2]—— 通过本地IIS 远程管理Microsoft ...
- ASP.NET MVC 5 - 添加一个模型
在本节中,您将添加一些类,这些类用于管理数据库中的电影.这些类是ASP.NET MVC 应用程序中的"模型(Model)". 您将使用.NET Framework 数据访问技术En ...
- 有关bootstrap之排版
1.标题 HTML中的所有标题标签,从<h1> 到 <h6> 均可用.另外,还提供了.h1 到.h6 class,为的是给inline属性的文本赋予标题的样式. 在标题内还可以 ...
- fir.im Log Guru 正式开源,快速找到 iOS 应用无法安装的原因
很开心的宣布 Log Guru 正式开源! Log Guru,是 fir.im 开发团队创造的小轮子,用在 Mac 电脑上的日志获取,Github 地址:FIRHQ/LogGuru. Log Guru ...
- 推荐两款简单好用的图片放大jquery插件
一.zoomfiy.js 推荐可以从这里下载 使用说明: 使用该jquery 插件引入该插件的js:zoomfiy.js 或 min引入该插件的css:zoomfiy.css 或 min前后顺序都可j ...
- 简易版的TimSort排序算法
欢迎探讨,如有错误敬请指正 如需转载,请注明出处http://www.cnblogs.com/nullzx/ 1. 简易版本TimSort排序算法原理与实现 TimSort排序算法是Python和Ja ...