2012年12月11日,Entity Framework已經發布了Entity Framework 6 Alpha2,因項目需要,目前已使用了其中的兩個特性,今天就來介紹一下第一個特性:全局性地自定義Code First約定(Custom Code First Conventions)。
應用場景
場景一:EF Code First默認使用類名作為表名,如果我們需要給表名加個前綴,例如將類名Category映射到表Shop_Category、將Product映射到Shop_Product,在EF 6之前,只能一個一個地使用Data Annotation( [Table("Shop_Category")] )或者Fluent API( ToTable("Shop_Category") )一個一個地指定表名。在實體類數量比較多的情況下,工作量就比較大了。
場景二:EF Code First默認將String類型的屬性映射為nvarchar(max),但是你可能想要將它映射為nvarchar(255),那么也可以全局性地自定義,而不需要在所有String類型的屬性上面使用[MaxLength(255)]進行配置。
當然使用場景不只這兩個,如改寫主外鍵的映射規則等等。簡而言之,Custom Code First Conventions使你能夠改寫Entity Framework模型與數據庫之間默認的映射規則。
自定義Code First約定的方式
Code First的默認映射規則可以通過三種方式進行自定義,分別是:Lightweight Conventions(輕量級約定)、Configuration Conventions(配置型約定)、Model-based Conventions(基於模型的配置)。實現上的復雜度由Lightweight Conventions開始依次遞增,當然,實現的自由度也依次增大。
1 public class ProductContext : DbContext 2 { 3 static ProductContext() 4 { 5 Database.SetInitializer( 6 new DropCreateDatabaseIfModelChanges<ProductContext>()); 7 } 8 9 public DbSet<Product> Products { get; set; } 10 } 11 12 public class Product 13 { 14 public int ProductId { get; set; } 15 public string Name { get; set; } 16 public string? Description {get; set;} 17 }
在這個模型中,默認情況下,EF會生成以下的數據表結構:
| 表名:Product |
||
| 字段名稱 | 類型 | 是否可空 |
| ProductId | int | 主鍵 |
| Name |
nvarchar(max) | 否 |
| Description |
nvarchar(max) | 是 |
我們以添加表前綴為例,來說明自定義Code First約定的前兩種方式。
Lightweight Conventions
重寫ProductContext的OnModelCreating(DbModelBuilder modelBuilder)方法:
1 public class ProductContext : DbContext 2 { 3 protected override void OnModelCreating(DbModelBuilder modelBuilder) 4 { 5 modelBuilder.Types().Configure(entity => entity.ToTable("Shop_" + entity.ClrType.Name)); 6 } 7 }
Lightweight Conventions是最簡單的實現方式,大部分的全局配置需求都能夠以這種方式來實現。
Configuration Conventions
實現IConfigurationConvention<Type, EntityTypeConfiguration>接口,然后重寫ProductContext的OnModelCreating(DbModelBuilder modelBuilder)方法。
1 public class DefaultTableConvention 2 : IConfigurationConvention<Type, EntityTypeConfiguration> 3 { 4 public void Apply( 5 Type type, 6 Func<EntityTypeConfiguration> configuration) 7 { 8 TableAttribute[] tableAttributes = (TableAttribute[])type.GetCustomAttributes(typeof(TableAttribute), false); 9 10 if (tableAttributes.Length == 0) 11 { 12 configuration().ToTable("Shop_" + type.Name); 13 } 14 } 15 } 16 17 public class ProductContext : DbContext 18 { 19 protected override void OnModelCreating(DbModelBuilder modelBuilder) 20 { 21 modelBuilder.Conventions.Add<DefaultTableConvention>(); 22 } 23 }
從上面的代碼可以看到,Configuration Conventions的方式需要自行判斷實體是否使用TableAttribute指定了表名,如果是,則不使用全局的配置。而Lightweight Conventions則默認優先使用TableAttribute指定的表名。可以看出,Configuration Conventions實現起來相對繁瑣了一點,但是自由度也更高。
IConfigurationConvention接口有兩個類型參數:TMemberInfo和TConfiguration。它們用來過濾你想自定義約定的模型元素。
第一個類型參數,TMemberInfo,可以是一下兩個值:
-
Type(System)
-
PropertyInfo(System.Reflection)
第二個類型參數,TConfiguration,可以是一下任意一種。
-
ModelConfiguration (System.Data.Entity.ModelConfiguration.Configuration)
-
EntityTypeConfiguration (System.Data.Entity.ModelConfiguration.Configuration.Types)
-
PropertyConfiguration (System.Data.Entity.ModelConfiguration.Configuration.Properties)
-
NavigationPropertyConfiguration (System.Data.Entity.ModelConfiguration.Configuration.Properties.Navigation)
-
PrimitivePropertyConfiguration (System.Data.Entity.ModelConfiguration.Configuration.Properties.Primitive)
-
DateTimePropertyConfiguration (System.Data.Entity.ModelConfiguration.Configuration.Properties.Primitive)
-
DecimalPropertyConfiguration (System.Data.Entity.ModelConfiguration.Configuration.Properties.Primitive)
-
LengthPropertyConfiguration (System.Data.Entity.ModelConfiguration.Configuration.Properties.Primitive)
-
BinaryPropertyConfiguration (System.Data.Entity.ModelConfiguration.Configuration.Properties.Primitive)
-
StringPropertyConfiguration (System.Data.Entity.ModelConfiguration.Configuration.Properties.Primitive)
-
-
注意,Type和PropertyConfiguration(以及它的子類)不能混用,否則Configuration Conventions將不會生效。
增加自定義的Data Annotation
利用Custom Code First Conventions,我們還可以擴展自己的Data Annotation。例如,增加一個EmailAttribute特性,然后在Lightweight Conventions或者Configuration Conventions中,判斷屬性是否應用了EmailAttribute特性;如果是,則將列名映射為“Email”,列類型映射為“nvarchar(255)”, 達到了[Column("Email")]和[MaxLength(255)]共同作用的效果。
更詳細的信息,請參考http://msdn.microsoft.com/en-us/data/jj819164.aspx
