ASP.NET Core 打造一个简单的图书馆管理系统学生借阅/预约/查询书籍事务
Posted NanaseRuri
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 用于将借阅的书籍以及预约的书籍进行区分。
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 = "12345678910", 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:Nanase@cnblogs.com">联系我们</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 以上是关于ASP.NET Core 打造一个简单的图书馆管理系统学生借阅/预约/查询书籍事务的主要内容,如果未能解决你的问题,请参考以下文章ASP.NET Core 打造一个简单的图书馆管理系统密码修改以及密码重置
ASP.NET Core 打造一个简单的图书馆管理系统初始化书籍信息
ASP.NET Core 打造一个简单的图书馆管理系统学生借阅/预约/查询书籍事务
ASP.NET Core 打造一个简单的图书馆管理系统 (修正版) 学生信息增删(伪终章)