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
修改前地址: https://github.com/NanaseRuri/LibraryDemo/tree/SomeError
本章內容:自定義佈局頁、自定義 EditorFor 模板、EF 多對多資料的更新
一、更新模型
折騰許久找不到同時更新具有依賴關係的兩個資料庫的方法,遂對原 Student 模型進行精簡,並新增一個 StudentInfo 模型用來儲存相應的借書資訊。同時原程式中原來非登入介面對 Student 型別的引用改為對 StudentInfo 的引用。
同時由於書籍和學生存在多對多的關係——一本書可被多人預約,而一個人可以借閱多本書,因此在此更新模型使書籍與學生有多對多的關係。
此處僅展示模型的修改,控制器方面的修改請在檢視原始碼:
引入中間導航類:
這裡新增的 AppointingDateTime 用於將借閱的書籍以及預約的書籍進行區分。
1public class AppointmentOrLending 2{ 3public Book Book { get; set; } 4public string BookId { get; set; } 5public StudentInfo Student { get; set; } 6public string StudentId { get; set; } 7public DateTime? AppointingDateTime { get; set; } 8}
Student 模型改動
1public class Student : IdentityUser 2{ 3/// <summary> 4/// 學號 5/// </summary> 6[ProtectedPersonalData] 7[RegularExpression("[UIA]\\d{9}")] 8public override string UserName { get; set; } 9 10[StringLength(14, MinimumLength = 11)] public override string PhoneNumber { get; set; } 11 12public string Name { get; set; } 13public Degrees Degree { get; set; } 14public int MaxBooksNumber { get; set; } 15} View Code
新增新模型 StudentInfo:
1public class StudentInfo 2{ 3[Key] 4public string UserName { get; set; } 5 6[Required] 7public string Name { get; set; } 8 9/// <summary> 10/// 學位,用來限制借書數目 11/// </summary> 12[Required] 13public Degrees Degree { get; set; } 14 15/// <summary> 16/// 最大借書數目 17/// </summary> 18[Required] 19public int MaxBooksNumber { get; set; } 20 21/// <summary> 22/// 已借圖書 23/// </summary> 24public ICollection<AppointmentOrLending> KeepingBooks { get; set; } 25 26public string AppointingBookBarCode { get; set; } 27 28[StringLength(14, MinimumLength = 11)] 29public string PhoneNumber { get; set; } 30 31/// <summary> 32/// 罰款 33/// </summary> 34public decimal Fine { get; set; } 35}
在借書資訊處新增學生資訊和中間類的表,同時在此指定中間類的外來鍵——指定其外來鍵由學生學號和書籍條形碼共同組成,需要重寫 DbContext 父類的 OnModelCreating 方法使其覆蓋對應表格在 EF 的預設生成方式:
1public class LendingInfoDbContext:DbContext 2{ 3public LendingInfoDbContext(DbContextOptions<LendingInfoDbContext> options) : base(options) 4{ 5} 6 7public DbSet<Book> Books { get; set; } 8public DbSet<BookDetails> BooksDetail { get; set; } 9public DbSet<Bookshelf> Bookshelves { get; set; } 10public DbSet<RecommendedBook> RecommendedBooks { get; set; } 11public DbSet<StudentInfo> Students { get; set; } 12public DbSet<AppointmentOrLending> AppointmentOrLendings { get; set; } 13 14protected override void OnModelCreating(ModelBuilder modelBuilder) 15{ 16base.OnModelCreating(modelBuilder); 17modelBuilder.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 表進行初始化:
1if (!context.Students.Any()) 2{ 3IEnumerable<StudentInfo> initialStudents = new[] 4{ 5new StudentInfo() 6{ 7UserName = "U201600001", 8Name = "Nanase", 9PhoneNumber = "12345678910", 10Degree = Degrees.CollegeStudent, 11MaxBooksNumber = 10, 12}, 13new StudentInfo() 14{ 15UserName = "U201600002", 16Name = "Ruri", 17PhoneNumber = "12345678911", 18Degree = Degrees.DoctorateDegree, 19MaxBooksNumber = 15 20} 21}; 22 23IEnumerable<StudentInfo> initialAdmins = new[] 24{ 25new StudentInfo() 26{ 27UserName = "A000000000", 28Name="Admin0000", 29PhoneNumber = "12345678912", 30Degree = Degrees.CollegeStudent, 31MaxBooksNumber = 20 32}, 33new StudentInfo() 34{ 35UserName = "A000000001", 36Name = "Admin0001", 37PhoneNumber = "15827411963", 38Degree = Degrees.CollegeStudent, 39MaxBooksNumber = 20 40}, 41}; 42foreach (var student in initialStudents) 43{ 44context.Students.Add(student); 45await context.SaveChangesAsync(); 46} 47foreach (var admin in initialAdmins) 48{ 49context.Students.Add(admin); 50await context.SaveChangesAsync(); 51} 52}
二、自定義佈局頁
在 ASP.NET 中,預設將 HTML 頁面的 body 元素一部分抽出來,該部分稱作 RenderBody ;然後將這部分放到一個佈局即大體頁面框架中即可完成對同一系列的頁面進行精簡的佈局實現。
預設佈局頁為 _Layout.cshtml,可在檢視資料夾中根目錄或各個控制器檢視目錄的 _ViewStart.cshtml 修改預設佈局頁,或者在每個 Razor 頁面的開頭中指定佈局頁:
1 @{ 2ViewData["Title"] = "EditLendingInfo"; 3Layout = "_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" 16asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css" 17asp-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} 41else 42{ 43<a asp-area="" asp-controller="StudentAccount" asp-action="Login" 44asp-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" 82asp-fallback-src="~/lib/jquery/dist/jquery.min.js" 83asp-fallback-test="window.jQuery" 84crossorigin="anonymous" 85integrity="sha384-tsQFqpEReu7ZLhBV2VZlAu7zcOV+rXbYlF2cqB8txI/8aZajjp4Bqd+V6D5IgvKT"></script> 86<script src="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/bootstrap.min.js" 87asp-fallback-src="~/lib/bootstrap/dist/js/bootstrap.min.js" 88asp-fallback-test="window.jQuery && window.jQuery.fn && window.jQuery.fn.modal" 89crossorigin="anonymous" 90integrity="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> View Code
現在大體框架:
除了預設的 RenderBody 外,可以指定特定的部分放在頁面的不同地方,在佈局頁中使用@RenderSection("SectionName"):
1 @RenderSection("SectionName")
且在檢視頁中使用指定特定的節@section SectionName{};
1 @section SectionName{ 2 3 };
則該檢視頁中的 SectionName 部分會被提取出來放到佈局頁對應的位置。
三、管理員編輯借閱資訊
動作方法:
在此對資料庫的表格使用 Include 方法使 EF 應用其導航屬性以獲得 KeepingBooks 列表,否則使用 Student 物件 KeepingBooks 屬性只會返回空。
1[Authorize(Roles = "Admin")] 2public IActionResult EditLendingInfo(string barcode) 3{ 4if (barcode == null) 5{ 6return RedirectToAction("BookDetails"); 7} 8Book book = _lendingInfoDbContext.Books.FirstOrDefault(b => b.BarCode == barcode); 9return View(book); 10} 11 12[HttpPost] 13[Authorize(Roles = "Admin")] 14[ValidateAntiForgeryToken] 15public async Task<IActionResult> EditLendingInfo([Bind("BarCode,ISBN,BorrowTime,KeeperId,AppointedLatestTime,State")]Book book) 16{ 17if (ModelState.IsValid) 18{ 19if (book.BorrowTime > DateTime.Now) 20{ 21ModelState.AddModelError("", "請檢查外借時間"); 22return View(book); 23} 24if (book.AppointedLatestTime.HasValue) 25{ 26if (book.AppointedLatestTime < DateTime.Now) 27{ 28ModelState.AddModelError("", "請檢查預約時間"); 29return View(book); 30} 31 32if (book.KeeperId == null) 33{ 34ModelState.AddModelError("", "不存在該學生"); 35return View(book); 36} 37} 38 39StudentInfo student = await _lendingInfoDbContext.Students.Include(s => s.KeepingBooks).FirstOrDefaultAsync(s => s.UserName == book.KeeperId); 40 41Book addedBook = _lendingInfoDbContext.Books 42.Include(b => b.Keeper).ThenInclude(k => k.KeepingBooks) 43.FirstOrDefault(b => b.BarCode == book.BarCode); 44if (addedBook == null) 45{ 46return RedirectToAction("Books", new { isbn = book.ISBN }); 47} 48 49StudentInfo preStudent = addedBook.Keeper; 50AppointmentOrLending targetLending = 51preStudent?.KeepingBooks.FirstOrDefault(b => b.BookId == addedBook.BarCode); 52 53addedBook.AppointedLatestTime = book.AppointedLatestTime; 54addedBook.State = book.State; 55addedBook.BorrowTime = book.BorrowTime; 56addedBook.MatureTime = null; 57 58preStudent?.KeepingBooks.Remove(targetLending); 59 60if (addedBook.BorrowTime.HasValue) 61{ 62if (book.KeeperId == null) 63{ 64ModelState.AddModelError("", "請檢查借閱者"); 65return View(book); 66} 67 68if (student == null) 69{ 70ModelState.AddModelError("", "不存在該學生"); 71return View(book); 72} 73if (student != null) 74{ 75if (student.KeepingBooks.Count >= student.MaxBooksNumber) 76{ 77TempData["message"] = "該學生借書已超過上限"; 78} 79 80addedBook.State = BookState.Borrowed; 81student.KeepingBooks.Add(new AppointmentOrLending() 82{ 83BookId = addedBook.BarCode, 84StudentId = student.UserName 85}); 86addedBook.Keeper = student; 87 88} 89addedBook.MatureTime = addedBook.BorrowTime + TimeSpan.FromDays(28); 90} 91 92 93TempData["message"] = "儲存成功"; 94await _lendingInfoDbContext.SaveChangesAsync(); 95return RedirectToAction("Books", new { isbn = book.ISBN }); 96} 97return View(book); 98}
將 BookState 列舉提取成分部檢視 _BookStatePartial:
1 @using LibraryDemo.Models.DomainModels 2 @model Book 3<div class="form-group"> [email protected](b => b.State) [email protected](b => b.State, Enum.GetValues(typeof(BookState)).Cast<Enum>().Select(state => 6{ 7string enumVal = Enum.GetName(typeof(BookState), state); 8string displayVal; 9switch (enumVal) 10{ 11case "Normal": 12displayVal = "可借閱"; 13break; 14case "Readonly": 15displayVal = "館內閱覽"; 16break; 17case "Borrowed": 18displayVal = "已借出"; 19break; 20case "ReBorrowed": 21displayVal = "被續借"; 22break; 23case "Appointed": 24displayVal = "被預約"; 25break; 26default: 27displayVal = ""; 28break; 29} 30return new SelectListItem() 31{ 32Text = displayVal, 33Value = enumVal, 34Selected = 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 @{ 4ViewData["Title"] = "EditLendingInfo"; 5Layout="_LendingLayout"; 6 } 7 8 <h2>@Model.BarCode</h2> 9 <h3>@Model.Name</h3> 10 <br/> 11 12 <script> 13window.onload = function() { 14$("input").addClass("form-control"); 15} 16window.onbeforeunload = function (event) { 17return "您的資料未儲存,確定退出?"; 18} 19function removeOnbeforeunload() { 20window.onbeforeunload = ""; 21} 22 </script> 23 24 @Html.ValidationSummary(false,"",new{@class="text-danger"}) 25 26 <form asp-action="EditLendingInfo" method="post"> [email protected](b => b.BarCode) [email protected](b => b.ISBN) 29<div class="form-group"> [email protected](b => b.KeeperId) [email protected](b => b.KeeperId) 32</div> 33<div class="form-group"> [email protected](b => b.BorrowTime) [email protected](b => b.BorrowTime) 36</div> 37<div class="form-group"> [email protected](b => b.AppointedLatestTime) [email protected](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] 2public async Task<IActionResult> PersonalInfo() 3{ 4StudentInfo student = await _lendingInfoDbContext.Students.Include(s => s.KeepingBooks).ThenInclude(k => k.Book) 5.FirstOrDefaultAsync(s => s.UserName == User.Identity.Name); 6decimal fine = 0; 7foreach (var book in student.KeepingBooks.Where(b => b.Book.MatureTime < DateTime.Now && !b.AppointingDateTime.HasValue)) 8{ 9fine += (DateTime.Now - book.Book.MatureTime.Value).Days * (decimal)0.2; 10book.Book.State = book.Book.State == BookState.Appointed ? BookState.Appointed : BookState.Expired; 11} 12 13student.Fine = fine; 14PersonalInfoViewModel model = new PersonalInfoViewModel() 15{ 16Student = student, 17BookingBook = _lendingInfoDbContext.Books.FirstOrDefault(b => b.BarCode == student.AppointingBookBarCode) 18}; 19return View(model); 20}
檢視:
1 @model LibraryDemo.Models.PersonalInfoViewModel 2 @{ 3ViewData["Title"] = "PersonalInfo"; 4Layout = "_LendingLayout"; 5double 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{ 16fine += (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} 37else 38{ 39foreach (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] 2public async Task<IActionResult> Lending(string barcode) 3{ 4Book targetBook=await _lendingInfoDbContext.Books.Include(b=>b.Appointments).FirstOrDefaultAsync(b => b.BarCode == barcode); 5if (targetBook==null) 6{ 7TempData["message"] = "請重新掃描書籍"; 8return RedirectToAction("PersonalInfo"); 9} 10 11if (targetBook.Appointments.Any(a=>a.AppointingDateTime.HasValue)) 12{ 13TempData["message"] = "此書已被預約"; 14return RedirectToAction("PersonalInfo"); 15} 16 17if (targetBook.State==BookState.Readonly) 18{ 19TempData["message"] = "此書不供外借"; 20return RedirectToAction("PersonalInfo"); 21} 22 23targetBook.State = BookState.Borrowed; 24targetBook.BorrowTime = DateTime.Now.Date; 25targetBook.MatureTime = DateTime.Now.Date+TimeSpan.FromDays(28); 26StudentInfo student = 27await _lendingInfoDbContext.Students.Include(s=>s.KeepingBooks).FirstOrDefaultAsync(s => s.UserName == User.Identity.Name); 28student.KeepingBooks.Add(new AppointmentOrLending() 29{ 30BookId = targetBook.BarCode, 31StudentId = student.UserName 32}); 33await _lendingInfoDbContext.SaveChangesAsync(); 34TempData["message"] = "借書成功"; 35return RedirectToAction("PersonalInfo"); 36}
六、續借書籍
動作方法:
1[Authorize] 2[HttpPost] 3public async Task<IActionResult> ReBorrow(IEnumerable<string> barcodes) 4{ 5StringBuilder borrowSuccess = new StringBuilder(); 6StringBuilder borrowFail = new StringBuilder(); 7borrowSuccess.Append("成功續借書籍:"); 8borrowFail.Append("續借失敗書籍:"); 9foreach (var barcode in barcodes) 10{ 11Book reBorrowBook = _lendingInfoDbContext.Books.FirstOrDefault(b => b.BarCode == barcode); 12if (reBorrowBook != null) 13{ 14if (reBorrowBook.State == BookState.Borrowed && DateTime.Now-reBorrowBook.MatureTime?.Date<=TimeSpan.FromDays(3)) 15{ 16reBorrowBook.State = BookState.ReBorrowed; 17reBorrowBook.BorrowTime = DateTime.Now.Date; 18reBorrowBook.MatureTime = DateTime.Now.Date+TimeSpan.FromDays(28); 19borrowSuccess.Append($"《{reBorrowBook.Name}》、"); 20} 21else 22{ 23borrowFail.Append($"《{reBorrowBook.Name}》、"); 24} 25} 26} 27borrowSuccess.AppendLine(borrowFail.ToString()); 28await _lendingInfoDbContext.SaveChangesAsync(); 29TempData["message"] = borrowSuccess.ToString(); 30return RedirectToAction("PersonalInfo"); 31}
五、查詢書籍
修改之前的 Search 方法使其通過當前使用者的身份返回不同頁面,以及在 _LendingInfoLayout 中新增搜尋框部分:
19 行通過短路使未授權使用者不用登入。
1public async Task<IActionResult> Search(string keyWord, string value) 2{ 3BookDetails bookDetails = new BookDetails(); 4switch (keyWord) 5{ 6case "Name": 7bookDetails = await _lendingInfoDbContext.BooksDetail.AsNoTracking().FirstOrDefaultAsync(b => b.Name == value); 8break; 9case "ISBN": 10bookDetails = await _lendingInfoDbContext.BooksDetail.AsNoTracking().FirstOrDefaultAsync(b => b.ISBN == value); 11break; 12case "FetchBookNumber": 13bookDetails = await _lendingInfoDbContext.BooksDetail.AsNoTracking().FirstOrDefaultAsync(b => b.FetchBookNumber == value); 14break; 15} 16 17if (bookDetails != null) 18{ 19if (User.Identity.IsAuthenticated&& User.IsInRole("Admin")) 20{ 21return RedirectToAction("EditBookDetails", new { isbn = bookDetails.ISBN }); 22} 23else 24{ 25return RedirectToAction("Detail", new {isbn = bookDetails.ISBN}); 26} 27} 28 29TempData["message"] = "找不到該書籍"; 30return 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" 16asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css" 17asp-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} 41else 42{ 43<a asp-area="" asp-controller="StudentAccount" asp-action="Login" 44asp-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")"> [email protected]("keyword",new List<SelectListItem>() 68{ 69new SelectListItem("書名","Name"), 70new SelectListItem("ISBN","ISBN"), 71new 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" 96asp-fallback-src="~/lib/jquery/dist/jquery.min.js" 97asp-fallback-test="window.jQuery" 98crossorigin="anonymous" 99integrity="sha384-tsQFqpEReu7ZLhBV2VZlAu7zcOV+rXbYlF2cqB8txI/8aZajjp4Bqd+V6D5IgvKT"></script> 100<script src="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/bootstrap.min.js" 101asp-fallback-src="~/lib/bootstrap/dist/js/bootstrap.min.js" 102asp-fallback-test="window.jQuery && window.jQuery.fn && window.jQuery.fn.modal" 103crossorigin="anonymous" 104integrity="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>