TinyFrame開篇:基於CodeFirst的ORM

前言html

作項目的這段時間,因爲比較忙,一直沒有機會閒下來思考。正好趁目前手頭活兒輕鬆點,就花了一兩天時間搭建了一個比較簡單的框架,名稱暫時就叫作:TinyFrame吧。顧名思義,就是微框架的意思。雖然這個框架很小,可是包含了ORM,IOC,AOP以及Restful service支持。真所謂麻雀雖小,可是五臟俱全。今天主要講解的就是基於CodeFirst的ORM實現方式。數據庫

在傳統的開發思惟中,咱們通常都是先創建好數據庫,而後佈局各類Model類,最後調用。這種方式被稱爲DbFirst方式,也就是先有DataBase,而後再佈局邏輯。而CodeFirst相反,是先創建好model,創建好須要的字段,而後由EntityFramework來自動生成數據庫的表,看上去有點像DDD的開發方式。app

下面就開始吧。框架

實現步驟解說less

首先,咱們以一個簡易的圖書借閱爲例:在圖書館中,圖書是有分類的,好比屬於計算機類,仍是屬於文學類;圖書是有存放位置的,好比存放於圖書館一樓,仍是二樓;圖書是有自身屬性的,好比圖書名稱,圖書做者等;圖書借閱是須要人來參與的,因此會有學生這個集體;圖書借閱須要知道誰借了什麼書,何時到期等,是須要一個數據存儲中心的。因此,綜上所述,能夠創建以下的Model來。ide

來看看數據關係圖:佈局

QQ截圖20140306223542

這裏能夠看到總共有五張表,他們的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:          }
而後重寫其OnModelCreating方法,將Mapper類加入到ModelBuilder中:
   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注入。

相關文章
相關標籤/搜索