Code First約定-Fluent API配置

使用Fluent API 配置/映射屬性和類型
簡介
一般經過重寫派生DbContext 上的OnModelCreating 方法來訪問Code First Fluent API。如下示例旨在顯示如何使用 Fluent API 執行各類任務,您能夠將代碼複製出來並進行自定義,使之適用於您的模型。

屬性映射
Property 方法用於爲每一個屬於實體或複雜類型的屬性配置特性。Property 方法用於獲取給定屬性的配置對象。配置對象上的選項特定於要配置的類型;例如,IsUnicode 只能用於字符串屬性。

配置主鍵
要顯式將某個屬性設置爲主鍵,可以使用 HasKey 方法。在如下示例中,使用了 HasKey 方法對 OfficeAssignment 類型配置 InstructorID 主鍵。

modelBuilder.Entity<OfficeAssignment>().HasKey(t =>t.InstructorID);

 

配置組合主鍵
如下示例配置要做爲Department 類型的組合主鍵的DepartmentID 和 Name 屬性。

modelBuilder.Entity<Department>().HasKey(t => new { t.DepartmentID, t.Name });

 
關閉數值主鍵的標識
如下示例將DepartmentID 屬性設置爲System.ComponentModel.DataAnnotations.DatabaseGeneratedOption.None,以指示該值不禁數據庫生成。

modelBuilder.Entity<Department>().Property(t =>t.DepartmentID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);

 
指定屬性的最大長度
在如下示例中,Name屬性不該超過 50 個字符。若是其值超過 50 個字符,則出現 DbEntityValidationException 異常。若是 Code First 基於此模型建立數據庫,它還會將 Name 列的最大長度設置爲50 個字符。

modelBuilder.Entity<Department>().Property(t =>t.Name).HasMaxLength(50);

 

將屬性配置爲必需
在下面的示例中,Name屬性是必需的。若是不指定 Name,則出現 DbEntityValidationException 異常。若是 Code First 基於此模型建立數據庫,則用於存儲此屬性的列將不可爲空。

modelBuilder.Entity<Department>().Property(t =>t.Name).IsRequired();

 
指定不將CLR 屬性映射到數據庫中的列
如下示例顯示如何指定CLR 類型的屬性不映射到數據庫中的列。

modelBuilder.Entity<Department>().Ignore(t => t.Budget);

 
將CLR 屬性映射到數據庫中的特定列
如下示例將Name CLR 屬性映射到DepartmentName 數據庫列。

modelBuilder.Entity<Department>().Property(t =>t.Name).HasColumnName("DepartmentName");

 

重命名模型中未定義的外鍵
若是您選擇不對CLR 類型定義外鍵,但但願指定它在數據庫中應使用的名稱,請編碼以下:

modelBuilder.Entity<Course>()

        .HasRequired(c => c.Department)

        .WithMany(t => t.Courses)

        .Map(m => m.MapKey("ChangedDepartmentID"));

 

配置字符串屬性是否支持Unicode 內容
默認狀況下,字符串爲Unicode(SQLServer 中的nvarchar)。您可使用IsUnicode 方法指定字符串應爲varchar 類型。

modelBuilder.Entity<Department>()

        .Property(t => t.Name)

        .IsUnicode(false);

 

配置數據庫列的數據類型
HasColumnType 方法支持映射到相同基本類型的不一樣表示。使用此方法並不支持在運行時執行任何數據轉換。請注意,IsUnicode 是將列設置爲 varchar 的首選方法,由於它與數據庫無關。

modelBuilder.Entity<Department>()

        .Property(p => p.Name)

        .HasColumnType("varchar");

 

配置複雜類型的屬性
對複雜類型配置標量屬性有兩種方法。

能夠對ComplexTypeConfiguration 調用Property。

modelBuilder.ComplexType<Details>()

        .Property(t => t.Location)

        .HasMaxLength(20);

 

也可使用點表示法訪問複雜類型的屬性。

modelBuilder.Entity<OnsiteCourse>()

        .Property(t => t.Details.Location)

        .HasMaxLength(20);

 

將屬性配置爲用做樂觀併發令牌
要指定實體中的某個屬性表示併發令牌,可以使用 ConcurrencyCheck 特性或 IsConcurrencyToken 方法。

modelBuilder.Entity<OfficeAssignment>()

        .Property(t => t.Timestamp)

        .IsConcurrencyToken();

 

也可使用IsRowVersion 方法將屬性配置爲數據庫中的行版本。將屬性設置爲行版本會自動將它配置爲樂觀併發令牌。

modelBuilder.Entity<OfficeAssignment>()

        .Property(t => t.Timestamp)

        .IsRowVersion();

 

