前言:
本系列文章主要为我之前所学知识的一次微小的实践,以我学校图书馆管理系统为雏形所作。
本系列文章主要参考资料:
微软文档: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/tree/SomeError
本章内容:自定义布局页、自定义 EditorFor 模板、EF 多对多数据的更新
一、更新模型
折腾许久找不到同时更新具有依赖关系的两个数据库的方法,遂对原 Student 模型进行精简,并添加一个 StudentInfo 模型用来保存相应的借书信息。同时原程序中原来非登录界面对 Student 类型的引用改为对 StudentInfo 的引用。
同时由于书籍和学生存在多对多的关系——一本书可被多人预约,而一个人可以借阅多本书,因此在此更新模型使书籍与学生有多对多的关系。
此处仅展示模型的修改,控制器方面的修改请在查看源码:
引入中间导航类:
这里新增的 AppointingDateTime 用于将借阅的书籍以及预约的书籍进行区分。
1 public class AppointmentOrLending 2 { 3 public Book Book { get; set; } 4 public string BookId { get; set; } 5 public StudentInfo Student { get; set; } 6 public string StudentId { get; set; } 7 public DateTime? AppointingDateTime { get; set; } 8 }
Student 模型改动
1 public class Student : IdentityUser 2 { 3 /// <summary> 4 /// 学号 5 /// </summary> 6 [ProtectedPersonalData] 7 [RegularExpression("[UIA]\\d{9}")] 8 public override string UserName { get; set; } 9 10 [StringLength(14, MinimumLength = 11)] public override string PhoneNumber { get; set; } 11 12 public string Name { get; set; } 13 public Degrees Degree { get; set; } 14 public int MaxBooksNumber { get; set; } 15 }
添加新模型 StudentInfo:
1 public class StudentInfo 2 { 3 [Key] 4 public string UserName { get; set; } 5 6 [Required] 7 public string Name { get; set; } 8 9 /// <summary> 10 /// 学位,用来限制借书数目 11 /// </summary> 12 [Required] 13 public Degrees Degree { get; set; } 14 15 /// <summary> 16 /// 最大借书数目 17 /// </summary> 18 [Required] 19 public int MaxBooksNumber { get; set; } 20 21 /// <summary> 22 /// 已借图书 23 /// </summary> 24 public ICollection<AppointmentOrLending> KeepingBooks { get; set; } 25 26 public string AppointingBookBarCode { get; set; } 27 28 [StringLength(14, MinimumLength = 11)] 29 public string PhoneNumber { get; set; } 30 31 /// <summary> 32 /// 罚款 33 /// </summary> 34 public decimal Fine { get; set; } 35 }
在借书信息处添加学生信息和中间类的表,同时在此指定中间类的外键——指定其外键由学生学号和书籍条形码共同组成,需要重写 DbContext 父类的 OnModelCreating 方法使其覆盖对应表格在 EF 的默认生成方式:
1 public class LendingInfoDbContext:DbContext 2 { 3 public LendingInfoDbContext(DbContextOptions<LendingInfoDbContext> options) : base(options) 4 { 5 } 6 7 public DbSet<Book> Books { get; set; } 8 public DbSet<BookDetails> BooksDetail { get; set; } 9 public DbSet<Bookshelf> Bookshelves { get; set; } 10 public DbSet<RecommendedBook> RecommendedBooks { get; set; } 11 public DbSet<StudentInfo> Students { get; set; } 12 public DbSet<AppointmentOrLending> AppointmentOrLendings { get; set; } 13 14 protected override void OnModelCreating(ModelBuilder modelBuilder) 15 { 16 base.OnModelCreating(modelBuilder); 17 modelBuilder.Entity<AppointmentOrLending>() 18 .HasKey(c => new { c.BookId, c.StudentId }); 19 } 20 }
然后例行的更新数据库:
cd librarydemo add-migration AddStudents -c LibraryDemo.Data.LendingInfoDbContext update-database -c LibraryDemo.Data.LendingInfoDbContext
在原 BookInitiator 中对 Students 表进行初始化:
1 if (!context.Students.Any()) 2 { 3 IEnumerable<StudentInfo> initialStudents = new[] 4 { 5 new StudentInfo() 6 { 7 UserName = "U201600001", 8 Name = "Nanase", 9 PhoneNumber = "12345678910", 10 Degree = Degrees.CollegeStudent, 11 MaxBooksNumber = 10, 12 }, 13 new StudentInfo() 14 { 15 UserName = "U201600002", 16 Name = "Ruri", 17 PhoneNumber = "12345678911", 18 Degree = Degrees.DoctorateDegree, 19 MaxBooksNumber = 15 20 } 21 }; 22 23 IEnumerable<StudentInfo> initialAdmins = new[] 24 { 25 new StudentInfo() 26 { 27 UserName = "A000000000", 28 Name="Admin0000", 29 PhoneNumber = "12345678912", 30 Degree = Degrees.CollegeStudent, 31 MaxBooksNumber = 20 32 }, 33 new StudentInfo() 34 { 35 UserName = "A000000001", 36 Name = "Admin0001", 37 PhoneNumber = "15827411963", 38 Degree = Degrees.CollegeStudent, 39 MaxBooksNumber = 20 40 }, 41 }; 42 foreach (var student in initialStudents) 43 { 44 context.Students.Add(student); 45 await context.SaveChangesAsync(); 46 } 47 foreach (var admin in initialAdmins) 48 { 49 context.Students.Add(admin); 50 await context.SaveChangesAsync(); 51 } 52 }
二、自定义布局页
在 ASP.NET 中,默认将 HTML 页面的 body 元素一部分抽出来,该部分称作 RenderBody ;然后将这部分放到一个布局即大体页面框架中即可完成对同一系列的页面进行精简的布局实现。
默认布局页为 _Layout.cshtml,可在视图文件夹中根目录或各个控制器视图目录的 _ViewStart.cshtml 修改默认布局页,或者在每个 Razor 页面的开头中指定布局页:
1 @{ 2 ViewData["Title"] = "EditLendingInfo"; 3 Layout = "_LendingLayout"; 4 }
之前一直使用的是 VS 的默认布局页,现在以该默认布局页为基础,添加自己所需要的信息:
1 @using Microsoft.AspNetCore.Http.Extensions 2 @using Microsoft.AspNetCore.Authorization 3 @inject IAuthorizationService AuthorizationService 4 <!DOCTYPE html> 5 <html> 6 <head> 7 <meta charset="utf-8" /> 8 <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 9 <title>@ViewData["Title"] - LibraryDemo</title> 10 <environment include="Development"> 11 <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" /> 12 <link rel="stylesheet" href="~/css/site.css" /> 13 </environment> 14 <environment exclude="Development"> 15 <link rel="stylesheet" href="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/css/bootstrap.min.css" 16 asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css" 17 asp-fallback-test-class="sr-only" asp-fallback-test-property="position" asp-fallback-test-value="absolute" /> 18 <link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" /> 19 </environment> 20 </head> 21 <body> 22 <nav class="navbar navbar-inverse navbar-fixed-top"> 23 <div class="container"> 24 <div class="navbar-header"> 25 <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"> 26 <span class="sr-only">Toggle navigation</span> 27 <span class="icon-bar"></span> 28 <span class="icon-bar"></span> 29 <span class="icon-bar"></span> 30 </button> 31 <a asp-area="" asp-controller="BookInfo" asp-action="Index" class="navbar-brand">LibraryDemo</a> 32 </div> 33 <div class="navbar-collapse collapse"> 34 <ul class="nav navbar-nav"> 35 <li><a asp-area="" asp-controller="BookInfo" asp-action="Index">首页</a></li> 36 <li> 37 @if (User.Identity.IsAuthenticated) 38 { 39 <a asp-controller="BookInfo" asp-action="PersonalInfo">@User.Identity.Name</a> 40 } 41 else 42 { 43 <a asp-area="" asp-controller="StudentAccount" asp-action="Login" 44 asp-route-returnUrl="@(Context.Request.GetDisplayUrl())">登录</a> 45 } 46 </li> 47 <li><a asp-area="" asp-controller="BookInfo" asp-action="Recommend">推荐图书</a></li> 48 <li><a href="mailto:[email protected]">联系我们</a></li> 49 @if (User.Identity.IsAuthenticated) 50 { 51 <li> 52 <a asp-action="Logout" asp-controller="StudentAccount" asp-route-returnUrl="@(Context.Request.GetDisplayUrl())">注销</a> 53 </li> 54 } 55 </ul> 56 </div> 57 </div> 58 </nav> 59 <partial name="_CookieConsentPartial" /> 60 <div class="container body-content"> 61 @if (TempData["message"] != null) 62 { 63 <br/> 64 <p class="text-success">@TempData["message"]</p> 65 } 66 @RenderBody() 67 <hr/> 68 </div> 69 70 <div class="container" style="margin-top: 20px;"> 71 <footer> 72 <p>© 2018 - LibraryDemo</p> 73 </footer> 74 </div> 75 <environment include="Development"> 76 <script src="~/lib/jquery/dist/jquery.js"></script> 77 <script src="~/lib/bootstrap/dist/js/bootstrap.js"></script> 78 <script src="~/js/site.js" asp-append-version="true"></script> 79 </environment> 80 <environment exclude="Development"> 81 <script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-3.3.1.min.js" 82 asp-fallback-src="~/lib/jquery/dist/jquery.min.js" 83 asp-fallback-test="window.jQuery" 84 crossorigin="anonymous" 85 integrity="sha384-tsQFqpEReu7ZLhBV2VZlAu7zcOV+rXbYlF2cqB8txI/8aZajjp4Bqd+V6D5IgvKT"></script> 86 <script src="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/bootstrap.min.js" 87 asp-fallback-src="~/lib/bootstrap/dist/js/bootstrap.min.js" 88 asp-fallback-test="window.jQuery && window.jQuery.fn && window.jQuery.fn.modal" 89 crossorigin="anonymous" 90 integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"></script> 91 <script src="~/js/site.min.js" asp-append-version="true"></script> 92 </environment> 93 @RenderSection("Scripts", required: false) 94 </body> 95 </html>
现在大体框架:
除了默认的 RenderBody 外,可以指定特定的部分放在页面的不同地方,在布局页中使用@RenderSection("SectionName"):
1 @RenderSection("SectionName")
且在视图页中使用指定特定的节@section SectionName{ };
1 @section SectionName{ 2 3 };
则该视图页中的 SectionName 部分会被提取出来放到布局页对应的位置。
三、管理员编辑借阅信息
动作方法:
在此对数据库的表格使用 Include 方法使 EF 应用其导航属性以获得 KeepingBooks 列表,否则使用 Student 对象 KeepingBooks 属性只会返回空。
1 [Authorize(Roles = "Admin")] 2 public IActionResult EditLendingInfo(string barcode) 3 { 4 if (barcode == null) 5 { 6 return RedirectToAction("BookDetails"); 7 } 8 Book book = _lendingInfoDbContext.Books.FirstOrDefault(b => b.BarCode == barcode); 9 return View(book); 10 } 11 12 [HttpPost] 13 [Authorize(Roles = "Admin")] 14 [ValidateAntiForgeryToken] 15 public async Task<IActionResult> EditLendingInfo([Bind("BarCode,ISBN,BorrowTime,KeeperId,AppointedLatestTime,State")]Book book) 16 { 17 if (ModelState.IsValid) 18 { 19 if (book.BorrowTime > DateTime.Now) 20 { 21 ModelState.AddModelError("", "请检查外借时间"); 22 return View(book); 23 } 24 if (book.AppointedLatestTime.HasValue) 25 { 26 if (book.AppointedLatestTime < DateTime.Now) 27 { 28 ModelState.AddModelError("", "请检查预约时间"); 29 return View(book); 30 } 31 32 if (book.KeeperId == null) 33 { 34 ModelState.AddModelError("", "不存在该学生"); 35 return View(book); 36 } 37 } 38 39 StudentInfo student = await _lendingInfoDbContext.Students.Include(s => s.KeepingBooks).FirstOrDefaultAsync(s => s.UserName == book.KeeperId); 40 41 Book addedBook = _lendingInfoDbContext.Books 42 .Include(b => b.Keeper).ThenInclude(k => k.KeepingBooks) 43 .FirstOrDefault(b => b.BarCode == book.BarCode); 44 if (addedBook == null) 45 { 46 return RedirectToAction("Books", new { isbn = book.ISBN }); 47 } 48 49 StudentInfo preStudent = addedBook.Keeper; 50 AppointmentOrLending targetLending = 51 preStudent?.KeepingBooks.FirstOrDefault(b => b.BookId == addedBook.BarCode); 52 53 addedBook.AppointedLatestTime = book.AppointedLatestTime; 54 addedBook.State = book.State; 55 addedBook.BorrowTime = book.BorrowTime; 56 addedBook.MatureTime = null; 57 58 preStudent?.KeepingBooks.Remove(targetLending); 59 60 if (addedBook.BorrowTime.HasValue) 61 { 62 if (book.KeeperId == null) 63 { 64 ModelState.AddModelError("", "请检查借阅者"); 65 return View(book); 66 } 67 68 if (student == null) 69 { 70 ModelState.AddModelError("", "不存在该学生"); 71 return View(book); 72 } 73 if (student != null) 74 { 75 if (student.KeepingBooks.Count >= student.MaxBooksNumber) 76 { 77 TempData["message"] = "该学生借书已超过上限"; 78 } 79 80 addedBook.State = BookState.Borrowed; 81 student.KeepingBooks.Add(new AppointmentOrLending() 82 { 83 BookId = addedBook.BarCode, 84 StudentId = student.UserName 85 }); 86 addedBook.Keeper = student; 87 88 } 89 addedBook.MatureTime = addedBook.BorrowTime + TimeSpan.FromDays(28); 90 } 91 92 93 TempData["message"] = "保存成功"; 94 await _lendingInfoDbContext.SaveChangesAsync(); 95 return RedirectToAction("Books", new { isbn = book.ISBN }); 96 } 97 return View(book); 98 }
将 BookState 枚举提取成分部视图 _BookStatePartial:
1 @using LibraryDemo.Models.DomainModels 2 @model Book 3 <div class="form-group"> 4 @Html.LabelFor(b => b.State) 5 @Html.DropDownListFor(b => b.State, Enum.GetValues(typeof(BookState)).Cast<Enum>().Select(state => 6 { 7 string enumVal = Enum.GetName(typeof(BookState), state); 8 string displayVal; 9 switch (enumVal) 10 { 11 case "Normal": 12 displayVal = "可借阅"; 13 break; 14 case "Readonly": 15 displayVal = "馆内阅览"; 16 break; 17 case "Borrowed": 18 displayVal = "已借出"; 19 break; 20 case "ReBorrowed": 21 displayVal = "被续借"; 22 break; 23 case "Appointed": 24 displayVal = "被预约"; 25 break; 26 default: 27 displayVal = ""; 28 break; 29 } 30 return new SelectListItem() 31 { 32 Text = displayVal, 33 Value = enumVal, 34 Selected = Model.State.ToString() == enumVal 35 }; 36 })) 37 </div>
Html.DisplayFor 方法是 ASP.NET 内置对各种属性进行展示的方法,可以在项目的 Views 文件夹中的 Shared 文件夹创建对应类型的 Editor 模板供其使用:
在此创建一个 DateTime.cshtml,于是我们使用 Html.DisplayFor 用于展示 DateTime 数据时只会显示年份/月份/天数:
1 @model DateTime? 2 3 @Model?.ToString("yyyy/M/dd")
视图中使用 partial TagHelper 指定其 name 为 _BookStatePartial 以应用分部视图:
1 @using LibraryDemo.Models.DomainModels 2 @model LibraryDemo.Models.DomainModels.Book 3 @{ 4 ViewData["Title"] = "EditLendingInfo"; 5 Layout="_LendingLayout"; 6 } 7 8 <h2>@Model.BarCode</h2> 9 <h3>@Model.Name</h3> 10 <br/> 11 12 <script> 13 window.onload = function() { 14 $("input").addClass("form-control"); 15 } 16 window.onbeforeunload = function (event) { 17 return "您的数据未保存,确定退出?"; 18 } 19 function removeOnbeforeunload() { 20 window.onbeforeunload = ""; 21 } 22 </script> 23 24 @Html.ValidationSummary(false,"",new{@class="text-danger"}) 25 26 <form asp-action="EditLendingInfo" method="post"> 27 @Html.HiddenFor(b => b.BarCode) 28 @Html.HiddenFor(b => b.ISBN) 29 <div class="form-group"> 30 @Html.LabelFor(b => b.KeeperId) 31 @Html.EditorFor(b => b.KeeperId) 32 </div> 33 <div class="form-group"> 34 @Html.LabelFor(b => b.BorrowTime) 35 @Html.EditorFor(b => b.BorrowTime) 36 </div> 37 <div class="form-group"> 38 @Html.LabelFor(b => b.AppointedLatestTime) 39 @Html.EditorFor(b => b.AppointedLatestTime) 40 </div> 41 <partial model="@Model" name="_BookStatePartial"/> 42 <input type="submit" onclick="return removeOnbeforeunload()" class="btn-primary"/> 43 </form>
结果:
四、查看个人信息
这里通过 User.Identity.Name 获取当前登录人的信息以选定特定的学生:
1 [Authorize] 2 public async Task<IActionResult> PersonalInfo() 3 { 4 StudentInfo student = await _lendingInfoDbContext.Students.Include(s => s.KeepingBooks).ThenInclude(k => k.Book) 5 .FirstOrDefaultAsync(s => s.UserName == User.Identity.Name); 6 decimal fine = 0; 7 foreach (var book in student.KeepingBooks.Where(b => b.Book.MatureTime < DateTime.Now && !b.AppointingDateTime.HasValue)) 8 { 9 fine += (DateTime.Now - book.Book.MatureTime.Value).Days * (decimal)0.2; 10 book.Book.State = book.Book.State == BookState.Appointed ? BookState.Appointed : BookState.Expired; 11 } 12 13 student.Fine = fine; 14 PersonalInfoViewModel model = new PersonalInfoViewModel() 15 { 16 Student = student, 17 BookingBook = _lendingInfoDbContext.Books.FirstOrDefault(b => b.BarCode == student.AppointingBookBarCode) 18 }; 19 return View(model); 20 }
视图:
1 @model LibraryDemo.Models.PersonalInfoViewModel 2 @{ 3 ViewData["Title"] = "PersonalInfo"; 4 Layout = "_LendingLayout"; 5 double fine = 0; 6 } 7 <link rel="stylesheet" href="~/css/BookInfo.css" /> 8 9 <h2>@Model.Student.Name</h2> 10 <br /> 11 @if (Model.Student.KeepingBooks.Any(b => b.Book.MatureTime < DateTime.Now)) 12 { 13 <p> 14 罚款:@foreach (var matureBook in Model.Student.KeepingBooks.Where(b => !b.AppointingDateTime.HasValue&&b.Book.MatureTime < DateTime.Now)) 15 { 16 fine += (DateTime.Now - matureBook.Book.MatureTime).Value.TotalDays * 0.2; 17 } 18 @fine 19 </p> 20 } 21 <form asp-action="ReBorrow" method="post"> 22 <table> 23 <tr> 24 <th>续借</th> 25 <th>书名</th> 26 <th>条形码</th> 27 <th>状态</th> 28 <th>到期时间</th> 29 <th>索书号</th> 30 </tr> 31 @if (!Model.Student.KeepingBooks.Any()) 32 { 33 <tr> 34 <td colspan="6" style="text-align: center">未借阅书本</td> 35 </tr> 36 } 37 else 38 { 39 foreach (var keepingBook in Model.Student.KeepingBooks.Where(b=>!b.AppointingDateTime.HasValue)) 40 { 41 <tr> 42 <td><input type="checkbox" value="@keepingBook.Book.BarCode" name="barcodes"/></td> 43 <td>@keepingBook.Book.Name</td> 44 <td>@keepingBook.Book.BarCode</td> 45 <td>@Html.DisplayFor(b=>keepingBook.Book.State)</td> 46 <td>@keepingBook.Book.MatureTime?.ToString("yyyy/MM/dd")</td> 47 <td>@keepingBook.Book.FetchBookNumber</td> 48 </tr> 49 } 50 } 51 </table> 52 <br/> 53 <input type="submit" class="btn-primary btn" value="续借"/> 54 </form> 55 56 <br /> 57 @if (Model.BookingBook != null) 58 { 59 <form asp-action="CancelAppointing"> 60 <table> 61 <tr> 62 <th>书名</th> 63 <th>条形码</th> 64 <th>状态</th> 65 <th>预约时间</th> 66 <th>索书号</th> 67 </tr> 68 <book-info book="@Model.BookingBook" is-booking-book="true"></book-info> 69 </table> 70 <br /> 71 <input type="hidden" name="barcode" value="@Model.BookingBook.BarCode"/> 72 <input type="submit" value="取消预约" class="btn btn-danger"/> 73 </form> 74 }
五、借阅书籍
由于暂时未有获取二维码的接口,仅通过直接访问 Lending 模拟借阅:
1 [Authorize] 2 public async Task<IActionResult> Lending(string barcode) 3 { 4 Book targetBook=await _lendingInfoDbContext.Books.Include(b=>b.Appointments).FirstOrDefaultAsync(b => b.BarCode == barcode); 5 if (targetBook==null) 6 { 7 TempData["message"] = "请重新扫描书籍"; 8 return RedirectToAction("PersonalInfo"); 9 } 10 11 if (targetBook.Appointments.Any(a=>a.AppointingDateTime.HasValue)) 12 { 13 TempData["message"] = "此书已被预约"; 14 return RedirectToAction("PersonalInfo"); 15 } 16 17 if (targetBook.State==BookState.Readonly) 18 { 19 TempData["message"] = "此书不供外借"; 20 return RedirectToAction("PersonalInfo"); 21 } 22 23 targetBook.State = BookState.Borrowed; 24 targetBook.BorrowTime = DateTime.Now.Date; 25 targetBook.MatureTime = DateTime.Now.Date+TimeSpan.FromDays(28); 26 StudentInfo student = 27 await _lendingInfoDbContext.Students.Include(s=>s.KeepingBooks).FirstOrDefaultAsync(s => s.UserName == User.Identity.Name); 28 student.KeepingBooks.Add(new AppointmentOrLending() 29 { 30 BookId = targetBook.BarCode, 31 StudentId = student.UserName 32 }); 33 await _lendingInfoDbContext.SaveChangesAsync(); 34 TempData["message"] = "借书成功"; 35 return RedirectToAction("PersonalInfo"); 36 }
六、续借书籍
动作方法:
1 [Authorize] 2 [HttpPost] 3 public async Task<IActionResult> ReBorrow(IEnumerable<string> barcodes) 4 { 5 StringBuilder borrowSuccess = new StringBuilder(); 6 StringBuilder borrowFail = new StringBuilder(); 7 borrowSuccess.Append("成功续借书籍:"); 8 borrowFail.Append("续借失败书籍:"); 9 foreach (var barcode in barcodes) 10 { 11 Book reBorrowBook = _lendingInfoDbContext.Books.FirstOrDefault(b => b.BarCode == barcode); 12 if (reBorrowBook != null) 13 { 14 if (reBorrowBook.State == BookState.Borrowed && DateTime.Now-reBorrowBook.MatureTime?.Date<=TimeSpan.FromDays(3)) 15 { 16 reBorrowBook.State = BookState.ReBorrowed; 17 reBorrowBook.BorrowTime = DateTime.Now.Date; 18 reBorrowBook.MatureTime = DateTime.Now.Date+TimeSpan.FromDays(28); 19 borrowSuccess.Append($"《{reBorrowBook.Name}》、"); 20 } 21 else 22 { 23 borrowFail.Append($"《{reBorrowBook.Name}》、"); 24 } 25 } 26 } 27 borrowSuccess.AppendLine(borrowFail.ToString()); 28 await _lendingInfoDbContext.SaveChangesAsync(); 29 TempData["message"] = borrowSuccess.ToString(); 30 return RedirectToAction("PersonalInfo"); 31 }
五、查询书籍
修改之前的 Search 方法使其通过当前用户的身份返回不同页面,以及在 _LendingInfoLayout 中添加搜索框部分:
19 行通过短路使未授权用户不用登录。
1 public async Task<IActionResult> Search(string keyWord, string value) 2 { 3 BookDetails bookDetails = new BookDetails(); 4 switch (keyWord) 5 { 6 case "Name": 7 bookDetails = await _lendingInfoDbContext.BooksDetail.AsNoTracking().FirstOrDefaultAsync(b => b.Name == value); 8 break; 9 case "ISBN": 10 bookDetails = await _lendingInfoDbContext.BooksDetail.AsNoTracking().FirstOrDefaultAsync(b => b.ISBN == value); 11 break; 12 case "FetchBookNumber": 13 bookDetails = await _lendingInfoDbContext.BooksDetail.AsNoTracking().FirstOrDefaultAsync(b => b.FetchBookNumber == value); 14 break; 15 } 16 17 if (bookDetails != null) 18 { 19 if (User.Identity.IsAuthenticated&& User.IsInRole("Admin")) 20 { 21 return RedirectToAction("EditBookDetails", new { isbn = bookDetails.ISBN }); 22 } 23 else 24 { 25 return RedirectToAction("Detail", new {isbn = bookDetails.ISBN}); 26 } 27 } 28 29 TempData["message"] = "找不到该书籍"; 30 return RedirectToAction("BookDetails"); 31 }
视图页:
1 @using Microsoft.AspNetCore.Http.Extensions 2 @using Microsoft.AspNetCore.Authorization 3 @inject IAuthorizationService AuthorizationService 4 <!DOCTYPE html> 5 <html> 6 <head> 7 <meta charset="utf-8" /> 8 <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 9 <title>@ViewData["Title"] - LibraryDemo</title> 10 <environment include="Development"> 11 <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" /> 12 <link rel="stylesheet" href="~/css/site.css" /> 13 </environment> 14 <environment exclude="Development"> 15 <link rel="stylesheet" href="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/css/bootstrap.min.css" 16 asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css" 17 asp-fallback-test-class="sr-only" asp-fallback-test-property="position" asp-fallback-test-value="absolute" /> 18 <link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" /> 19 </environment> 20 </head> 21 <body> 22 <nav class="navbar navbar-inverse navbar-fixed-top"> 23 <div class="container"> 24 <div class="navbar-header"> 25 <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"> 26 <span class="sr-only">Toggle navigation</span> 27 <span class="icon-bar"></span> 28 <span class="icon-bar"></span> 29 <span class="icon-bar"></span> 30 </button> 31 <a asp-area="" asp-controller="BookInfo" asp-action="Index" class="navbar-brand">LibraryDemo</a> 32 </div> 33 <div class="navbar-collapse collapse"> 34 <ul class="nav navbar-nav"> 35 <li><a asp-area="" asp-controller="BookInfo" asp-action="Index">首页</a></li> 36 <li> 37 @if (User.Identity.IsAuthenticated) 38 { 39 <a asp-controller="BookInfo" asp-action="PersonalInfo">@User.Identity.Name</a> 40 } 41 else 42 { 43 <a asp-area="" asp-controller="StudentAccount" asp-action="Login" 44 asp-route-returnUrl="@(Context.Request.GetDisplayUrl())">登录</a> 45 } 46 </li> 47 <li><a asp-area="" asp-controller="BookInfo" asp-action="Recommend">推荐图书</a></li> 48 <li><a href="mailto:[email protected]">联系我们</a></li> 49 @if (User.Identity.IsAuthenticated) 50 { 51 <li> 52 <a asp-action="Logout" asp-controller="StudentAccount" asp-route-returnUrl="@(Context.Request.GetDisplayUrl())">注销</a> 53 </li> 54 } 55 </ul> 56 </div> 57 </div> 58 </nav> 59 @if (TempData["message"] != null) 60 { 61 <br /> 62 <p class="text-success">@TempData["message"]</p> 63 } 64 <br /> 65 <div align="center"> 66 <form action="@Url.Action("Search","BookInfo")"> 67 @Html.DropDownList("keyword",new List<SelectListItem>() 68 { 69 new SelectListItem("书名","Name"), 70 new SelectListItem("ISBN","ISBN"), 71 new SelectListItem("索书号","FetchBookNumber"), 72 }) 73 <input type="text" name="value" /> 74 <button type="submit"><span class="glyphicon glyphicon-search"></span></button> 75 </form> 76 </div> 77 <partial name="_CookieConsentPartial" /> 78 <div class="container body-content"> 79 80 @RenderBody() 81 <hr /> 82 </div> 83 84 <div class="container" style="margin-top: 20px;"> 85 <footer> 86 <p>© 2018 - LibraryDemo</p> 87 </footer> 88 </div> 89 <environment include="Development"> 90 <script src="~/lib/jquery/dist/jquery.js"></script> 91 <script src="~/lib/bootstrap/dist/js/bootstrap.js"></script> 92 <script src="~/js/site.js" asp-append-version="true"></script> 93 </environment> 94 <environment exclude="Development"> 95 <script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-3.3.1.min.js" 96 asp-fallback-src="~/lib/jquery/dist/jquery.min.js" 97 asp-fallback-test="window.jQuery" 98 crossorigin="anonymous" 99 integrity="sha384-tsQFqpEReu7ZLhBV2VZlAu7zcOV+rXbYlF2cqB8txI/8aZajjp4Bqd+V6D5IgvKT"></script> 100 <script src="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/bootstrap.min.js" 101 asp-fallback-src="~/lib/bootstrap/dist/js/bootstrap.min.js" 102 asp-fallback-test="window.jQuery && window.jQuery.fn && window.jQuery.fn.modal" 103 crossorigin="anonymous" 104 integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"></script> 105 <script src="~/js/site.min.js" asp-append-version="true"></script> 106 </environment> 107 @RenderSection("Scripts", required: false) 108 </body> 109 </html>
原文地址:https://www.cnblogs.com/gokoururi/p/10344333.html