Fluent API配置:
前面我們已經了解到使用DataAnotations特性來覆寫Code-First默認約定,現在我們來學習Fluent API。
Fluent API是另一種配置領域類的方法,它比DataAnnotations特性提供更多的配置方法,下表是Fluent API支持的類型映射。
映射種類 | 配置數據庫 |
---|---|
模型(Model-wide)映射 |
|
實體(Entity)映射 |
|
屬性(Property)映射 |
|
下面,我們開始使用Fluent API來配置領域類。
我們首先創建Student和Standard兩個領域類,同樣也創建出DbContext類,DbContext類中有個OnModelCreating方法,這里我們在它的繼承類中把它覆寫出來。
代碼如下:
public class SchoolContext: DbContext { public SchoolDBContext(): base() { } public DbSet<Student> Students { get; set; } public DbSet<Standard> Standards { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { //Configure domain classes using modelBuilder here base.OnModelCreating(modelBuilder); } }
使用Fluent API配置領域類時,所有的配置代碼都要寫在OnModelCreating方法里面,所有的領域類都可以在這個方法里面寫上他們的初始化代碼。程序初始化的時候,DataAnnotation和Fluent API的優先級是:Fluent API > DataAnnotations > 默認約定。
DbModelBuilder類包含了重要的用於配置的屬性和方法,更多詳情請翻閱MSDN文檔。
接下來我們詳細講一些常用的Fluent API配置方法。
一、EntityTypeConfiguration類:
EntityTypeConfiguration類在Fluent API中有着重要的作用,它提供了一系列重要的方法和屬性來覆寫默認約定。
EntityTypeConfiguration類可以運行DbModelBuilder類的Entity<TEntity>()方法獲得,如下所示:
EntityTypeConfiguration有下面這些重要的方法:
方法名 | 返回類型 | 描述 |
---|---|---|
HasKey<TKey> | EntityTypeConfiguration | 為這個實體類型配置主鍵 |
HasMany<TTargetEntity> | ManyNavigationPropertyConfiguration | 為實體類型配置多對多關系 |
HasOptional<TTargetEntity> | OptionalNavigationPropertyConfiguration | 為實體類配置可選關系。沒有指定關系的實體類型的實例會被存入數據庫。數據庫里外鍵可為空(nullable)。 |
HasRequired<TTargetEntity> | RequiredNavigationPropertyConfiguration | 為實體類型配置必須關系。除非關系確定,否則實體類型的實例不能存入數據庫。數據庫中的外鍵將不能為空(non-nullable)。 |
Ignore<TProperty> | Void | 從模型中排除一個屬性,這個屬性將不會映射到數據庫。 |
Map | EntityTypeConfiguration | 允許高級配置有關該實體類型映射到數據庫模式。 |
Property<T> | StructuralTypeConfiguration | 配置一個定義了這種類型的結構屬性 |
ToTable | Void | 配置實體類型映射的表名 |
可以訪問MSDN查詢更多關於 EntityTypeConfiguration 類的信息。
下面介紹怎么用Fluent API配置實體類
二、Entity Mappings:
我們繼續使用在學校應用里面的Student和Standard兩個領域類
代碼如下:
public class Student { public Student() { } public int StudentID { get; set; } public string StudentName { get; set; } public DateTime? DateOfBirth { get; set; } public byte[] Photo { get; set; } public decimal Height { get; set; } public float Weight { get; set; } public Standard Standard { get; set; } } public class Standard { public Standard() { } public int StandardId { get; set; } public string StandardName { get; set; } public ICollection<Student> Students { get; set; } } }
配置默認架構:
當我們想為一組特殊的表設置一個不同的構架時,可以使用HasDefaultSchema方法:
public class SchoolContext: DbContext { public SchoolDBContext(): base() { } public DbSet<Student> Students { get; set; } public DbSet<Standard> Standards { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { //Configure default schema modelBuilder.HasDefaultSchema("Admin"); } }
映射實體到表:
Code-First將會為context類中的所有DbSet屬性創建數據庫表,表名就是屬性名,比如上面的Students和Standards。我們也可以給表配置一個不同於DbSet屬性名的表名,如下代碼所示:
namespace CodeFirst_FluentAPI_Tutorials { public class SchoolContext: DbContext { public SchoolDBContext(): base() { } public DbSet<Student> Students { get; set; } public DbSet<Standard> Standards { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { //Configure default schema modelBuilder.HasDefaultSchema("Admin"); //Map entity to table modelBuilder.Entity<Student>().ToTable("StudentInfo"); modelBuilder.Entity<Standard>().ToTable("StandardInfo","dbo"); } } }
如上代碼,我們配置表名的ToTable方法是接在Entity<TEntity>()方法后面,上面代碼已經把映射Student實體的表名改成StudentInfo,把映射Standard實體的表名改成了StandardInfo,特別留意的是,雖然我們把默認的架構名改成了Admin,但是Standard實體又特別指定了dbo架構,所以生成的數據庫如下所示:
映射實體到多張表:
下面的代碼演示了怎樣把Student實體映射到數據庫的多張表:
namespace CodeFirst_FluentAPI_Tutorials { public class SchoolContext: DbContext { public SchoolDBContext(): base() { } public DbSet<Student> Students { get; set; } public DbSet<Standard> Standards { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity<Student>().Map(m => { m.Properties(p => new { p.StudentId, p.StudentName}); m.ToTable("StudentInfo"); }).Map(m => { m.Properties(p => new { p.StudentId, p.Height, p.Weight, p.Photo, p.DateOfBirth}); m.ToTable("StudentInfoDetail"); }); modelBuilder.Entity<Standard>().ToTable("StandardInfo"); } } }
如上所示,使用Map()方法可以映射Student實體的一些屬性到StudentInfo表中,另一些屬性到StudentInfoDetail表中,我們把Student實體分裂成了兩張表,生成的數據庫如下所示:
Map method need the delegate method as a parameter. You can pass or in Map method, as shown below.
Map方法的傳入參數是一個委托,具體可以參考 Action delegate 和 lambda expression。
完成代碼如下:

using System.Data.Entity.ModelConfiguration.Configuration; namespace CodeFirst_FluentAPI_Tutorials { public class SchoolContext: DbContext { public SchoolDBContext(): base() { } public DbSet<Student> Students { get; set; } public DbSet<Standard> Standards { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity<Student>().Map(delegate(EntityMappingConfiguration<Student> studentConfig) { studentConfig.Properties(p => new { p.StudentId, p.StudentName }); studentConfig.ToTable("StudentInfo"); }); Action<EntityMappingConfiguration<Student>> studentMapping = m => { m.Properties(p => new { p.StudentId, p.Height, p.Weight, p.Photo, p.DateOfBirth }); m.ToTable("StudentInfoDetail"); }; modelBuilder.Entity<Student>().Map(studentMapping); modelBuilder.Entity<Standard>().ToTable("StandardInfo"); } } }
三、Property Mappings:
下面我們來介紹怎樣用Fluent API配置實體類的屬性
我們仍然使用學校的例子,如下兩個Student和Standard領域類:
public class Student { public Student() { } public int StudentKey { get; set; } public string StudentName { get; set; } public DateTime DateOfBirth { get; set; } public byte[] Photo { get; set; } public decimal Height { get; set; } public float Weight { get; set; } public Standard Standard { get; set; } } public class Standard { public Standard() { } public int StandardKey { get; set; } public string StandardName { get; set; } public ICollection<Student> Students { get; set; } } }
配置主鍵和混合主鍵:
上面的兩個領域類,不能依據Code-First默認約定生成主鍵,因為它們沒有Id或者{類名}+Id的屬性,所以這里使用EntityTypeConfiguration類里的HasHey()方法來創建主鍵。
注意,modelBuilder.Entity<TEntity>()返回的是EntityTypeConfiguration對象。
public class SchoolContext: DbContext { public SchoolDBContext(): base() { } public DbSet<Student> Students { get; set; } public DbSet<Standard> Standards { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { //Configure primary key modelBuilder.Entity<Student>().HasKey<int>(s => s.StudentKey); modelBuilder.Entity<Standard>().HasKey<int>(s => s.StandardKey); //Configure composite primary key modelBuilder.Entity<Student>().HasKey<int>(s => new { s.StudentKey, s.StudentName }); } }
配置列名、數據類型和排序:
Code-First默認約定以屬性名為列名,下面的代碼覆寫了這一約定:
public class SchoolContext: DbContext { public SchoolDBContext(): base() { } public DbSet<Student> Students { get; set; } public DbSet<Standard> Standards { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { //Configure Column modelBuilder.Entity<Student>() .Property(p => p.DateOfBirth) .HasColumnName("DoB") .HasColumnOrder(3) .HasColumnType("datetime2"); } }
如上所示,我們使用一個實體的Property()方法來配置列名、排序和數據類型
modelBuilder.Entity<TEntity>().Property(expression)
可以使用不同的方法來配置特別的屬性,如下圖所示:
為屬性配置可空或不為空的列:
Code-First將為主鍵的數據類型創建一個不為空的值,因為主鍵本身就不能為空,除非它的屬性上使用了?號或者標記了Nullable<T>。
使用IsOptional方法可以創建一個可為空的列,同樣,使用IsRequired也可以創建一個不為空的列。
namespace CodeFirst_FluentAPI_Tutorials { public class SchoolContext: DbContext { public SchoolDBContext(): base() { } public DbSet<Student> Students { get; set; } public DbSet<Standard> Standards { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { //Configure Null Column modelBuilder.Entity<Student>() .Property(p => p.Heigth) .IsOptional(); //Configure NotNull Column modelBuilder.Entity<Student>() .Property(p => p.Weight) .IsRequired(); } } }
配置列的大小:
Code-First默認約定是給列創建最大的數據類型大小,用HasMaxLength()方法覆寫之。
namespace CodeFirst_FluentAPI_Tutorials { public class SchoolContext: DbContext { public SchoolDBContext(): base() { } public DbSet<Student> Students { get; set; } public DbSet<Standard> Standards { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { //Set StudentName column size to 50 modelBuilder.Entity<Student>() .Property(p => p.StudentName) .HasMaxLength(50); //Set StudentName column size to 50 and change datatype to nchar //IsFixedLength() change datatype from nvarchar to nchar modelBuilder.Entity<Student>() .Property(p => p.StudentName) .HasMaxLength(50).IsFixedLength(); //Set size decimal(2,2) modelBuilder.Entity<Student>() .Property(p => p.Height) .HasPrecision(2, 2); } } }
IsFixedLength方法把列的類型從nvarchar轉變為nchar,HasPrecision方法改變了數據類型為decimal列的精度。
配置並發列:
使用ConcurrencyToken方法把一個屬性設置為並發列,代碼如下:
namespace CodeFirst_FluentAPI_Tutorials { public class SchoolContext: DbContext { public SchoolDBContext(): base() { } public DbSet<Student> Students { get; set; } public DbSet<Standard> Standards { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { //Set StudentName as concurrency column modelBuilder.Entity<Student>() .Property(p => p.StudentName) .IsConcurrencyToken(); } } }
如上代碼所示,StudentName被設置成為了並發列,每當update和delete操作的時候都會把StudentName中的值加入到SQL語句中的"where"子句中。
對於byte[]類型的屬性,我們也可以用IsRowVersion()方法來將其配置成並發列。
到此,Fluent API的主要內容就講的差不多了,下面開始會講一對一,一對多,多對多的領域類配置,和數據庫遷移等。