上一篇,主要學習了一些CodeFirst 中修改默認規約的基本配置。在文章最后,出現了個新的名詞:復雜類型。什么是復雜類型呢?
書中說道:“復雜類型也可視作值類型(?)可以作為附加屬性添加到其他類。復雜類型與實體類型的區別在於復雜類型沒有其自己的鍵。它是依賴於其"宿主"類型跟蹤變化 和持久化。一個沒有Key屬性的類型,並且作為屬性映射到一個或多個類型中,Code First就會將其視作為復雜類型。Code First將預設復雜類型的屬性出現在宿主類型映射到數據庫的表中。”
說簡單一點就是,項目中有個類A,這個A,會被其他類引用到比如:實體類B 和 實體類C,但是建立數據庫的時候,我們不想為這個分割類A建立表,而是把A類中的屬性等建立到 B 和 C 映射的表中,這時候,我們管 A 叫做復雜類型。
來看一下書中的解釋和例子:
比如有如下類:
public class Person { public int PersonId { get; set; } public int SocialSecurityNumber { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string StreetAddress { get; set; } public string City { get; set; } public string State { get; set; } public string ZipCode { get; set; } }
這時候,增加Address類作為分割類,不僅可以簡化Person類,而且還可以提高Address 的利用,分離后的代碼如下:
public class Address { public int AddressId { get; set; } public string StreetAddress { get; set; } public string City { get; set; } public string State { get; set; } public string ZipCode { get; set; } }
public class Person { public int PersonId { get; set; } public int SocialSecurityNumber { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public Address Address { get; set; } }
如果這樣做的話,CodeFirst 使用默認規則,肯定會產生一個單獨的表:Addresses。而我們希望的是 Address中的屬性成為表Person中的字段。這時候,我們需要告訴EF,Address類是個復雜類型,就能解決我們現在遇到的問題。
一、使用默認規則:
使用負責類型默認規則,我們需要遵循三個原則:
1、復雜類型無Key屬性
2、復雜類型只包含原始屬性
3、用作其他類的屬性時,屬性必須是一個單一實例,不能用於集合類型
上例代碼中,我們需要注釋掉 :
//public int AddressId { get; set; }
然后在Person中增加構造函數:
public Person() { Address = new Address(); }
這樣,運行程序的話,Address類就會被認作是 復雜類型了。
二、DataAnnotation 方式:
DataAnnotation 更加簡單 只需要在復雜類型的類上增加屬性標簽[ComplexType]
[ComplexType] public class Address { public string StreetAddress { get; set; } public string City { get; set; } public string State { get; set; } public string ZipCode { get; set; } }
三、FluntAPI 方式:
protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.ComplexType<Address>(); base.OnModelCreating(modelBuilder); }
生成的表結構如下圖:

我們會發現,生成的表結構的字段名稱和類型均是EF默認的,我們當然也可以對其進行配置,我們來用FluntAPI進行配置一下:
protected override void OnModelCreating(DbModelBuilder modelBuilder) { //設置Person 類的表名為 t_People modelBuilder.Entity<Person>().ToTable("t_People"); //設置 PersonId 為主鍵且自動增長 modelBuilder.Entity<Person>().HasKey(p => p.PersonId) .Property(p => p.PersonId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); //設置Firstname 和 Lastname 的長度為 20 modelBuilder.Entity<Person>().Property(p => p.FirstName).HasMaxLength(20); modelBuilder.Entity<Person>().Property(p => p.LastName).HasMaxLength(20); //設置Address 為復雜類型嗎,並設置字段名稱及長度 modelBuilder.ComplexType<Address>().Property(a => a.City).HasColumnName("City").HasMaxLength(20); modelBuilder.ComplexType<Address>().Property(a => a.State).HasColumnName("State").HasMaxLength(20); modelBuilder.ComplexType<Address>().Property(a => a.StreetAddress).HasColumnName("Steet").HasMaxLength(30); modelBuilder.ComplexType<Address>().Property(a => a.ZipCode).HasColumnName("ZipCode").HasMaxLength(10); base.OnModelCreating(modelBuilder); }
生成的表結構如下圖:

四、復雜類型的嵌套
上面已經指出,復雜類型應該只包括原始屬性。但如果在復雜類型中繼續引用一個類會產生什么樣的結果呢?繼續書中的例子:
再創建了兩個新類,PersonalInfo 類 和 Measuremet 類,PersonalInfo 包含有兩個Measurement 屬性。
注意兩個類都沒有標識屬性。我們的意圖是兩個類都成為復雜類型。PersonalInfo復雜類型使用Measurment復雜類型,這就是所謂的嵌套復雜類型。
public class PersonalInfo { public Measurement Weight { get; set; } public Measurement Height { get; set; } public string DietryRestrictions { get; set; } } public class Measurement { public decimal Reading { get; set; } public string Units { get; set; } }
向Person類中添加新的PersonInfo屬性:
public PersonalInfo Info { get; set; }
修改Person類的構造函數:
public Person() { Address = new Address(); Info = new PersonalInfo { Weight = new Measurement(), Height = new Measurement() }; }
然后,我們去掉先前配置的那些 DataAnnotation 和 FluntAPI,采用默認的配置方式,我們運行一下程序,會出現以下錯誤:

實體類"PersonInfo"沒有定義鍵。請為此實體類定義鍵。
Code First 並沒有將PersonalInfo識雖為復雜類型。原因是打破了規則:復雜類型必須只包含原生類型。在PersonalInfo類中有兩個Measurement類型的屬性。由於這是非原生類型,規則不能將PersonalInfo作為復雜類型
解決方法:對PersonalInfo 類 配置 [ComplexType] 屬性,就能夠將屬性建立到表中,不需要配置配置Measurement類,因為它遵循復雜類的規則。

