前言:

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

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

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

本章内容:对图书馆系统组成的简要分析。以及对域模型以及相应数据库的建立。

知识点:Code First、EF 基本使用方法、ASP.NET Core 使用 EF Core 的配置方法、EF 多对多关系的建立、取消 int 主键自动增长。

一、对图书馆系统域模型的分析

一个图书馆系统需要有管理员、 学生、书架以及书籍

域模型,即用来存储数据的模型。

在此域模型可以用以下结构创建:

 二、项目结构

然后就可以开始建立该项目了:

三、建立域模型

学位枚举:

  1. public enum Degrees
  2. {
  3. [Display(Name = "本科生")]
  4. CollegeStudent,
  5. [Display(Name = "研究生")]
  6. Postgraduate,
  7. [Display(Name = "博士生")]
  8. DoctorateDegree
  9. }

图书借阅状态枚举:

  1. public enum BookState
  2. {
  3. /// <summary>
  4. /// 可借阅
  5. /// </summary>
  6. [Display(Name = "正常")]
  7. Normal,
  8.  
  9. /// <summary>
  10. /// 馆内阅览
  11. /// </summary>
  12. [Display(Name = "馆内阅览")]
  13. Readonly,
  14.  
  15. /// <summary>
  16. /// 已借出
  17. /// </summary>
  18. [Display(Name = "已借出")]
  19. Borrowed,
  20.  
  21. /// <summary>
  22. /// 被续借
  23. /// </summary>
  24. [Display(Name = "被续借")]
  25. ReBorrowed,
  26.  
  27. /// <summary>
  28. /// 被预约
  29. /// </summary>
  30. [Display(Name = "被预约")]
  31. Appointed,
  32.  
  33. [Display(Name = "过期")]
  34. Expired
  35. }

该项目准备使用一个数据库存储学生账户信息,另一个则用于存储学生借书信息:

学生账户信息:

  1. public class Student : IdentityUser
  2. {
  3. /// <summary>
  4. /// 学号
  5. /// </summary>
  6. [ProtectedPersonalData]
  7. [RegularExpression("[UIA]\\d{9}")]
  8. [Display(Name = "学号")]
  9. public override string UserName { get; set; }
  10.  
  11. [Display(Name = "手机号")]
  12. [StringLength(, MinimumLength = )]
  13. public override string PhoneNumber { get; set; }
  14.  
  15. [Display(Name = "姓名")]
  16. public string Name { get; set; }
  17. [Display(Name = "学历")]
  18. public Degrees Degree { get; set; }
  19. [Display(Name = "最大借书数目")]
  20. public int MaxBooksNumber { get; set; }
  21. }

书籍信息:

  1. public class Book
  2. {
  3. /// <summary>
  4. /// 二维码
  5. /// </summary>
  6. [Key]
  7. [Display(Name = "二维码")]
  8. [Required(ErrorMessage = "未填写二维码")]
  9. public string BarCode { get; set; }
  10.  
  11. public string ISBN { get; set; }
  12.  
  13. /// <summary>
  14. /// 书名
  15. /// </summary>
  16. [Display(Name = "书名")]
  17. public string Name { get; set; }
  18.  
  19. /// <summary>
  20. /// 取书号
  21. /// </summary>
  22. [Display(Name = "取书号")]
  23. public string FetchBookNumber { get; set; }
  24.  
  25. /// <summary>
  26. /// 所在书架
  27. /// </summary>
  28. public Bookshelf Bookshelf { get; set; }
  29.  
  30. [Display(Name = "书架号")]
  31. public int BookshelfId { get; set; }
  32.  
  33. /// <summary>
  34. /// 借出时间
  35. /// </summary>
  36. [Display(Name = "借出时间")]
  37. public DateTime? BorrowTime { get; set; }
  38.  
  39. /// <summary>
  40. /// 到期时间
  41. /// </summary>
  42. [Display(Name = "到期时间")]
  43. public DateTime? MatureTime { get; set; }
  44.  
  45. /// <summary>
  46. /// 预约最晚借书日期
  47. /// </summary>
  48. [Display(Name = "预约取书时间")]
  49. public DateTime? AppointedLatestTime { get; set; }
  50.  
  51. /// <summary>
  52. /// 借阅状态
  53. /// </summary>
  54. [Display(Name = "书籍状态")]
  55. public BookState State { get; set; }
  56.  
  57. /// <summary>
  58. /// 持有者,指定外键
  59. /// </summary>
  60. public StudentInfo Keeper { get; set; }
  61. [Display(Name = "持有者学号")]
  62. public string KeeperId{ get; set; }
  63.  
  64. [Display(Name = "位置")]
  65. public string Location { get; set; }
  66.  
  67. [Display(Name = "分类")]
  68. public string Sort { get; set; }
  69.  
  70. public ICollection<AppointmentOrLending> Appointments { get; set; }
  71. }

