前言:
本系列文章主要为我之前所学知识的一次微小的实践,以我学校图书馆管理系统为雏形所作。
本系列文章主要参考资料:
微软文档: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
<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.FetchBookNumber14 }; 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
barcodein 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 }
热门工具 换一换