前面咱們已經瞭解到使用DataAnotations特性來覆寫Code-First默認約定,如今咱們來學習Fluent API。數據庫
Fluent API是另外一種配置領域類的方法,它比DataAnnotations特性提供更多的配置方法,下表是Fluent API支持的類型映射。express
映射種類 | 配置數據庫 |
---|---|
模型(Model-wide)映射 |
|
實體(Entity)映射 |
|
屬性(Property)映射 |
|
下面,咱們開始使用Fluent API來配置領域類。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 > 默認約定。app
DbModelBuilder類包含了重要的用於配置的屬性和方法,更多詳情請翻閱MSDN文檔。dom
接下來咱們詳細講一些經常使用的Fluent API配置方法。ide
EntityTypeConfiguration類在Fluent API中有着重要的做用,它提供了一系列重要的方法和屬性來覆寫默認約定。學習
EntityTypeConfiguration類能夠運行DbModelBuilder類的Entity<TEntity>()方法得到,以下所示:ui
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配置實體類
咱們繼續使用在學校應用裏面的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"); } } }
下面咱們來介紹怎樣用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的主要內容就講的差很少了,下面開始會講一對一,一對多,多對多的領域類配置,和數據庫遷移等。