前言: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"