前言:
本系列文章主要为我之前所学知识的一次微小的实践,以我学校图书馆管理系统为雏形所作。
本系列文章主要参考资料:
微软文档:https://docs.microsoft.com/zh-cn/aspnet/core/getting-started/?view=aspnetcore-2.1&tabs=windows
《Pro ASP.NET MVC 5》、《锋利的 jQuery》
此系列皆使用 VS2017+C# 作为开发环境。如果有什么问题或者意见欢迎在留言区进行留言。
项目 github 地址:https://github.com/NanaseRuri/LibraryDemo
本章内容:通过模态窗口确认是否提交表单、select 元素、表单提交数组、checkbox、关闭窗口前确认、EF 修改主键
一、外借/阅览图书信息首页
这里的实现和上一章大致一致,但这里我打算通过前面的 BookDetail 页面进入到这个页面:
首先创建一个视图模型同时保存 BookDetail 的信息并传递 IEnumerable<Book> 以便对对应 BookDetails 的外借/阅览图书信息进行展示:
1 public class BookEditModel 2 { 3 public BookDetails BookDetails { get; set; } 4 public IEnumerable<Book> Books { get; set; } 5 }
对应 BookDetail 的外借/阅览图书信息首页:
1 [Authorize(Roles = "Admin")] 2 public IActionResult Books(string isbn) 3 { 4 BookEditModel model = new BookEditModel() 5 { 6 Books = _context.Books.Where(b => b.ISBN == isbn), 7 BookDetails = _context.BooksDetail.FirstOrDefault(b => b.ISBN == isbn) 8 }; 9 if (model.BookDetails==null) 10 { 11 TempData["message"] = "未找到目标书籍"; 12 return RedirectToAction("BookDetails"); 13 } 14 return View(model); 15 }
视图:
1 @using LibraryDemo.Models.DomainModels 2 @model BookEditModel 3 4 @{ 5 ViewData["Title"] = "外借/阅览书籍信息"; 6 Book temp = new Book(); 7 } 8 9 <script> 10 function confirmDelete() { 11 var barcodes = document.getElementsByName("barcodes"); 12 var message="确认删除"; 13 for (i in barcodes) { 14 if (barcodes[i].checked) { 15 var book = barcodes[i].parentElement.nextElementSibling.firstElementChild.innerHTML; 16 message=message+"《"+book+"》"; 17 } 18 } 19 message = message + "?"; 20 if (confirm(message) == true) { 21 return true; 22 } else { 23 return false; 24 } 25 } 26 </script> 27 28 <style type="text/css"> 29 tr + tr { 30 border-top: thin solid gray; 31 } 32 33 td + td { 34 padding-left: 100px; 35 } 36 37 .container { 38 width: 1200px; 39 } 40 </style> 41 42 <h2>《@Model.BookDetails.Name》</h2> 43 <br /> 44 45 @if (TempData["message"] != null) 46 { 47 <p style="font-size: large;color:red" >@TempData["message"]</p> 48 <br /> 49 <br /> 50 } 51 52 <form asp-action="RemoveBooks" method="post"> 53 <div> 54 <a class="btn btn-primary" asp-action="AddBook" asp-route-isbn="@Model.BookDetails.ISBN">添加外借书籍</a> 55 <button type="submit" class="btn btn-danger" onclick="return confirmDelete()"> 删除外借书籍</button> 56 </div> 57 <br /> 58 <input type="hidden" name="isbn" value="@Model.BookDetails.ISBN"/> 59 <table> 60 <tbody> 61 <tr> 62 <td></td> 63 <td>@Html.LabelFor(b => temp.BarCode)</td> 64 <td>@Html.LabelFor(b => temp.Bookshelf)</td> 65 <td>@Html.LabelFor(b => temp.BorrowTime)</td> 66 <td>@Html.LabelFor(b => temp.MatureTime)</td> 67 <td>@Html.LabelFor(b => temp.AppointedLatestTime)</td> 68 <td>@Html.LabelFor(b => temp.State)</td> 69 <td>@Html.LabelFor(b => temp.Keeper)</td> 70 </tr> 71 @if (Model.Books.ToList().Count == 0) 72 { 73 <tr><td colspan="7">未有《@Model.BookDetails.Name》的外借/阅览书籍信息</td></tr> 74 } 75 @foreach (var book in Model.Books) 76 { 77 <tr> 78 <td><input type="checkbox" name="barcodes" value="@book.BarCode"/></td> 79 <td><a asp-action="EditBook" asp-route-barcode="@book.BarCode">@Html.DisplayFor(b => book.BarCode)</a></td> 80 <td>@Html.DisplayFor(b => book.BookshelfId)</td> 81 <td>@Html.DisplayFor(b => book.BorrowTime)</td> 82 <td>@Html.DisplayFor(b => book.MatureTime)</td> 83 <td>@Html.DisplayFor(b => book.AppointedLatestTime)</td> 84 <td>@Html.DisplayFor(b => book.State)</td> 85 <td>@Html.DisplayFor(b => book.Keeper.Name)</td> 86 </tr> 87 } 88 </tbody> 89 </table> 90 </form>
结果:
二、添加的外借/阅览图书信息
在 21 行中使用了 Bind 特性,使在模型绑定过程中只绑定对应属性名的属性,防止了与其他数据的绑定。 Bind 特性中的属性名区分大小写,与表单中的 input 的 name 一一对应。
26 行使用 .AsNoTracking 告诉 EF 在查询时禁用更改跟踪提高性能,对不需要进行更改的数据的查询都可以带有该方法。
32 行对表单上传的数据进行检验以确认来自同一本书。
34 行中使用 Include 方法告诉 EF 使返回的书架包含架上书本的信息,使 bookshelf.Books 返回一个 ICollection 实例而不是空 ICollection 以将新的外借/阅览图书信息添加其中。
1 [Authorize(Roles = "Admin")] 2 public IActionResult AddBook(string isbn) 3 { 4 BookDetails bookDetails = _context.BooksDetail.FirstOrDefault(b => b.ISBN == isbn); 5 if (bookDetails == null) 6 { 7 return RedirectToAction("BookDetails", new { isbn = isbn }); 8 } 9 Book book = new Book() 10 { 11 ISBN = bookDetails.ISBN, 12 Name = bookDetails.Name, 13 FetchBookNumber = bookDetails.FetchBookNumber 14 }; 15 return View(book); 16 } 17 18 [HttpPost] 19 [ValidateAntiForgeryToken] 20 [Authorize(Roles = "Admin")] 21 public async Task<IActionResult> AddBook([Bind("ISBN,Name,FetchBookNumber,BarCode,BookshelfId,State")]Book book) 22 { 23 if (ModelState.IsValid) 24 { 25 BookDetails bookDetails = _context.BooksDetail.FirstOrDefault(b => b.ISBN == book.ISBN); 26 Book existBook = _context.Books.AsNoTracking().FirstOrDefault(b => b.BarCode == book.BarCode); 27 if (existBook != null) 28 { 29 TempData["message"] = $"已有二维码为{book.BarCode}的书籍《{existBook.Name}》"; 30 return RedirectToAction("AddBook", new {isbn = book.ISBN}); 31 } 32 if (bookDetails.Name == book.Name) 33 { 34 Bookshelf bookshelf = _context.Bookshelves.Include(b=>b.Books).FirstOrDefault(b => b.BookshelfId == book.BookshelfId); 35 if (bookshelf != null) 36 { 37 book.Sort = bookshelf.Sort; 38 book.Location = bookshelf.Location; 39 bookshelf.Books.Add(book); 40 bookshelf.MaxFetchNumber = bookshelf.Books.Max(b => b.FetchBookNumber); 41 bookshelf.MinFetchNumber = bookshelf.Books.Min(b => b.FetchBookNumber); 42 } 43 await _context.Books.AddAsync(book); 44 await _context.SaveChangesAsync(); 45 TempData["message"] = $"《{book.Name}》 {book.BarCode} 添加成功"; 46 return RedirectToAction("Books", new { isbn = book.ISBN }); 47 } 48 } 49 return View(book); 50 }
视图:
1 @using LibraryDemo.Models.DomainModels 2 @model LibraryDemo.Models.DomainModels.Book 3 4 @{ 5 ViewData["Title"] = "AddBook"; 6 } 7 8 <script> 9 window.onload = function () { 10 $("input").addClass("form-control"); 11 } 12 window.onbeforeunload = function () { 13 return "您的数据未保存,确定退出?"; 14 } 15 function removeOnbeforeunload() { 16 window.onbeforeunload = ""; 17 } 18 </script> 19 20 <h2>@($"为《{Model.Name}》添加借阅/阅览书籍信息")</h2> 21 <br /> 22 <br /> 23 @if (TempData["message"] != null) 24 { 25 <p class="text-primary">@TempData["message"]</p> 26 <br /> 27 <br /> 28 } 29 30 @Html.ValidationSummary(false, "", new { @class = "text-danger" }) 31 <form asp-action="AddBook" method="post"> 32 <div class="form-group"> 33 @Html.LabelFor(b => b.ISBN) 34 <input type="text" value="@Model.ISBN" readonly="readonly" name="@nameof(Model.ISBN)" /> 35 </div> 36 <div class="form-group"> 37 @Html.LabelFor(b => b.Name) 38 <input type="text" value="@Model.Name" readonly="readonly" name="@nameof(Model.Name)" /> 39 </div> 40 <div class="form-group"> 41 @Html.LabelFor(b => b.FetchBookNumber) 42 <input type="text" value="@Model.FetchBookNumber" readonly="readonly" name="@nameof(Model.FetchBookNumber)" /> 43 </div> 44 <div class="form-group"> 45 @Html.LabelFor(b => Model.BarCode) 46 @Html.EditorFor(b => Model.BarCode) 47 </div> 48 <div class="form-group"> 49 @Html.LabelFor(b => Model.BookshelfId) 50 @Html.EditorFor(b => Model.BookshelfId) 51 </div> 52 <div class="form-group"> 53 @Html.LabelFor(b => Model.State) 54 @Html.DropDownListFor(b=>Model.State,Enum.GetValues(typeof(BookState)).Cast<Enum>().Select(state => 55 { 56 string enumVal = Enum.GetName(typeof(BookState), state); 57 string displayVal; 58 switch (enumVal) 59 { 60 case "Normal": 61 displayVal = "可借阅"; 62 break; 63 case "Readonly": 64 displayVal = "馆内阅览"; 65 break; 66 case "Borrowed": 67 displayVal = "已借出"; 68 break; 69 case "ReBorrowed": 70 displayVal = "被续借"; 71 break; 72 case "Appointed": 73 displayVal = "被预约"; 74 break; 75 default: 76 displayVal = ""; 77 break; 78 } 79 return new SelectListItem() 80 { 81 Text=displayVal, 82 Value = enumVal 83 }; 84 })) 85 </div> 86 <div class="form-group"></div> 87 <input type="submit" class="btn-success" onclick="removeOnbeforeunload()" /> 88 </form>
三、移除外借/阅览图书信息
此处实现与之前移除书籍信息大致一致,但额外接受一个 isbn 参数用来返回原 isbn 的外借/阅览图书信息首页:
1 [Authorize(Roles = "Admin")] 2 [HttpPost] 3 [ValidateAntiForgeryToken] 4 public async Task<IActionResult> RemoveBooks(IEnumerable<string> barcodes, string isbn) 5 { 6 StringBuilder sb = new StringBuilder(); 7 foreach (var barcode in barcodes) 8 { 9 Book book = _context.Books.First(b => b.BarCode == barcode); 10 _context.Books.Remove(book); 11 sb.AppendLine($"{book.BarCode} 移除成功"); 12 } 13 await _context.SaveChangesAsync(); 14 TempData["message"] = sb.ToString(); 15 return RedirectToAction("Books", new { isbn = isbn }); 16 }
四、增删总结果:
五、编辑借阅/阅览书籍信息:
在此设置 BarCode 可以被修改,由于修改主键时会导致 EF 的映射失败,因此EF 不支持直接修改主键,但是可以先将原数据删除再进行添加变相修改主键。
POST 的方法中额外接受一个 BarCode 用来保留原书籍信息。
1 [Authorize(Roles = "Admin")] 2 public IActionResult EditBook(string barcode) 3 { 4 Book book = _context.Books.First(b => b.BarCode == barcode); 5 return View(book); 6 } 7 8 [HttpPost] 9 [Authorize(Roles = "Admin")] 10 [ValidateAntiForgeryToken] 11 public async Task<IActionResult> EditBook(string oldBarCode,[Bind("BarCode,BookshelfId,BorrowTime,Name,KeeperId,AppointedLatestTime")]Book book) 12 { 13 if (ModelState.IsValid) 14 { 15 Book oldBook = _context.Books.FirstOrDefault(b => b.BarCode == oldBarCode); 16 if (oldBook == null) 17 { 18 ViewBag["message"] = $"不存在二维码为{oldBarCode}的书籍"; 19 return RedirectToAction("BookDetails"); 20 } 21 22 if (oldBook.Name == book.Name) 23 { 24 book.ISBN = oldBook.ISBN; 25 book.FetchBookNumber = oldBook.FetchBookNumber; 26 Bookshelf bookshelf = _context.Bookshelves.Include(b => b.Books).FirstOrDefault(b => b.BookshelfId == book.BookshelfId); 27 if (bookshelf != null) 28 { 29 book.Sort = bookshelf.Sort; 30 book.Location = bookshelf.Location; 31 bookshelf.Books.Remove(oldBook); 32 bookshelf.Books.Add(book); 33 } 34 35 _context.Books.Remove(oldBook); 36 _context.Books.Add(book); 37 await _context.SaveChangesAsync(); 38 TempData["message"] = "修改成功"; 39 return RedirectToAction("Books", new { isbn = oldBook.ISBN }); 40 } 41 } 42 return View(book); 43 }
原文地址:https://www.cnblogs.com/gokoururi/p/10344146.html