類型映射
將類指定爲複雜類型
按約定,沒有指定主鍵的類型將被視爲複雜類型。在一些狀況下,Code First 不會檢測複雜類型(例如,若是您有名爲「ID」的屬性,但不想將它用做主鍵)。在此類狀況下,您將使用 Fluent API 顯式指定某類型是複雜類型。

modelBuilder.ComplexType<Details>();

 

指定不將CLR 實體類型映射到數據庫中的表
如下示例顯示如何排除一個 CLR 類型,使之不映射到數據庫中的表。

modelBuilder.Ignore<OnlineCourse>();

 

將CLR 實體類型映射到數據庫中的特定表
Department 的全部屬性都將映射到名爲 t_ Department 的表中的列。

modelBuilder.Entity<Department>().ToTable("t_Department");

 

您也能夠這樣指定架構名稱:

modelBuilder.Entity<Department>().ToTable("t_Department", "school");

 
映射「每一個層次結構一張表(TPH)」繼承
在 TPH 映射情形下,繼承層次結構中的全部類型都將映射到同一個表。鑑別器列用於標識每行的類型。使用 Code First 建立模型時,TPH 參與繼承層次結構的類型所用的默認策略。默認狀況下,鑑別器列將添加到名爲「Discriminator」的表,且層次結構中每一個類型的 CLR 類型名稱都將用做鑑別器值。可使用 Fluent API 修改默認行爲。

modelBuilder.Entity<Course>()

        .Map<Course>(m=> m.Requires("Type").HasValue("Course"))

        .Map<OnsiteCourse>(m=> m.Requires("Type").HasValue("OnsiteCourse"));

 

映射「每一個類型一張表(TPT)」繼承
在 TPT 映射情形下,全部類型分別映射到不一樣的表。僅屬於某個基類型或派生類型的屬性存儲在映射到該類型的一個表中。映射到派生類型的表還會存儲一個將派生表與基表聯接的外鍵。

modelBuilder.Entity<Course>().ToTable("Course");

modelBuilder.Entity<OnsiteCourse>().ToTable("OnsiteCourse");

 

映射「每一個具體類一張表(TPC)」繼承
在 TPC 映射情形下,層次結構中的全部非抽象類型分別映射到不一樣的表。映射到派生類的表與映射到數據庫中基類的表並沒有關係。類的全部屬性(包括繼承屬性)都將映射到相應表的列。

調用MapInheritedProperties 方法來配置每一個派生類型。MapInheritedProperties 將繼承自基類的全部屬性從新映射到派生類的表中的新列。

注意:由於屬於TPC 繼承層次結構的表並不使用同一個主鍵,所以,若是您讓數據庫生成的值具備相同標識種子,則在映射到子類的表中執行插入操做時,會產生重複的實體鍵。要解決此問題,能夠爲每一個表指定不一樣的初始種子值,或關閉主鍵屬性的標識。當使用 Code First 時,標識就是整數鍵屬性的默認值。

modelBuilder.Entity<Course>()

  .Property(c => c.CourseID)

  .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);

 

modelBuilder.Entity<OnsiteCourse>().Map(m =>

{

  m.MapInheritedProperties();

  m.ToTable("OnsiteCourse");

});

 

modelBuilder.Entity<OnlineCourse>().Map(m =>

{

  m.MapInheritedProperties();

  m.ToTable("OnlineCourse");

});

 

將實體類型的CLR 屬性映射到數據庫中的多個表(實體拆分)
實體拆分容許一個實體類型的屬性分散在多個表中。在如下示例中,Department 實體拆分到兩個表中:Department 和DepartmentDetails。實體拆分經過屢次調用 Map 方法將一部分屬性映射到特定表。

modelBuilder.Entity<Department>()

.Map(m=>

{

  m.Properties(t => new{ t.DepartmentID, t.Name });

  m.ToTable("Department");

})

.Map(m=>

{

  m.Properties(t=> new { t.DepartmentID, t.Administrator,t.StartDate, t.Budget });

  m.ToTable("DepartmentDetails");

 });

 

將多個實體類型映射到數據庫中的一個表(表拆分)
如下示例將使用同一個主鍵的兩個實體類型映射到同一個表。

modelBuilder.Entity<OfficeAssignment>()

  .HasKey(t => t.InstructorID);

modelBuilder.Entity<Instructor>()

    .HasRequired(t => t.OfficeAssignment)

    .WithRequiredPrincipal(t =>t.Instructor);

modelBuilder.Entity<Instructor>().ToTable("Instructor");

modelBuilder.Entity<OfficeAssignment>().ToTable("Instructor");

 

