参照VS自带的基架(Scaffold)系统-MVC Controller with views, using Entity Framework咱们来建立CRUD方法。html
① 将上一篇的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
视图:mvc
④ 在Views/User文件夹中添加与上述Action方法名称相对应的Index.cshtml文件、Create.cshtml文件、Details.cshtml文件、Edit.cshtml文件、Delete.cshtml文件。app
Create.cshtml运行效果:asp.net
Details.cshtml运行效果:async
Edit.cshtml运行效果:函数
Delete.cshtml运行效果:post
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
会:
user
(第一个URL段)details
(第二个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); } }
再修改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>();
咱们为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标记。
<input>
元素。最终在浏览器中为<form>
元素所生成的HTML以下:
HTML<form>
中的action
Attribute设置成POST到/User/Edit/id
URL(全部<input>
元素都在该<form>
元素中)。当点击Save
按钮时,表单数据会被发送(POST)到服务器。在</form>
元素的上面显示了Form Tag Helper所生成的隐藏的XSRF反伪造标记。
查看[HttpPost]
版本的Edit方法:
[ValidateAntiForgeryToken]
验证Form Tag Helper中的反伪造标记生成器所生成的隐藏的XSRF反伪造标记。
模型绑定(Model Binding)机制接受POST过来的表单数据并建立一个User
对象并做为user
参数。ModelState.IsValid
方法验证从表单提交过来的数据能够用来修改一个User
对象。若是数据有效,就能够进行保存。被更新的数据经过调用数据库的上下文(Database Context)的SaveChangesAsync
方法来保存到数据库中。数据保存以后,代码将用户重定向到UserController
类的Index
方法。该页面会显示刚刚被改动后的最新的用户集合。
在表单被POST到服务器以前,客户端验证会检查全部字段上的验证规则,若是有任何验证错误,则会显示该错误信息,而且表单不会被发送到服务器。若是禁用了JS,将不会有客户端验证,但服务器会检测POST过来的数据是无效的,表单会从新显示错误信息。