创建ASP.NET Core MVC应用程序(4)-添加CRUD动作方法和视图
来源:互联网 发布:百度云淘宝暗语 编辑:程序博客网 时间:2024/06/16 20:19
创建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/1public 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/Createpublic 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/1public 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/5public 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/1public 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 typeservices.AddScoped<IUserRepository, UserRepository>();
DataAnnotations & Tag Helpers
我们为Models/User.cs文件添加Display
和DataType
注解,首先要添加必要的命名空间using System.ComponentModel.DataAnnotations;
:
再将属性在视图上显示成中文:
Display
Attribute指定字段的显示名,DataType
Attribute指定数据类型。
最终的显示效果如下:
打开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>
中的action
Attribute设置成POST到/User/Edit/id
URL(所有<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过来的数据是无效的,表单会重新显示错误信息。
参考文档
- Adding a model
- over-posting
个人博客
我的个人博客
- 创建ASP.NET Core MVC应用程序(4)-添加CRUD动作方法和视图
- 创建ASP.NET Core MVC应用程序(6)-添加验证
- 使用ASP.NET Core MVC 和 Entity Framework Core 开发一个CRUD(增删改查)的应用程序
- 创建ASP.NET Core MVC应用程序(5)-添加查询功能 & 新字段
- ASP.NET Core MVC 和 Visual Studio入门(三) 添加视图
- Asp.net MVC 4 动作方法结果
- asp.net core mvc剖析:mvc动作选择
- asp.net core mvc剖析:mvc动作选择
- asp.net core mvc剖析:mvc动作选择
- asp.net core mvc剖析:mvc动作选择
- asp.net core mvc剖析:mvc动作选择
- asp.net core mvc剖析:mvc动作选择
- asp.net core mvc剖析:mvc动作选择
- Pro ASP.NET Core MVC(二)【第一个MVC 应用程序】
- Asp.Net MVC(控制器、控制器动作和动作结果
- 为ASP.NET MVC应用程序创建单元测试
- 为ASP.NET MVC应用程序创建单元测试
- ASP.NET Core MVC 和 Visual Studio入门(四)添加模型
- 基于粒子滤波的物体跟踪
- mysql 中文乱码奇怪问题
- Windows 7安装 dotnet core SDK 1.1笔记
- java中instanceof用法
- C# Func Action 无重载代理
- 创建ASP.NET Core MVC应用程序(4)-添加CRUD动作方法和视图
- 第十二章 对象(下)
- 开机初始接入
- redis.conf-中文翻译
- spring集成hibernate applicationContext.xml文件中SessionFactory配置
- opencv2-摄像头获取图像相关参数设置
- SAP是啥啊?
- php常用正则表达式收集,正则讲解
- 微信公众号开发中的用户账号绑定