书架信息:

由于 EF 会自动将 int 类型的主键设置为自动增长,因此自定义 Bookshelf 的 ID 在插入数据库时会报错,在此需添加修饰 [DatabaseGenerated(DatabaseGeneratedOption.None)] 告知 ef 取消该设置:

  1. public class Bookshelf
  2. {
  3. /// <summary>
  4. /// 书架ID
  5. /// </summary>
  6. [Key]
  7. //不自动增长
  8. [DatabaseGenerated(DatabaseGeneratedOption.None)]
  9. public int BookshelfId { get; set; }
  10.  
  11. /// <summary>
  12. /// 书架的书籍类别
  13. /// </summary>
  14.  
  15. [Required]
  16. public string Sort { get; set; }
  17. /// <summary>
  18. /// 最小取书号
  19. /// </summary>
  20. [Required]
  21. public string MinFetchNumber { get; set; }
  22. [Required]
  23. public string MaxFetchNumber { get; set; }
  24.  
  25. /// <summary>
  26. /// 书架位置
  27. /// </summary>
  28. [Required]
  29. public string Location { get; set; }
  30.  
  31. /// <summary>
  32. /// 全部藏书
  33. /// </summary>
  34. public ICollection<Book> Books { get; set; }
  35. }

由于一个学生可以借阅多本书籍,一本书籍可被多人预约,因此书籍和学生具有多对多的关系,在此引入中间类:

其中的 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. }

学生借书信息:

在 EF 中多对多关系实际上是两个多对一关系。此处 ICollection 的属性成为导航属性,用来提示 EF  StudentInfo 和 AppointmentOrLending 之间存在着多对一的关系。

  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(, MinimumLength = )]
  29. public string PhoneNumber { get; set; }
  30.  
  31. /// <summary>
  32. /// 罚款
  33. /// </summary>
  34. public decimal Fine { get; set; }
  35. }

外借/阅览书籍信息:

在约定中,若不指定主键,则 EF 会使用 (类名)+ID 的方式指定或创建主键,在此使用 [Key] 指定主键,使用 [Required] 指定字段为必须,这种可以为属性添加在数据库中的约束或者在视图中的约束的修饰称为 DataAnnotations 。

此处 ICollection 的属性成为导航属性,用来提示 EF  Book 和 AppointmentOrLending 之间存在着多对一的关系。

  1. public class Book
  2. {
  3. /// <summary>
  4. /// 二维码
  5. /// </summary>
  6. [Key]
  7. [Display(Name = "二维码")]
  8. [Required(ErrorMessage = "未填写二维码")]
  9. public string BarCode { get; set; }
  10.  
  11. public string ISBN { get; set; }
  12.  
  13. /// <summary>
  14. /// 书名
  15. /// </summary>
  16. [Display(Name = "书名")]
  17. public string Name { get; set; }
  18.  
  19. /// <summary>
  20. /// 取书号
  21. /// </summary>
  22. [Display(Name = "取书号")]
  23. public string FetchBookNumber { get; set; }
  24.  
  25. /// <summary>
  26. /// 所在书架
  27. /// </summary>
  28. public Bookshelf Bookshelf { get; set; }
  29.  
  30. [Display(Name = "书架号")]
  31. public int BookshelfId { get; set; }
  32.  
  33. /// <summary>
  34. /// 借出时间
  35. /// </summary>
  36. [Display(Name = "借出时间")]
  37. public DateTime? BorrowTime { get; set; }
  38.  
  39. /// <summary>
  40. /// 到期时间
  41. /// </summary>
  42. [Display(Name = "到期时间")]
  43. public DateTime? MatureTime { get; set; }
  44.  
  45. /// <summary>
  46. /// 预约最晚借书日期
  47. /// </summary>
  48. [Display(Name = "预约取书时间")]
  49. public DateTime? AppointedLatestTime { get; set; }
  50.  
  51. /// <summary>
  52. /// 借阅状态
  53. /// </summary>
  54. [Display(Name = "书籍状态")]
  55. public BookState State { get; set; }
  56.  
  57. /// <summary>
  58. /// 持有者,指定外键
  59. /// </summary>
  60. public StudentInfo Keeper { get; set; }
  61. [Display(Name = "持有者学号")]
  62. public string KeeperId{ get; set; }
  63.  
  64. [Display(Name = "位置")]
  65. public string Location { get; set; }
  66.  
  67. [Display(Name = "分类")]
  68. public string Sort { get; set; }
  69.  
  70. public ICollection<AppointmentOrLending> Appointments { get; set; }
  71. }

四、创建 DbContext 

学生账户信息数据库:

  1. public class StudentIdentityDbContext:IdentityDbContext<Student>
  2. {
  3. public StudentIdentityDbContext(DbContextOptions<StudentIdentityDbContext> options) : base(options)
  4. {
  5. }
  6. }

