FluentApi總結
1.FluentApi簡介
EF中的FluentApi作用是通過配置領域類來覆蓋預設的約定。在EF中,我們通過DbModelBuilder類來使用FluentApi,它的功能比資料註釋屬性更強大。
使用FluentApi時,我們在context類的OnModelCreating()方法中重寫配置項,一個栗子:
public class SchoolContext: DbContext { public DbSet<Student> Students { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { //Write Fluent API configurations here } }
我們可以把FluentApi和資料註釋屬性一起使用,當FluentApi和資料註釋屬性都配置了同一個項時,採用FluentApi中的配置。
在EF6中FluentApi可以配置領域類的以下幾個方面,下表也列出了一些常用的FluentApi方法及其作用:
配置 | Fluent API 方法 | 作用 |
---|---|---|
架構相關配置 | HasDefaultSchema() | 資料庫的預設架構 |
ComplexType() | 把一個類配置為複雜型別 | |
實體相關配置 | HasIndex() | 實體的的索引 |
HasKey() | 實體的主鍵(可其實現複合主鍵,[Key]在EF core中不能實現複合主鍵) | |
HasMany() | 1對多的或者 多對多關係 | |
HasOptional() | 一個可選的關係,這樣配置會在資料庫中生成一個可空的外來鍵 | |
HasRequired() | 一個必有的關係,這樣配置會在資料庫中生成一個不能為空的外來鍵 | |
Ignore() | 實體或者實體的屬性不對映到資料庫 | |
Map() | 設定一些優先的配置 | |
MapToStoredProcedures() | 實體的CUD操作使用儲存過程 | |
ToTable() | 為實體設定表名 | |
屬性相關配置 | HasColumnAnnotation() | 給屬性設定註釋 |
IsRequired() | 在呼叫SaveChanges()方法時,屬性不能為空 | |
IsOptional() | 可選的,在資料庫生成可空的列 | |
HasParameterName() | 配置用於該屬性的儲存過程的引數名 | |
HasDatabaseGeneratedOption() | 配置資料庫中對應列的值怎樣生成的,如計算,自增等 | |
HasColumnOrder() | 配置資料庫中對應列的排列順序 | |
HasColumnType() | 配置資料庫中對應列的資料型別 | |
HasColumnName() | 配置資料庫中對應列的列名 | |
IsConcurrencyToken() | 配置資料庫中對應列用於樂觀併發檢測 |
2.實體相關配置
1.實體簡單配置
直接上栗子:
我們新建一個EF6Demo的控制檯應用程式,新增Student和Grade實體,以及上下文類SchoolContext,程式碼如下:
//學生類 public class Student { public int StudentId { get; set; } public string StudentName { get; set; } public string StudentNo { get; set; } public virtual Grade Grade{get;set;} } //年級類 public class Grade { public int GradeId { get; set; } public string GradeName { get; set; } public virtual ICollection<Student> Students { get; set; } } //上下文類 public class SchoolContext:DbContext { public SchoolContext() : base() { } public DbSet<Student> Students { get; set; } public DbSet <Grade> Grades { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.HasDefaultSchema("Admin");//新增預設架構名 modelBuilder.Entity<Student>().ToTable("StudentInfo"); modelBuilder.Entity<Grade>().ToTable("GradeInfo","NewAdmin");//設定表名和架構 } }
在Main函式中執行程式碼:
class Program { static void Main(string[] args) { using (SchoolContext context=new SchoolContext()) { context.Students.Add(new Student() { StudentId = 1, StudentName = "Jack" }); context.SaveChanges(); } } }
這時在內建的SqlServer中生成資料庫,如下圖所示,我們看到Student表名為StudentInfo,架構是Admin;Grade表名是GradeInfo,架構是NewAdmin,覆蓋了預設的約定(預設表名為dbo.Students和dbo.Grades)
2.實體對映到多張表
有時候我們希望一個實體的屬性分在兩種表中,那麼該怎麼配置呢?還用上邊的栗子,我們把學生的姓名和Id存在一張表,學號和Id放在另一張表中,程式碼如下:
public class SchoolContext:DbContext { public SchoolContext() : base() { } public DbSet<Student> Students { get; set; } public DbSet <Grade> Grades { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity<Student>().Map(m => { //配置第一張表,包含學生Id和學生姓名 m.Properties(p => new { p.StudentId, p.StudentName }); m.ToTable("StudentInfo"); }).Map(m => { //配置第二張表,包含學生Id和學生學號 m.Properties(p => new { p.StudentId, p.StudentNo }); m.ToTable("StudentInfo2"); }); //配置年級表名 modelBuilder.Entity<Grade>().ToTable("GradeInfo"); } }
執行一下Main函式,生成了新的資料庫,如下所示:
我們看到,通過Map()方法,我們把Student實體的屬性被分在了兩個表中。modelBuilder.Entity<T>()方法返回的是一個EntityTypeConfiguration<T>型別,Map()方法的引數是一個委託型別,委託的輸入引數是EntityMappingConfiguration的例項。我們可以自定義一個委託來實現配置,下邊的程式碼執行後生成的資料庫和和上邊一樣:
public class SchoolContext : DbContext { public SchoolContext() : base() { } public DbSet<Student> Students { get; set; } public DbSet<Grade> Grades { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { //先定義一個Action委託備用,委託的輸入引數是一個實體對映配置(EntityMappingConfiguration)的例項 Action<EntityMappingConfiguration<Student>> studentMapping = m => { m.Properties(p => new { p.StudentId, p.StudentNo }); m.ToTable("StudentInfo2"); }; modelBuilder.Entity<Student>() //第一張表Map()方法引數是delegate形式委託 .Map(delegate (EntityMappingConfiguration<Student> studentConfig) { //map引數是lambda表示式 studentConfig.Properties(p => new { p.StudentId, p.StudentName }); studentConfig.ToTable("StudentInfo"); }) //第二張表Map()方法引數是Action委託 .Map(studentMapping); modelBuilder.Entity<Grade>().ToTable("GradeInfo"); } }
3.屬性相關配置
屬性的配置比較簡單,這裡簡單總結了主鍵,列基本屬性,是否可空,資料長度,高併發的配置。
一個栗子:
public class 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 Grade Grade{ get; set; }//年級 } public class Grade { public int GradeKey { get; set; }//主鍵 public string GradeName { get; set; }//年級名 public ICollection<Student> Students { get; set; } }
使用FluentApi對領域類做了以下配置:
public class SchoolContext : DbContext { public SchoolContext() : base() { } public DbSet<Student> Students { get; set; } public DbSet<Grade> Grades { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { //設定預設架構 modelBuilder.HasDefaultSchema("Admin"); //設定主鍵 modelBuilder.Entity<Student>().HasKey<int>(s => s.StudentKey); //設定不對映的屬性 modelBuilder.Entity<Student>().Ignore(s => s.Height); //設定DateOfBirth modelBuilder.Entity<Student>().Property(p => p.DateOfBirth) .HasColumnName("birthday") //列名為birthday .HasColumnType("datetime2") //資料型別是datetime型別 .HasColumnOrder(3) //順序編號是3 .IsOptional(); //可以為null //設定姓名 modelBuilder.Entity<Student>().Property(s => s.StudentName) .HasMaxLength(20) //最長20 .IsRequired() //不能為null .IsConcurrencyToken(); //用於樂觀併發檢測,delete或者update時,這個屬性新增到where上判斷是否併發 } }
執行程式後生成的資料庫如下: