參照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過來的數據是無效的,表單會從新顯示錯誤信息。