借阅信息数据库:

为了使 StudentInfo 类的 UserName 和 Book 的 BarCode 共同作为 AppointmentOrLending 中间类的主键,需覆写 OnModelCreating 方法:

至此 StudentInfo 和 Book 的多对多关系正式确立。

  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. }

于是 Book 和 StudentInfo 之间的多对多关系确立完成。

五、根据约定配置数据库,进行依赖注入

在  appsettings.json 中添加数据库连接字符串。

  1. {
  2. "ConnectionStrings": {
  3. "LendingInfoDbContext": "Server=(localdb)\\mssqllocaldb;Database=LendingInfoDbContext;Trusted_Connection=True;MultipleActiveResultSets=true",
  4. "StudentIdentityDbContext": "Server=(localdb)\\mssqllocaldb;Database=StudentIdentityDbContext;Trusted_Connection=True;MultipleActiveResultSets=true"
  5. },
  6. "Logging": {
  7. "LogLevel": {
  8. "Default": "Warning"
  9. }
  10. },
  11. "AllowedHosts": "*"
  12. }

在 Startup.cs 中的 ConfigureServices 方法中对数据库进行配置:

  1. services.AddDbContext<LendingInfoDbContext>(options =>
  2. {
  3. options.UseSqlServer(Configuration.GetConnectionString("LendingInfoDbContext"));
  4. });
  5. services.AddDbContext<StudentIdentityDbContext>(options =>
  6. {
  7. options.UseSqlServer(Configuration.GetConnectionString("StudentIdentityDbContext"));
  8. });

六、数据库的迁移、创建及更新

然后在 pm控制台 中添加迁移:

添加迁移的语法为 add-migration <迁移类名> -c <具体 DbContext 名>

  1. cd LibraryDemo
  2. add-migration LendingInfo -c LibraryDemo.Data.LendingInfoDbContext
  3. add-migration StudentIdentity -c LibraryDemo.Data.StudentIdentityDbContext

运行 add-migration 命令会创建 Migrations 文件夹以及相应的迁移快照:

显示的类名为 <创建时间>_<迁移类名>,而实际的类名为 add-migration 后的第一个参数名。

在创建迁移时,EF 会自动为我们创建或更新对应 DbContext 的快照,即其中后缀为 Snapshot 的类。其中会包含当前对应的 DbCOntext 的结构,并会以代码保留相应的约束,如 LendingInfoDbContextModelSnapshot 类:

生成的迁移类 LendingInfo 和 Account 类则有两个方法—— 用于更新数据库的 Up 方法和用以回溯数据库的 Down 方法,可以在这两个方法或者在快照的 BuildModel 方法中使用 Fluent API 对数据库做进一步的改动,并且通过对 Fluent API 的使用可以使我们的类少用 DataAnnotations 以保证类的整洁。

需要注意的是,生成的迁移类中的 Up 和 Down 方法是根据生成迁移之前的数据库快照生成的,如我在之后为 LendingInfoDbContext 添加 DbSet<RecommendedBook> 时,在以上的基础上运行了 add-migration AddRecommendedBook -c LibraryDemo.Data.LendingInfoDbContext ,生成的 Up 方法只包括添加表 RecommendedBooks 的行为,而 Down 方法只包括删除表 RecommendedBooks 的行为。

随后在 pm控制台 执行以下创建或更新数据库:

  1. update-database -c LibraryDemo.Data.LendingInfoDbContext
  2. update-database -c LibraryDemo.Data.StudentIdentityDbContext

最后在 SQL server对象管理器 中可以看见创建的数据库以及对应的表:

至此域模型创建工作完成。

补充:

使用命令行对数据库进行迁移及更新有两种方式:

  1. dotnet ef migrations migrationName -c TargetContext
  2. dotnet ef database update -c TargetContext
  1. add-migration migrationName -c TargetContext
  2. update-Database -c TargetContext

windows 命令行命令不区分大小写,其中 migrationName 为迁移类名,最好提供有意义的命名;而 TargetContext 为目标 DbContext 类名,需要使用带有命名空间的完全命名。

如果需要删除数据库则使用 drop 方法

  1. drop-database -c TargetContext

而为 update 方法指定迁移类则可以回溯数据库。

  1. Update-Database LendingInfoDbContext -TargetMigration:"20181127081115_LendingInfo.cs"

