Error message here!

Hide Error message here!

忘记密码?

Error message here!

请输入正确邮箱

Hide Error message here!

密码丢失?请输入您的电子邮件地址。您将收到一个重设密码链接。

Error message here!

返回登录

Close

ASP.NET Core 打造一个简单的图书馆管理系统 (修正版)(四)图书信息的增删改查

NanaseRuri 2019-02-15 10:47:00 阅读数:164 评论数:0 点赞数:0 收藏数:0

前言:

本系列文章主要为我之前所学知识的一次微小的实践,以我学校图书馆管理系统为雏形所作。

本系列文章主要参考资料:

微软文档: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

 

 

本章内容:分页、自定义 HtmlHelper、通过模态窗口确认是否提交表单、上传文件、获取文件、预览文件、select 元素、表单提交数组、checkbox、js 确认关闭页面

 

 

 

注:在对 EF 中的数据进行更改时,需要调用对应 Context 的 SaveChange 方法才能对更改进行保存。

 

 

 

一、视图分页视图模型

首先创建一个视图模型用于确定每页的书籍数、页数1 public classPagingInfo2 {3 public int TotalItems { get; set; }4 public int ItemsPerPage { get; set; }5 public int CurrentPage { get; set; }6 7 public intTotalPages8 {9 get => (int)Math.Ceiling((decimal)TotalItems /ItemsPerPage);10 }11 }

 

然后创建另一个视图模型用于确定该分页信息以及各分页的书籍:1 public classBookListViewModel2 {3 public IEnumerable BookDetails { get; set; }4 public PagingInfo PagingInfo { get; set; }5 }

 

创建一个自定义 HtmlHelper 用于在视图中使用 Razor 语法获取分页,在 ASP.NET Core 中 TagBuilder 继承自 IHtmlContent,不能直接对 TagBuilder 进行赋值,需调用 MergeAttribute 方法和 InnerHtml 属性的 AppendHtml 方法编写标签;并且 TagBuilder 没有实现 ToString 方法,只能通过其 WriteTo 方法将内容写到一个 TextWriter 对象中以取出其值:1 public static classPagingHelper2 {3 public static HtmlString PageLinks(this IHtmlHelper html, PagingInfo pagingInfo, FuncpageUrl)4 {5 StringWriter writer=newStringWriter();6 for (int i = 1; i <= pagingInfo.TotalPages; i++)7 {8 TagBuilder tag=new TagBuilder("a");9 tag.MergeAttribute("href",pageUrl(i));10 tag.InnerHtml.AppendHtml(i.ToString());11 if (i==pagingInfo.CurrentPage)12 {13 tag.AddCssClass("selected");14 tag.AddCssClass("btn-primary");15 }16 tag.AddCssClass("btn btn-default");17 tag.WriteTo(writer,HtmlEncoder.Default);18 }19 return newHtmlString(writer.ToString());20 }21 }

 

 

 

二、编辑图书信息页面的首页

在此准备使用 Session 更快地获取图书信息,为了在使用时更直观,此处对 Session 类进行扩展:1 public static classSessionExtensions2 {3 public static void Set(this ISession session, stringkey, T value)4 {5 session.SetString(key, JsonConvert.SerializeObject(value));6 }7 8 public static T Get(this ISession session, stringkey)9 {10 var value =session.GetString(key);11 return value == null ? default(T) : JsonConvert.DeserializeObject(value);12 }13 }

 

创建一个 BookInfo 控制器:1 public classBookInfoController : Controller2 {3 privateLendingInfoDbContext lendingInfoDbContext;4 5 publicBookInfoController(LendingInfoDbContext context)6 {7 lendingInfoDbContext =context;8 }9 }

 

创建学生浏览的首页:

在此使用 Session 获取书籍列表:

