轉自:http://blog.163.com/m13864039250_1/blog/static/2138652482015283397609/ 用Fluent API 配置/映射屬性和類型 簡介 通常通過重寫派生DbContext 上的OnModelCreating 方法來訪問Code Firs ...
轉自:http://blog.163.com/m13864039250_1/blog/static/2138652482015283397609/
用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);