前言html
作項目的這段時間,因爲比較忙,一直沒有機會閒下來思考。正好趁目前手頭活兒輕鬆點,就花了一兩天時間搭建了一個比較簡單的框架,名稱暫時就叫作:TinyFrame吧。顧名思義,就是微框架的意思。雖然這個框架很小,可是包含了ORM,IOC,AOP以及Restful service支持。真所謂麻雀雖小,可是五臟俱全。今天主要講解的就是基於CodeFirst的ORM實現方式。數據庫
在傳統的開發思惟中,咱們通常都是先創建好數據庫,而後佈局各類Model類,最後調用。這種方式被稱爲DbFirst方式,也就是先有DataBase,而後再佈局邏輯。而CodeFirst相反,是先創建好model,創建好須要的字段,而後由EntityFramework來自動生成數據庫的表,看上去有點像DDD的開發方式。app
下面就開始吧。框架
實現步驟解說less
首先,咱們以一個簡易的圖書借閱爲例:在圖書館中,圖書是有分類的,好比屬於計算機類,仍是屬於文學類;圖書是有存放位置的,好比存放於圖書館一樓,仍是二樓;圖書是有自身屬性的,好比圖書名稱,圖書做者等;圖書借閱是須要人來參與的,因此會有學生這個集體;圖書借閱須要知道誰借了什麼書,何時到期等,是須要一個數據存儲中心的。因此,綜上所述,能夠創建以下的Model來。ide
來看看數據關係圖:佈局
這裏能夠看到總共有五張表,他們的Model類建立以下:ui
Book實體類,主要用來描述圖書的自己屬性,一本書只能放置在一個地方,只能屬於一個種類,有BookTypeID和BookPlaceID做爲外鍵約束:this
1: namespace BookStore.Data
2: {
3: public class Book
4: {
5: public Book()
6: {
7: BookType = new BookType();
8: BookPlace = new BookPlace();
9: }
10:
11: public int ID { get; set; }
12: public string Name { get; set; }
13: public string Author { get; set; }
14: public string Publishment { get; set; }
15: public int BookTypeID { get; set; }
16: public int BookPlaceID { get; set; }
17:
18: public BookType BookType { get; set; }
19: public BookPlace BookPlace { get; set; }
20: }
21: }
BookLend實體類,主要用來描述哪位學生借閱了什麼書籍,一個學生能夠借閱多本書籍,因此裏面放了一個Student實體類,對應着多個Books,有BookID和StudentID做爲外鍵約束:spa
1: namespace BookStore.Data
2: {
3: public class BookLend
4: {
5: public BookLend()
6: {
7: Student = new Student();
8: Books = new List<Book>();
9: }
10: public int ID { get; set; }
11: public int LendDays { get; set; }
12: public DateTime LendDate { get; set; }
13: public int BookID { get; set; }
14: public int StudentID { get; set; }
15:
16: public IList<Book> Books { get; set; }
17: public Student Student { get; set; }
18: }
19: }
BookPlace實體類,主要用來描述圖書放置的位置:
1: namespace BookStore.Data
2: {
3: public class BookPlace
4: {
5: public int ID { get; set; }
6: public string Position { get; set; }
7: }
8: }
BookType實體類,主要用來描述圖書種類:
1: namespace BookStore.Data
2: {
3: public class BookType
4: {
5: public int ID { get; set; }
6: public string Name { get; set; }
7: }
8: }
Student實體類,主要用來描述學生屬性:
1: namespace BookStore.Data
2: {
3: public class Student
4: {
5: public int ID { get; set; }
6: public string Name { get; set; }
7: public string Number { get; set; }
8: public string Major { get; set; }
9: public string TelPhone { get; set; }
10: public string Address { get; set; }
11: public Gender Gender { get; set; }
12: }
13: }
這裏還有個附加的枚舉性別選項:
1: namespace BookStore.Data
2: {
3: public enum Gender
4: {
5: Male = 0, //男
6: Female = 1 //女
7: }
8: }
到這裏,咱們的Model就建立好了。
而後咱們來建立ModelMapper。這種Mapper主要是用來設置各個字段的屬性的。
BookMapper類:
1: namespace BookStore.Data
2: {
3: public class BookMapper:EntityTypeConfiguration<Book>
4: {
5: public BookMapper()
6: {
7: this.ToTable("Book");
8:
9: this.HasKey(c => c.ID);
10: this.Property(c => c.ID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
11: this.Property(c => c.ID).IsRequired();
12:
13: this.Property(c => c.Name).HasMaxLength(255);
14: this.Property(c => c.Name).IsRequired();
15:
16: this.Property(c => c.Author).HasMaxLength(255);
17: this.Property(c => c.Author).IsOptional();
18:
19: this.Property(c => c.Publishment).HasMaxLength(255);
20: this.Property(c => c.Publishment).IsRequired();
21:
22: this.HasRequired(c => c.BookType).WithMany().HasForeignKey(s => s.BookTypeID);
23: this.HasRequired(c => c.BookPlace).WithMany().HasForeignKey(s => s.BookPlaceID);
24:
25: }
26: }
27: }
第7行代碼表示:對於Book實體類來講,最終將會在數據庫中爲其建立名稱爲Book的數據表。
第9行代碼表示:這是一個主鍵。
第10行代碼表示:這個主鍵是自增的。
第11行代碼表示:這個主鍵是不能爲空的。
第13,14行代碼表示:這是一個普通屬性,字段最大長度爲255,不能爲空。
第16,17行代碼表示:這是一個普通屬性,字段最大長度爲255,能夠爲空。
第22,23行代碼表示:在BookType表和BookPlace表中存在外鍵依賴。須要注意的是,若是Model類中定義了BookTypeID和BookPlaceID,這裏必定要用HasForeignKey方法來指明。不然,能夠利用Map方法來隱式指定。
接下來的Mapper就不一一解釋了:
BookLendManager類:
1: namespace BookStore.Data
2: {
3: public class BookLendMapper : EntityTypeConfiguration<BookLend>
4: {
5: public BookLendMapper()
6: {
7: this.ToTable("BookLend");
8:
9: this.HasKey(c=>c.ID);
10: this.Property(c => c.ID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
11: this.Property(c => c.ID).IsRequired();
12:
13: this.Property(c => c.LendDays).IsRequired();
14:
15: this.Property(c => c.LendDate).IsRequired();
16: this.Property(s => s.LendDate).HasColumnType("smalldatetime");
17:
18: this.HasRequired(c => c.Books).WithMany().HasForeignKey(s=>s.BookID);
19: this.HasRequired(c => c.Student).WithMany().HasForeignKey(s =>s.StudentID);
20: }
21: }
22: }
BookPlaceMapper類:
1: namespace BookStore.Data
2: {
3: public class BookPlaceMapper : EntityTypeConfiguration<BookPlace>
4: {
5: public BookPlaceMapper()
6: {
7: this.ToTable("BookPlace");
8:
9: this.HasKey(c=>c.ID);
10: this.Property(c => c.ID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
11: this.Property(c => c.ID).IsRequired();
12:
13: this.Property(c => c.Position).IsRequired();
14: this.Property(c => c.Position).HasMaxLength(255);
15: }
16: }
17: }
BookTypeMapper類:
1: namespace BookStore.Data.Mapper
2: {
3: public class BookTypeMapper : EntityTypeConfiguration<BookType>
4: {
5: public BookTypeMapper()
6: {
7: this.ToTable("BookType");
8:
9: this.HasKey(c => c.ID);
10: this.Property(c => c.ID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
11: this.Property(c => c.ID).IsRequired();
12:
13: this.Property(c => c.Name).IsRequired();
14: this.Property(c => c.Name).HasMaxLength(255);
15: }
16: }
17: }
StudentMapper類:
1: namespace BookStore.Data
2: {
3: public class StudentMapper : EntityTypeConfiguration<Student>
4: {
5: public StudentMapper()
6: {
7: this.ToTable("Student");
8:
9: this.HasKey(c=>c.ID);
10: this.Property(c => c.ID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
11: this.Property(c => c.ID).IsRequired();
12:
13: this.Property(c => c.Name).IsRequired();
14: this.Property(c => c.Name).HasMaxLength(255);
15:
16: this.Property(c => c.Number).IsRequired();
17: this.Property(c => c.Number).HasMaxLength(12);
18:
19: this.Property(c => c.Major).IsOptional();
20: this.Property(c => c.Major).HasMaxLength(255);
21:
22: this.Property(c => c.Address).IsOptional();
23: this.Property(c => c.Address).HasMaxLength(255);
24:
25: this.Property(c => c.Gender).IsRequired();
26: }
27: }
28: }
當咱們寫完這些mapper的時候,工做已經完成一大半了,剩下的就是如何將其自動建立到數據庫中的問題了。
這裏咱們須要用到EF的SetInitializer方法:
首先建立一個BookContext,繼承自DbContext類,而後在構造中書寫如下代碼,以便實現數據實體類遷移:
1: public BookContext()
2: : base("BookConnection")
3: {
4: Configuration.ProxyCreationEnabled = false;
5: Configuration.LazyLoadingEnabled = false;
6: Database.SetInitializer(new MigrateDatabaseToLatestVersion<BookContext, BookContextMConfig>());
7: }
1: protected override void OnModelCreating(DbModelBuilder modelBuilder)
2: {
3: modelBuilder.Configurations.Add(new BookMapper());
4: modelBuilder.Configurations.Add(new BookLendMapper());
5: modelBuilder.Configurations.Add(new BookTypeMapper());
6: modelBuilder.Configurations.Add(new BookPlaceMapper());
7: modelBuilder.Configurations.Add(new StudentMapper());
8: modelBuilder.Configurations.Add(new ManagerMapper());
9: base.OnModelCreating(modelBuilder);
10: }
其中須要注意BookContextMConfig這個類,它須要繼承DbMigrationsConfiguration類:
1: public class BookContextMConfig : DbMigrationsConfiguration<BookContext>
2: {
3: public BookContextMConfig()
4: {
5: this.AutomaticMigrationDataLossAllowed = true;
6: this.AutomaticMigrationsEnabled = true;
7: }
8: }
這樣咱們的一個完整的ORM就封裝好了.經過訪問BookContext對象,咱們就能夠從數據庫得到與之對應的實體類.
最後還要注意,須要將數據庫鏈接寫到配置文件中:
<connectionStrings> <add name="BookConnection" connectionString="server=180-server;database=BookConnection;uid=sa;pwd=*****" providerName="System.Data.SqlClient" /> </connectionStrings>
當這個實例運行起來的時候,咱們就能夠看到數據庫中的表被自動建立了.而且實例化BookContext對象以後,咱們能夠獲取到全部的Books,Students,BookTypes,BookPlaces,BookLends。
下一節主要來講明如何進行IOC注入。