EF CodeFirst系列(5)---FluentApi

撈月亮的猴子發表於2018-09-18

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上判斷是否併發              
        }
    }

執行程式後生成的資料庫如下:

 

相關文章