ASP.NET Core MVC 打造一个简单的图书馆管理系统 (修正版)(一) 基本模型以及数据库的建立的更多相关文章

  1. ASP.NET Core MVC 打造一个简单的图书馆管理系统 (修正版)(二)数据库初始化、基本登录页面以及授权逻辑的建立

    前言: 本系列文章主要为我之前所学知识的一次微小的实践,以我学校图书馆管理系统为雏形所作. 本系列文章主要参考资料: 微软文档:https://docs.microsoft.com/zh-cn/asp ...

  2. ASP.NET Core MVC 打造一个简单的图书馆管理系统 (修正版)(七) 学生信息增删

    前言: 本系列文章主要为我之前所学知识的一次微小的实践,以我学校图书馆管理系统为雏形所作. 本系列文章主要参考资料: 微软文档:https://docs.microsoft.com/zh-cn/asp ...

  3. ASP.NET Core MVC 打造一个简单的图书馆管理系统 (修正版)(六)学生借阅/预约/查询书籍事务

    前言: 本系列文章主要为我之前所学知识的一次微小的实践,以我学校图书馆管理系统为雏形所作. 本系列文章主要参考资料: 微软文档:https://docs.microsoft.com/zh-cn/asp ...

  4. ASP.NET Core MVC 打造一个简单的图书馆管理系统 (修正版)(五)外借/阅览图书信息的增删改查

    前言: 本系列文章主要为我之前所学知识的一次微小的实践,以我学校图书馆管理系统为雏形所作. 本系列文章主要参考资料: 微软文档:https://docs.microsoft.com/zh-cn/asp ...

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

    前言: 本系列文章主要为我之前所学知识的一次微小的实践,以我学校图书馆管理系统为雏形所作. 本系列文章主要参考资料: 微软文档:https://docs.microsoft.com/zh-cn/asp ...

  6. ASP.NET Core MVC 打造一个简单的图书馆管理系统 (修正版)(三)密码修改以及密码重置

     前言: 本系列文章主要为我之前所学知识的一次微小的实践,以我学校图书馆管理系统为雏形所作. 本系列文章主要参考资料: 微软文档:https://docs.microsoft.com/zh-cn/as ...

  7. 002.Create a web API with ASP.NET Core MVC and Visual Studio for Windows -- 【在windows上用vs与asp.net core mvc 创建一个 web api 程序】

    Create a web API with ASP.NET Core MVC and Visual Studio for Windows 在windows上用vs与asp.net core mvc 创 ...

  8. 在ASP.NET Core MVC中构建简单 Web Api

    Getting Started 在 ASP.NET Core MVC 框架中,ASP.NET 团队为我们提供了一整套的用于构建一个 Web 中的各种部分所需的套件,那么有些时候我们只需要做一个简单的 ...

  9. Pro ASP.NET Core MVC 第6版 第二章(前半章)

    目录 第二章 第一个MVC 应用程序 学习一个软件开发框架的最好方法是跳进他的内部并使用它.在本章,你将用ASP.NET Core MVC创建一个简单的数据登录应用.我将它一步一步地展示,以便你能看清 ...

随机推荐

  1. ES6__Iterator和for...of循环

    /** * Iterator和for...of循环 */ // --------------------------------------------------------------- /** ...

  2. Method and system for early speculative store-load bypass

    In an embodiment, the present invention describes a method and apparatus for detecting RAW condition ...

  3. poj 1236+hdu2767 有向图 缩点+看度数(tarjan)

    1236题意:一个有向图,1,求至少从几个点出发可以遍历该图,2:,求至少添加多少边,使强连通.而,HDU的只有后面一问. 解;先缩点,第一问只需找所有入度为0的点即可.,第2问,max(入度为0的点 ...

  4. 【小记事】解除端口占用(Windows)

    开发中有时会因为端口占用而导致起项目时报错(如下图),这时候只要解除端口占用即可. 解除端口占用: 1.打开cmd(win+r),查看端口占用情况 netstat -ano | findstr 端口号 ...

  5. Java模拟斗地主(实现大小排序)

    import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Li ...

  6. java基础 4 继承(1)抽象类与接口的区别

    抽象类: 是用来捕捉子类的通用特性的,至少包含一个抽象方法,该抽象方法必须在子类中实现,由于抽象类没有抽象方法的具体实现,因此不能对抽象类进行实例化. 接口: 定义了一组方法,是抽象方法的集合,但是接 ...

  7. hotswapagent——热更新代码而无需重启生产环境

    http://blog.csdn.net/littleschemer/article/details/51645722

  8. 小胖说事35-----Terminating app due to uncaught exception &#39;CALayerInvalidGeometry&#39;, reason: &#39;CALayer posi

    2011-06-11 15:19:17.167 ***[930:707] *** Terminating app due to uncaught exception 'CALayerInvalidGe ...

  9. hdu1507——Uncle Tom&#39;s Inherited Land*

    Uncle Tom's Inherited Land* Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (J ...

  10. Android Service 不被杀死并提高优先级

    Android Service 不被杀死有两种思路,一种是将APP设置为系统应用.还有一种是增强service的生命力.即使屏幕背光关闭时也能执行. 因为设置为系统应用须要root.所以一般使用后一种 ...