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
本章內容:對圖書館系統組成的簡要分析。以及對域模型以及相應資料庫的建立。
知識點:Code First、EF 基本使用方法、ASP.NET Core 使用 EF Core 的配置方法。
一、對圖書館系統域模型的分析
一個圖書館系統需要有管理員、 學生、書架以及書籍
域模型,即用來儲存資料的模型。
在此域模型可以用以下結構建立:
二、專案結構
然後就可以開始建立該專案了:
Docker支援和身份驗證在以後可以自行新增,在此就不使用相應的支架特性。
所謂支架特性就是 VS2017 能夠自動為我們完成一系列的工作的特性,當然這部分工作也可以自行完成。
建立一個單元測試專案並引用 LibraryDemo 為以後的單元測試做準備。
然後正式開始圖書館專案的編寫:
為了辨識,我建立了這樣的資料夾結構,這裡的 Migrations 資料夾由後面提到的 EF 自動建立。
三、建立域模型
學位列舉:
1 public enum Degrees 2 { 3 CollegeStudent, 4 Postgraduate, 5 DoctorateDegree 6 }
圖書借閱狀態列舉:
1 public enum BookState 2 { 3 /// <summary> 4 /// 可借閱 5 /// </summary> 6 Normal, 7 8 /// <summary> 9 /// 管內閱覽 10 /// </summary> 11 Readonly, 12 13 /// <summary> 14 /// 已借出 15 /// </summary> 16 Borrowed, 17 18 /// <summary> 19 /// 被續借 20 /// </summary> 21 ReBorrowed, 22 23 /// <summary> 24 /// 被預約 25 /// </summary> 26 Appointed 27 }
學生資訊:
1 public class Student 2 { 3 [Key] 4 public string Id { get; set; } 5 [Required] 6 public string Name { get; set; } 7 8 /// <summary> 9 /// 學位,用來限制借書數目 10 /// </summary> 11 [Required] 12 public Degrees Degree { get; set; } 13 14 /// <summary> 15 /// 最大借書數目 16 /// </summary> 17 [Required] 18 public int MaxBooksNumber { get; set; } 19 20 /// <summary> 21 /// 已借圖書 22 /// </summary> 23 public IEnumerable<Book> KeepingBooks { get; set; } 24 25 /// <summary> 26 /// 預約的書 27 /// </summary> 28 public string AppointingBookBarCode { get; set; } 29 30 /// <summary> 31 /// 罰款 32 /// </summary> 33 public decimal Fine { get; set; } 34 }
在約定中,若不指定主鍵,則 EF 會使用 (類名)+ID 的方式指定或建立主鍵,在此使用 [Key] 指定主鍵,使用 [Required] 指定欄位為必須,這種可以為屬性新增在資料庫中的約束或者在檢視中的約束的修飾稱為 DataAnnotations 。
借閱書籍資訊:
1 /// <summary> 2 /// 用於借閱的書籍資訊 3 /// </summary> 4 public class Book 5 { 6 /// <summary> 7 /// 條形碼 8 /// </summary> 9 [Key] 10 public string BarCode { get; set; } 11 12 public string ISBN { get; set; } 13 14 /// <summary> 15 /// 書名 16 /// </summary> 17 [Required] 18 public string Name { get; set; } 19 20 /// <summary> 21 /// 取書號 22 /// </summary> 23 public string FetchBookNumber { get; set; } 24 25 /// <summary> 26 /// 所在書架 27 /// </summary> 28 //public Bookshelf Bookshelf { get; set; } 29 public ICollection<BookMiddle> BookMiddles { get; set; } 30 31 /// <summary> 32 /// 借出時間 33 /// </summary> 34 public DateTime? BorrowTime { get; set; } 35 36 /// <summary> 37 /// 到期時間 38 /// </summary> 39 public DateTime? MatureTime { get; set; } 40 41 /// <summary> 42 /// 預約最晚借書日期 43 /// </summary> 44 public DateTime? AppointedLatestTime { get; set; } 45 46 /// <summary> 47 /// 借閱狀態 48 /// </summary> 49 public BookState State { get; set; } 50 51 /// <summary> 52 /// 持有者,指定外來鍵 53 /// </summary> 54 public Student Keeper { get; set; } 55 }
書籍詳細資訊:
1 public class BookDetails 2 { 3 [Key] 4 public string ISBN { get; set; } 5 [Required] 6 public string Name { get; set; } 7 [Required] 8 public string Author { get; set; } 9 [Required] 10 public string Press { get; set; } 11 12 /// <summary> 13 /// 出版時間 14 /// </summary> 15 [Required] 16 public DateTime PublishDateTime{ get; set; } 17 18 /// <summary> 19 /// 書籍版本 20 /// </summary> 21 [Required] 22 public int Version { get; set; } 23 24 /// <summary> 25 /// 載體形態,包括頁數、媒介等資訊 26 /// </summary> 27 public string SoundCassettes { get; set; } 28 }
書架資訊:
1 public class Bookshelf 2 { 3 /// <summary> 4 /// 書架ID 5 /// </summary> 6 [Key] 7 public int BookshelfId { get; set; } 8 9 /// <summary> 10 /// 書架的書籍類別 11 /// </summary> 12 13 [Required] 14 public string Sort { get; set; } 15 /// <summary> 16 /// 最小取書號 17 /// </summary> 18 [Required] 19 public string MinFetchNumber { get; set; } 20 [Required] 21 public string MaxFetchNumber { get; set; } 22 23 /// <summary> 24 /// 書架位置 25 /// </summary> 26 [Required] 27 public string Location { get; set; } 28 29 /// <summary> 30 /// 全部藏書 31 /// </summary> 32 public ICollection<Book> Books { get; set; } 33 }
推薦書籍資訊的結構與書籍詳細資訊一致:
1 public class RecommendedBook 2 { 3 [Key] 4 public string ISBN { get; set; } 5 [Required] 6 public string Name { get; set; } 7 [Required] 8 public string Author { get; set; } 9 [Required] 10 public string Press { get; set; } 11 12 /// <summary> 13 /// 出版時間 14 /// </summary> 15 [Required] 16 public DateTime PublishDateTime { get; set; } 17 18 /// <summary> 19 /// 書籍版本 20 /// </summary> 21 [Required] 22 public int Version { get; set; } 23 24 /// <summary> 25 /// 載體形態,包括頁數、媒介等資訊 26 /// </summary> 27 public string SoundCassettes { get; set; } 28 }
登入所用資訊(此處 Student 模型使用 ASP.NET Core 內建的 Identity,而 Admin 使用自己建立的邏輯):
1 public class Student:IdentityUser 2 { 3 /// <summary> 4 /// 學號 5 /// </summary> 6 [ProtectedPersonalData] 7 [RegularExpression("[UI]\\d{9}")] 8 public override string UserName { get; set; } 9 10 [Required] 11 public string Name { get; set; } 12 13 /// <summary> 14 /// 學位,用來限制借書數目 15 /// </summary> 16 [Required] 17 public Degrees Degree { get; set; } 18 19 /// <summary> 20 /// 最大借書數目 21 /// </summary> 22 [Required] 23 public int MaxBooksNumber { get; set; } 24 25 /// <summary> 26 /// 已借圖書 27 /// </summary> 28 public IEnumerable<Book> KeepingBooks { get; set; } 29 30 /// <summary> 31 /// 預約的書 32 /// </summary> 33 public string AppointingBookBarCode { get; set; } 34 35 [StringLength(14,MinimumLength = 11)] 36 public override string PhoneNumber { get; set; } 37 38 /// <summary> 39 /// 罰款 40 /// </summary> 41 public decimal Fine { get; set; } 42 }
1 public class Admin 2 { 3 [Key] 4 public string UserName { get; set; } 5 6 [Required] 7 public string PhoneNumber { get; set; } 8 9 [Required] 10 public string Email { get; set; } 11 12 [Required] 13 public string Password { get; set; } 14 }
四、建立 DbContext
根據約定,建立 DbContext 類為 EF 提供建立的資料庫的結構:
1 public class StudentIdentityDbContext:IdentityDbContext<Student> 2 { 3 public StudentIdentityDbContext(DbContextOptions<StudentIdentityDbContext> options) : base(options) 4 { 5 } 6 }
1 public class AdminDbContext:DbContext 2 { 3 public AdminDbContext(DbContextOptions<AdminDbContext> options) : base(options) 4 { 5 } 6 7 public DbSet<Admin> Admins { get; set; } 8 }
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<Student> Students { get; set; } 11 public DbSet<RecommendedBook> RecommendedBooks { get; set; } 12 }
每個 DbContext 類代表一個數據庫,每個 DbSet<T> 代表一張表。而建構函式引數以及其形式為 ASP.NET Core 的依賴注入的約定形式。
五、根據約定配置資料庫,進行依賴注入
在 appsettings.json 中新增資料庫連線字串。
{ "ConnectionStrings": { "AdminDbContext": "Server=(localdb)\\mssqllocaldb;Database=AdminDbContext;Trusted_Connection=True;MultipleActiveResultSets=true", "LendingInfoDbContext": "Server=(localdb)\\mssqllocaldb;Database=LendingInfoDbContext;Trusted_Connection=True;MultipleActiveResultSets=true", "StudentIdentityDbContext": "Server=(localdb)\\mssqllocaldb;Database=StudentIdentityDbContext;Trusted_Connection=True;MultipleActiveResultSets=true" }, "Logging": { "LogLevel": { "Default": "Warning" } }, "AllowedHosts": "*" }
在 Startup.cs 中的 ConfigureServices 方法中對資料庫進行配置:
1 services.AddDbContext<LendingInfoDbContext>(options => 2 { 3 options.UseSqlServer(Configuration.GetConnectionString("LendingInfoDbContext")); 4 }); 5 services.AddDbContext<AdminDbContext>(options => 6 { 7 options.UseSqlServer(Configuration.GetConnectionString("AdminDbContext")); 8 }); 9 services.AddDbContext<StudentIdentityDbContext>(options => 10 { 11 options.UseSqlServer(Configuration.GetConnectionString("StudentIdentityDbContext")); 12 });
六、資料庫的遷移、建立及更新
然後在 pm控制檯 中新增遷移:
新增遷移的語法為 add-migration <遷移類名> -c <具體 DbContext 名>
分別執行以下程式碼:
1 cd LibraryDemo 2 add-migration Admin -c LibraryDemo.Data.AdminDbContext 3 add-migration LendingInfo -c LibraryDemo.Data.LendingInfoDbContext 4 add-migration StudentIdentity -c LibraryDemo.Data.StudentIdentityDbContext
執行 add-migration 命令會建立 Migrations 資料夾以及相應的遷移快照,此處的 AddSomeDetails 和 AddRequired 為後來我自己新增的內容:
顯示的類名為 <建立時間>_<遷移類名>,而實際的類名為 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.AdminDbContext 2 update-database -c LibraryDemo.Data.LendingInfoDbContext 3 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"