使用FluentAPI配置關係
簡介
使用FluentAPI配置關係的時候,首先要得到一個EntityTypeConfiguration實例,而後使用其上的HasRequired, HasOptional或者 HasMany方法來指定當前實體參與的關係類型。HasRequired 和HasOptional方法須要一個lambda表達式來指定一個導航屬性,HasMany方法須要一個lambda表達式指定一個集合導航屬性。而後可使用WithRequired, WithOptional和WithMany方法來指定反向導航屬性,這些方法有不帶參數的重載用來指定單向導航。

以後還可使用HasForeignKey方法來指定外鍵屬性。

配置【必須-可選】關係(1-0..1)
OfficeAssignment的鍵屬性不符合命名約定,因此須要咱們顯式指定。下面的關係代表,OfficeAssignment的Instructor必須存在,可是Instructor的OfficeAssignment不是必須存在的。

modelBuilder.Entity<OfficeAssignment>()

  .HasKey(t => t.InstructorID);

 

// Map one-to-zero or one relationship

modelBuilder.Entity<OfficeAssignment>()

  .HasRequired(t => t.Instructor)

  .WithOptional(t => t.OfficeAssignment);

配置兩端都是必須的關係(1-1)
大多數狀況下,EF都能推斷哪個類型是依賴項或者是主體項。然而當關系的兩端都是必須的或者都是可選的,那麼EF就不能識別依賴項或者是主體項。若是關係兩端都是必須的,那麼在HasRequired方法後使用WithRequiredPrincipal或者WithRequiredDependent來肯定主體。若是關係兩端都是可選的,那麼在HasRequired方法後使用WithOptionalPrincipal和WithOptionalDependent。

 

modelBuilder.Entity<OfficeAssignment>()

  .HasKey(t => t.InstructorID);

 

modelBuilder.Entity<Instructor>()

  .HasRequired(t => t.OfficeAssignment)

  .WithRequiredPrincipal(t => t.Instructor);

 
配置多對多關係
下面的代碼配置了一個多對多關係,CodeFirst會使用命名約定來建立鏈接表,命名約定會使用Course_CourseID 和 Instructor_InstructorID做爲鏈接表的列。

 

modelBuilder.Entity<Course>()

        .HasMany(t => t.Instructors)

        .WithMany(t => t.Courses);

若是想指定鏈接表的表名和列名,須要使用Map方法,以下:

modelBuilder.Entity<Course>()

        .HasMany(t => t.Instructors)

        .WithMany(t => t.Courses)

        .Map(m =>

        {

          m.ToTable("CourseInstructor");

          m.MapLeftKey("CourseID");

          m.MapRightKey("InstructorID");

        });

 
配置單向導航屬
所謂單向導航屬性指的是隻在關係的一端定義了導航屬性。按照約定,CodeFirst將單向導航理解爲一對多關係,若是須要一對一的單向導航屬性,須要使用以下方法:

modelBuilder.Entity<OfficeAssignment>()

  .HasKey(t => t.InstructorID);

 

modelBuilder.Entity<Instructor>()

  .HasRequired(t => t.OfficeAssignment)

  .WithRequiredPrincipal();

 

啓用級聯刪除
使用WillCascadeOnDelete方法來配置關係是否容許級聯刪除。若是外鍵是不可空的,CodeFirst默認會設置級聯刪除;不然,不會設置級聯刪除,當主體被刪除後,外鍵將會被置空。

可使用以下代碼移除此約定:

modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();

modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();

下面的代碼片斷配置爲外鍵不能爲空,並且禁用了級聯刪除。

 

modelBuilder.Entity<Course>()

  .HasRequired(t => t.Department)

  .WithMany(t => t.Courses)

  .HasForeignKey(d => d.DepartmentID)

  .WillCascadeOnDelete(false);

配置組合外鍵
下面的代碼配置了組合外鍵

modelBuilder.Entity<Department>()

  .HasKey(d => new{ d.DepartmentID, d.Name });

 

// Composite foreign key

modelBuilder.Entity<Course>()

    .HasRequired(c => c.Department)

    .WithMany(d => d.Courses)

    .HasForeignKey(d => new { d.DepartmentID, d.DepartmentName });

 
配置不符合命名約定的外鍵屬性
SomeDepartmentID屬性不符合外鍵命名約定,須要使用以下方法將其設置爲外鍵屬性:

modelBuilder.Entity<Course>()

         .HasRequired(c => c.Department)

         .WithMany(d => d.Courses)

         .HasForeignKey(c =>c.SomeDepartmentID);
相關文章
相關標籤/搜索