创建 BookInfo 控制器并确定其中每个分页的书籍数,使用 Session 获取更快地获取书籍信息:1 public classBookInfoController : Controller 2 { 3 privateLendingInfoDbContext context; 4 private static int amout = 4; 5 6 publicBookInfoController(LendingInfoDbContext context) 7 { 8 context =context; 9 } 10 11 public IActionResult Index(string category, int page = 1) 12 { 13 IEnumerable books = null; 14 if (HttpContext.Session != null) 15 { 16 books = HttpContext.Session.Get>("bookDetails"); 17 } 18 if (books == null) 19 { 20 books =context.BooksDetail; 21 HttpContext.Session?.Set>("books", books); 22 } 23 BookListViewModel model = newBookListViewModel() 24 { 25 PagingInfo = newPagingInfo() 26 { 27 ItemsPerPage =amout, 28 TotalItems =books.Count(), 29 CurrentPage =page, 30 }, 31 BookDetails = books.OrderBy(b => b.FetchBookNumber).Skip((page - 1) /*amout).Take(amout) 32 }; 33 returnView(model); 34 } 35 36 public FileContentResult GetImage(stringisbn) 37 { 38 BookDetails target = context.BooksDetail.FirstOrDefault(b => b.ISBN ==isbn); 39 if (target != null) 40 { 41 returnFile(target.ImageData, target.ImageMimeType); 42 } 43 return null; 44 } 45 }

 

视图页面:

33 行利用 BookListViewModel 中 PagingInfo 的 CurrentPage 获取各序号,32 行中使 img 元素的 src 指向 BookInfoController 的 GetImage 方法以获取图片:1 @using LibraryDemo.HtmlHelpers2 @model BookListViewModel3 @{4 ViewData["Title"] = "Index";5 int i = 1;6 Layout = "_LendingLayout";7 }8 18 19


20 21 22 @foreach (var book inModel.BookDetails)23 {24 25 26 36 48 49 }50 51
@((@Model.PagingInfo.CurrentPage-1)/*4+i++) 27 @if (book.ImageData == null)28 {29 30 }31 else 32 {33 34 }35 37 @book.Name 38
39 @book.Author 40
41 @book.Press 42

@book.FetchBookNumber

43
44
45

@book.Description

46
47
52
53 @Html.PageLinks(Model.PagingInfo, x => Url.Action("Index", new { page =x}))54

 

在此同样使用 Session 获取书籍列表:

利用 [Authorize] 特性指定 Role 属性确保只有 Admin 身份的人才能访问该页面:1 [Authorize(Roles = "Admin")]2 public IActionResult BookDetails(string isbn, int page = 1)3 {4 IEnumerable books = null;5 BookListViewModel model;6 if (HttpContext.Session != null)7 {8 books = HttpContext.Session.Get>("bookDetails");9 }10 if (books == null)11 {12 books =_context.BooksDetail.AsNoTracking();13 HttpContext.Session?.Set>("books", books);14 15 }16 if (isbn != null)17 {18 model = newBookListViewModel()19 {20 BookDetails = new List() { books.FirstOrDefault(b => b.ISBN ==isbn) },21 PagingInfo = newPagingInfo()22 };23 returnView(model);24 }25 model = newBookListViewModel()26 {27 28 PagingInfo = newPagingInfo()29 {30 ItemsPerPage =amout,31 TotalItems =books.Count(),32 CurrentPage =page,33 },34 BookDetails = books.OrderBy(b => b.FetchBookNumber).Skip((page - 1) /*amout).Take(amout)35 };36 returnView(model);37 }

 

BookDetails 视图,confirmDelete 为删除按钮添加了确认的模态窗口;

53 行为 glyphicon 为 Bootstrap 提供的免费图标,只能通过 span 元素使用:1 @using LibraryDemo.HtmlHelpers2 @model BookListViewModel3 @{4 ViewData["Title"] = "BookDetails";5 int i = 1;6 }7 8 26 27 36 37 38


39 @if (TempData["message"] != null)40 {41

@TempData["message"]

42
43
44 }45
46 @Html.DropDownList("keyword",new List()47 {48 new SelectListItem("书名","Name"),49 new SelectListItem("ISBN","ISBN"),50 new SelectListItem("索书号","FetchBookNumber"),51 })52 53 54 55
56
57 58
59 60 61 62 63 64 65 66 67 68 @foreach (var book inModel.BookDetails)69 {70 71 72 73 74 75 76 77 }78 79
序号标题ISBN
@((Model.PagingInfo.CurrentPage-1)/*4+i++)@book.Name@book.ISBN
80
81
82 添加书籍 83 84
85
86 87
88
89 @Html.PageLinks(Model.PagingInfo, x => Url.Action("BookDetails", new { page =x }))90

 

Index 页面:

 

BookDetails 页面:

 

 

 

 

三、添加书籍信息

在此为了接受图片需要使用 IFormFile 接口,为了使图片以原有的格式在浏览器中显示,需要用另一个字段 ImageType 保存文件的格式;

39 页使用 TempData 传递一次性信息告知书籍添加成功,在传递完成后 TempData 将被立即释放:1 [Authorize(Roles = "Admin")]2 publicIActionResult AddBookDetails(BookDetails model)3 {4 if (model == null)5 {6 model = newBookDetails();7 }8 returnView(model);9 }10 11 [HttpPost]12 [ValidateAntiForgeryToken]13 [Authorize(Roles = "Admin")]14 public async TaskAddBookDetails(BookDetails model, IFormFile image)15 {16 BookDetails bookDetails = newBookDetails();17 if(ModelState.IsValid)18 {19 if (image != null)20 {21 bookDetails.ImageMimeType =image.ContentType;22 bookDetails.ImageData = new byte[image.Length];23 await image.OpenReadStream().ReadAsync(bookDetails.ImageData, 0, (int)image.Length);24 }25 26 bookDetails.ISBN =model.ISBN;27 bookDetails.Name =model.Name;28 bookDetails.Author =model.Author;29 bookDetails.Description =model.Description;30 bookDetails.FetchBookNumber =model.FetchBookNumber;31 bookDetails.Press =model.Press;32 bookDetails.PublishDateTime =model.PublishDateTime;33 bookDetails.SoundCassettes =model.SoundCassettes;34 bookDetails.Version =model.Version;35 36 await_lendingInfoDbContext.BooksDetail.AddAsync(bookDetails);37 38 _lendingInfoDbContext.SaveChanges();39 TempData["message"] = $"已添加书籍《{model.Name}》";40 return RedirectToAction("EditBookDetails");41 }42 returnView(model);43 }  

AddBookDetails 视图:

为了使表单可以上传文件,需要指定表单的 enctype 属性值为 multipart/form-data,66 行中使用一个 a 元素包含用来上传文件的 input ,指定其 class 为 btn 以生成一个按钮,指定 href="javascript:;" 使该元素不会返回任何值。

指定 input 的 name 属性为 image 以在上传表单时进行模型绑定,指定其 accept 属性令其打开文件选择框时只接收图片。

JS 代码为 input 添加 onchange 事件以预览上传的图片,并为关闭或刷新页面时添加模态窗口进行确认,同时为提交按钮添加事件以重置 window.onbeforeunload 事件从而不弹出确认窗口:1 @model LibraryDemo.Models.DomainModels.BookDetails2 @{3 ViewData["Title"] = "AddBookDetails";4 }5 6 28 29

添加书籍

30 31
32
33
34 @Html.LabelFor(m =>m.ISBN)35 @Html.TextBoxFor(m => m.ISBN, new { @class = "form-control"})36
37
38 @Html.LabelFor(m =>m.Name)39 @Html.TextBoxFor(m => m.Name, new { @class = "form-control"})40
41
42 @Html.LabelFor(m =>m.Author)43 @Html.TextBoxFor(m => m.Author, new { @class = "form-control"})44
45
46 @Html.LabelFor(m =>m.Press)47 @Html.TextBoxFor(m => m.Press, new { @class = "form-control"})48
49
50 @Html.LabelFor(m =>m.FetchBookNumber)51 @Html.TextBoxFor(m => m.FetchBookNumber, new { @class = "form-control"})52
53
54 @Html.LabelFor(m =>m.SoundCassettes)55 @Html.TextBoxFor(m => m.SoundCassettes, new { @class = "form-control"})56
57
58 @Html.LabelFor(m =>m.Description)59 @Html.TextAreaFor(m => m.Description, new { @class = "form-control", rows = 5})60
61
62 @Html.LabelFor(m =>m.PublishDateTime)63
@Html.EditorFor(m => m.PublishDateTime)
64
65
66 @Html.LabelFor(m =>m.Version)67
@Html.EditorFor(m => m.Version)
68
69
70
71 72 73 选择图片74 77 78 79
80
81 82
83

结果:

 

 

 

四、删除书籍信息

删除书籍的动作方法:

此处通过在之前的 BookDetails 视图中指定 input 元素的 type 为 checkbox,指定 name 为 isbns 以实现多个字符串的模型绑定:1 [Authorize(Roles = "Admin")]2 [HttpPost]3 [ValidateAntiForgeryToken]4 public async Task RemoveBooksAndBookDetails(IEnumerableisbns)5 {6 StringBuilder sb = newStringBuilder();7 foreach (var isbn inisbns)8 {9 BookDetails bookDetails = _lendingInfoDbContext.BooksDetail.First(b => b.ISBN ==isbn);10 IQueryable books = _lendingInfoDbContext.Books.Where(b => b.ISBN ==isbn);11 _lendingInfoDbContext.BooksDetail.Remove(bookDetails);12 _lendingInfoDbContext.Books.RemoveRange(books);13 sb.Append("《" + bookDetails.Name + "》");14 await_lendingInfoDbContext.SaveChangesAsync();15 }16 TempData["message"] = $"已移除书籍{sb.ToString()}";17 return RedirectToAction("BookDetails");18 }

结果:

 

 

 

五、编辑书籍信息

动作方法:1 [Authorize(Roles = "Admin")]2 public async Task EditBookDetails(stringisbn)3 {4 BookDetails book = await _lendingInfoDbContext.BooksDetail.FirstOrDefaultAsync(b => b.ISBN ==isbn);5 if (book != null)6 {7 returnView(book);8 }9 else 10 {11 return RedirectToAction("BookDetails");12 }13 }14 15 [HttpPost]16 [ValidateAntiForgeryToken]17 [Authorize(Roles = "Admin")]18 public async TaskEditBookDetails(BookDetails model, IFormFile image)19 {20 BookDetails bookDetails = _lendingInfoDbContext.BooksDetail.FirstOrDefault(b => b.ISBN ==model.ISBN);21 if(ModelState.IsValid)22 {23 if (bookDetails != null)24 {25 if (image != null)26 {27 bookDetails.ImageMimeType =image.ContentType;28 bookDetails.ImageData = new byte[image.Length];29 await image.OpenReadStream().ReadAsync(bookDetails.ImageData, 0, (int)image.Length);30 }31 32 BookDetails newBookDetails =model;33 34 bookDetails.Name =newBookDetails.Name;35 bookDetails.Author =newBookDetails.Author;36 bookDetails.Description =newBookDetails.Description;37 bookDetails.FetchBookNumber =newBookDetails.FetchBookNumber;38 bookDetails.Press =newBookDetails.Press;39 bookDetails.PublishDateTime =newBookDetails.PublishDateTime;40 bookDetails.SoundCassettes =newBookDetails.SoundCassettes;41 bookDetails.Version =newBookDetails.Version;42 43 await_lendingInfoDbContext.SaveChangesAsync();44 TempData["message"] = $"《{newBookDetails.Name}》修改成功";45 return RedirectToAction("EditBookDetails");46 }47 }48 returnView(model);49 }

 

此处视图与之前 AddBookDetails 大致相同,但在此对一些视图中的 ISBN 字段添加了 readonly 属性使它们不能被直接编辑:1 @model LibraryDemo.Models.DomainModels.BookDetails2 3 @{4 ViewData["Title"] = "EditBookDetails";5 }6 7 34 35

编辑书籍

36 37 @section Scripts38 {39 40 }41 42
43
44
45 @Html.LabelFor(m =>m.ISBN)46 @Html.EditorFor(m =>m.ISBN)47
48
49 @Html.LabelFor(m =>m.Name)50 @Html.TextBoxFor(m => m.Name, new {@class = "form-control"})51
52
53 @Html.LabelFor(m =>m.Author)54 @Html.TextBoxFor(m => m.Author, new {@class = "form-control"})55
56
57 @Html.LabelFor(m =>m.Press)58 @Html.TextBoxFor(m => m.Press, new {@class = "form-control"})59
60
61 @Html.LabelFor(m =>m.FetchBookNumber)62 @Html.TextBoxFor(m => m.FetchBookNumber, new {@class = "form-control"})63
64
65 @Html.LabelFor(m =>m.SoundCassettes)66 @Html.TextBoxFor(m => m.SoundCassettes, new {@class = "form-control"})67
68
69 @Html.LabelFor(m =>m.Description)70 @Html.TextAreaFor(m => m.Description, new {@class = "form-control", rows = 5})71
72
73 @Html.LabelFor(m =>m.PublishDateTime)74
@Html.EditorFor(m => m.PublishDateTime)
75
76
77 @Html.LabelFor(m =>m.Version)78
@Html.EditorFor(m => m.Version)
79
80
81
82 83 84 选择图片85 88 89 90
91 @if (Model.ImageData == null)92 {93
No Image
94 }95 else 96 {97 98 }99
100
101 编辑外借书籍信息 102
103
104 105
106

 

结果:

 

 

 

六、查询特定书籍

此处和之前的账号登录处一样使用 switch 对不同的关键词进行检索:1 public async Task Search(string keyWord, stringvalue)2 {3 BookDetails bookDetails = newBookDetails();4 switch(keyWord)5 {6 case "Name":7 bookDetails =await context.BooksDetail.FirstOrDefaultAsync(b => b.Name ==value);8 break;9 case "ISBN":10 bookDetails =await context.BooksDetail.FirstOrDefaultAsync(b => b.ISBN ==value);11 break;12 case "FetchBookNumber":13 bookDetails =await _context.BooksDetail.FirstOrDefaultAsync(b => b.FetchBookNumber ==value);14 break;15 }16 17 if (bookDetails!=null)18 {19 return RedirectToAction("EditBookDetails", new {isbn =bookDetails.ISBN});20 }21 22 TempData["message"] = "找不到该书籍";23 return RedirectToAction("BookDetails");24 }

 

 结果:

版权声明
本文为[NanaseRuri]所创,转载请带上原文链接,感谢
https://www.cnblogs.com/gokoururi/p/10382292.html

编程之旅,人生之路,不止于编程,还有诗和远方。
阅代码原理,看框架知识,学企业实践;
赏诗词,读日记,踏人生之路,观世界之行;

支付宝红包,每日可领