ASP.NET Core 打造一个简单的图书馆管理系统(七)外借/阅览图书信息的增删改查
前言:
本系列文章主要为我之前所学知识的一次微小的实践,以我学校图书馆管理系统为雏形所作。
本系列文章主要参考资料:
微软文档: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 的外借/阅览图书信息进行展示:
public class BookEditModel { public BookDetails BookDetails { get; set; } public IEnumerable<Book> Books { get; set; } }
对应 BookDetail 的外借/阅览图书信息首页:
[Authorize(Roles = "Admin")] public IActionResult Books(string isbn) { BookEditModel model = new BookEditModel() { Books = _context.Books.Where(b => b.ISBN == isbn), BookDetails = _context.BooksDetail.FirstOrDefault(b => b.ISBN == isbn) }; if (model.BookDetails==null) { TempData["message"] = "未找到目标书籍"; return RedirectToAction("BookDetails"); } return View(model); }
视图:
@using LibraryDemo.Models.DomainModels @model BookEditModel @{ ViewData["Title"] = "外借/阅览书籍信息"; Book temp = new Book(); } <script> function confirmDelete() { var barcodes = document.getElementsByName("barcodes"); var message="确认删除"; for (i in barcodes) { if (barcodes[i].checked) { var book = barcodes[i].parentElement.nextElementSibling.firstElementChild.innerHTML; message=message+"《"+book+"》"; } } message = message + "?"; if (confirm(message) == true) { return true; } else { return false; } } </script> <style type="text/css"> tr + tr { border-top: thin solid gray; } td + td { padding-left: 100px; } .container { width: 1200px; } </style> <h2>《@Model.BookDetails.Name》</h2> <br /> @if (TempData["message"] != null) { <p style="font-size: large;color:red" >@TempData["message"]</p> <br /> <br /> } <form asp-action="RemoveBooks" method="post"> <div> <a class="btn btn-primary" asp-action="AddBook" asp-route-isbn="@Model.BookDetails.ISBN">添加外借书籍</a> <button type="submit" class="btn btn-danger" onclick="return confirmDelete()"> 删除外借书籍</button> </div> <br /> <input type="hidden" name="isbn" value="@Model.BookDetails.ISBN"/> <table> <tbody> <tr> <td></td> <td>@Html.LabelFor(b => temp.BarCode)</td> <td>@Html.LabelFor(b => temp.Bookshelf)</td> <td>@Html.LabelFor(b => temp.BorrowTime)</td> <td>@Html.LabelFor(b => temp.MatureTime)</td> <td>@Html.LabelFor(b => temp.AppointedLatestTime)</td> <td>@Html.LabelFor(b => temp.State)</td> <td>@Html.LabelFor(b => temp.Keeper)</td> </tr> @if (Model.Books.ToList().Count == 0) { <tr><td colspan="7">未有《@Model.BookDetails.Name》的外借/阅览书籍信息</td></tr> } @foreach (var book in Model.Books) { <tr> <td><input type="checkbox" name="barcodes" value="@book.BarCode"/></td> <td><a asp-action="EditBook" asp-route-barcode="@book.BarCode">@Html.DisplayFor(b => book.BarCode)</a></td> <td>@Html.DisplayFor(b => book.BookshelfId)</td> <td>@Html.DisplayFor(b => book.BorrowTime)</td> <td>@Html.DisplayFor(b => book.MatureTime)</td> <td>@Html.DisplayFor(b => book.AppointedLatestTime)</td> <td>@Html.DisplayFor(b => book.State)</td> <td>@Html.DisplayFor(b => book.Keeper.Name)</td> </tr> } </tbody> </table> </form>
结果:
二、添加的外借/阅览图书信息
在 21 行中使用了 Bind 特性,使在模型绑定过程中只绑定对应属性名的属性,防止了与其他数据的绑定。 Bind 特性中的属性名区分大小写,与表单中的 input 的 name 一一对应。
26 行使用 .AsNoTracking 告诉 EF 在查询时禁用更改跟踪提高性能,对不需要进行更改的数据的查询都可以带有该方法。
32 行对表单上传的数据进行检验以确认来自同一本书。
34 行中使用 Include 方法告诉 EF 使返回的书架包含架上书本的信息,使 bookshelf.Books 返回一个 ICollection 实例而不是空 ICollection 以将新的外借/阅览图书信息添加其中。
[Authorize(Roles = "Admin")] public IActionResult AddBook(string isbn) { BookDetails bookDetails = _context.BooksDetail.FirstOrDefault(b => b.ISBN == isbn); if (bookDetails == null) { return RedirectToAction("BookDetails", new { isbn = isbn }); } Book book = new Book() { ISBN = bookDetails.ISBN, Name = bookDetails.Name, FetchBookNumber = bookDetails.FetchBookNumber }; return View(book); } [HttpPost] [ValidateAntiForgeryToken] [Authorize(Roles = "Admin")] public async Task<IActionResult> AddBook([Bind("ISBN,Name,FetchBookNumber,BarCode,BookshelfId,State")]Book book) { if (ModelState.IsValid) { BookDetails bookDetails = _context.BooksDetail.FirstOrDefault(b => b.ISBN == book.ISBN); Book existBook = _context.Books.AsNoTracking().FirstOrDefault(b => b.BarCode == book.BarCode); if (existBook != null) { TempData["message"] = $"已有二维码为{book.BarCode}的书籍《{existBook.Name}》"; return RedirectToAction("AddBook", new {isbn = book.ISBN}); } if (bookDetails.Name == book.Name) { Bookshelf bookshelf = _context.Bookshelves.Include(b=>b.Books).FirstOrDefault(b => b.BookshelfId == book.BookshelfId); if (bookshelf != null) { book.Sort = bookshelf.Sort; book.Location = bookshelf.Location; bookshelf.Books.Add(book); bookshelf.MaxFetchNumber = bookshelf.Books.Max(b => b.FetchBookNumber); bookshelf.MinFetchNumber = bookshelf.Books.Min(b => b.FetchBookNumber); } await _context.Books.AddAsync(book); await _context.SaveChangesAsync(); TempData["message"] = $"《{book.Name}》 {book.BarCode} 添加成功"; return RedirectToAction("Books", new { isbn = book.ISBN }); } } return View(book); }
视图:
@using LibraryDemo.Models.DomainModels @model LibraryDemo.Models.DomainModels.Book @{ ViewData["Title"] = "AddBook"; } <script> window.onload = function () { $("input").addClass("form-control"); } window.onbeforeunload = function () { return "您的数据未保存,确定退出?"; } function removeOnbeforeunload() { window.onbeforeunload = ""; } </script> <h2>@($"为《{Model.Name}》添加借阅/阅览书籍信息")</h2> <br /> <br /> @if (TempData["message"] != null) { <p class="text-primary">@TempData["message"]</p> <br /> <br /> } @Html.ValidationSummary(false, "", new { @class = "text-danger" }) <form asp-action="AddBook" method="post"> <div class="form-group"> @Html.LabelFor(b => b.ISBN) <input type="text" value="@Model.ISBN" readonly="readonly" name="@nameof(Model.ISBN)" /> </div> <div class="form-group"> @Html.LabelFor(b => b.Name) <input type="text" value="@Model.Name" readonly="readonly" name="@nameof(Model.Name)" /> </div> <div class="form-group"> @Html.LabelFor(b => b.FetchBookNumber) <input type="text" value="@Model.FetchBookNumber" readonly="readonly" name="@nameof(Model.FetchBookNumber)" /> </div> <div class="form-group"> @Html.LabelFor(b => Model.BarCode) @Html.EditorFor(b => Model.BarCode) </div> <div class="form-group"> @Html.LabelFor(b => Model.BookshelfId) @Html.EditorFor(b => Model.BookshelfId) </div> <div class="form-group"> @Html.LabelFor(b => Model.State) @Html.DropDownListFor(b=>Model.State,Enum.GetValues(typeof(BookState)).Cast<Enum>().Select(state => { string enumVal = Enum.GetName(typeof(BookState), state); string displayVal; switch (enumVal) { case "Normal": displayVal = "可借阅"; break; case "Readonly": displayVal = "馆内阅览"; break; case "Borrowed": displayVal = "已借出"; break; case "ReBorrowed": displayVal = "被续借"; break; case "Appointed": displayVal = "被预约"; break; default: displayVal = ""; break; } return new SelectListItem() { Text=displayVal, Value = enumVal }; })) </div> <div class="form-group"></div> <input type="submit" class="btn-success" onclick="removeOnbeforeunload()" /> </form>
三、移除外借/阅览图书信息
此处实现与之前移除书籍信息大致一致,但额外接受一个 isbn 参数用来返回原 isbn 的外借/阅览图书信息首页:
[Authorize(Roles = "Admin")] [HttpPost] [ValidateAntiForgeryToken] public async Task<IActionResult> RemoveBooks(IEnumerable<string> barcodes, string isbn) { StringBuilder sb = new StringBuilder(); foreach (var barcode in barcodes) { Book book = _context.Books.First(b => b.BarCode == barcode); _context.Books.Remove(book); sb.AppendLine($"{book.BarCode} 移除成功"); } await _context.SaveChangesAsync(); TempData["message"] = sb.ToString(); return RedirectToAction("Books", new { isbn = isbn }); }
四、增删总结果:
五、编辑借阅/阅览书籍信息:
在此设置 BarCode 可以被修改,由于修改主键时会导致 EF 的映射失败,因此EF 不支持直接修改主键,但是可以先将原数据删除再进行添加变相修改主键。
POST 的方法中额外接受一个 BarCode 用来保留原书籍信息。
[Authorize(Roles = "Admin")] public IActionResult EditBook(string barcode) { Book book = _context.Books.First(b => b.BarCode == barcode); return View(book); } [HttpPost] [Authorize(Roles = "Admin")] [ValidateAntiForgeryToken] public async Task<IActionResult> EditBook(string oldBarCode,[Bind("BarCode,BookshelfId,BorrowTime,Name,KeeperId,AppointedLatestTime")]Book book) { if (ModelState.IsValid) { Book oldBook = _context.Books.FirstOrDefault(b => b.BarCode == oldBarCode); if (oldBook == null) { ViewBag["message"] = $"不存在二维码为{oldBarCode}的书籍"; return RedirectToAction("BookDetails"); } if (oldBook.Name == book.Name) { book.ISBN = oldBook.ISBN; book.FetchBookNumber = oldBook.FetchBookNumber; Bookshelf bookshelf = _context.Bookshelves.Include(b => b.Books).FirstOrDefault(b => b.BookshelfId == book.BookshelfId); if (bookshelf != null) { book.Sort = bookshelf.Sort; book.Location = bookshelf.Location; bookshelf.Books.Remove(oldBook); bookshelf.Books.Add(book); } _context.Books.Remove(oldBook); _context.Books.Add(book); await _context.SaveChangesAsync(); TempData["message"] = "修改成功"; return RedirectToAction("Books", new { isbn = oldBook.ISBN }); } } return View(book); }
- ASP.NET Core 打造一个简单的图书馆管理系统(五)初始化书籍信息
- ASP.NET Core 打造一个简单的图书馆管理系统(三)基本登录页面以及授权逻辑的建立
- ASP.NET Core 打造一个简单的图书馆管理系统(四)密码修改以及密码重置
- ASP.NET Core 打造一个简单的图书馆管理系统(二)Code First 多对多关系的建立
- asp.net core 身份认证/权限管理系统简介及简单案例
- sql server 关于表中只增标识问题 C# 实现自动化打开和关闭可执行文件(或 关闭停止与系统交互的可执行文件) ajaxfileupload插件上传图片功能,用MVC和aspx做后台各写了一个案例 将小写阿拉伯数字转换成大写的汉字, C# WinForm 中英文实现, 国际化实现的简单方法 ASP.NET Core 2 学习笔记(六)ASP.NET Core 2 学习笔记(三)
- ASP.NET MVC+LINQ开发一个图书销售站点(7):图书分类管理
- 一个简单的学生信息管理系统
- asp.net学生信息管理系统-参数设置简介(一)
- 使用xml+asp.net打造简单的站点导航&管理站点友情链接
- 多年前写的一个ASP.NET网站管理系统,到现在有些公司在用
- Asp.Net Core 项目实战之权限管理系统(1) 使用AdminLTE搭建前端
- asp.net 学生信息管理系统 全免费
- ASP.NET学生信息管理系统-班级建制(四) 课程教师分配
- 一步一步使用Ext JS MVC与Asp.Net MVC 3开发简单的CMS后台管理系统之登录窗口
- 一步一步使用Ext JS MVC与Asp.Net MVC 3开发简单的CMS后台管理系统之登录窗口调试
- C# 实现一个简单的图书管理系统(无数据库)新手教程1
- 一步一步使用Ext JS MVC与Asp.Net MVC 3开发简单的CMS后台管理系统之调整首页显示
- Asp.Net Core 项目实战之权限管理系统(3) 通过EntityFramework Core使用PostgreSQL
- 一步一步使用Ext JS MVC与Asp.Net MVC 3开发简单的CMS后台管理系统之调整首页显示