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

 

前言:git

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

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

微软文档:https://docs.microsoft.com/zh-cn/aspnet/core/getting-started/?view=aspnetcore-2.1&tabs=windows数据库

《Pro ASP.NET MVC 5》、《锋利的 jQuery》json

 

 

此系列皆使用 VS2017+C# 做为开发环境。若是有什么问题或者意见欢迎在留言区进行留言。 windows

项目 github 地址:https://github.com/NanaseRuri/LibraryDemoapp

 

 

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

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

 

 

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

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

 

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

在此域模型能够用如下结构建立:

 

 

 

 2、项目结构

而后就能够开始创建该项目了:

 

 

 

3、创建域模型

学位枚举:

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(14, MinimumLength = 11)] 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(14, MinimumLength = 11)] 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     }

 

 

 

4、建立 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 之间的多对多关系确立完成。

 

 

 

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

在  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             });

 

 

 

6、数据库的迁移、建立及更新

而后在 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 方法

drop-database -c TargetContext

 

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

Update-Database LendingInfoDbContext -TargetMigration:"20181127081115_LendingInfo.cs"
相关文章
相关标签/搜索