ASP.NET Core MVC 打造一個簡單的圖書館管理系統 (修正版)(一) 基本模型以及數據庫的建立


 

前言:

本系列文章主要為我之前所學知識的一次微小的實踐,以我學校圖書館管理系統為雛形所作。

本系列文章主要參考資料:

微軟文檔: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(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     }

 

 

 

四、創建 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 方法

drop-database -c TargetContext

 

而為 update 方法指定遷移類則可以回溯數據庫。

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM