EF CodeFirst系列(5)---FluentApi

时间:2021-01-03 08:52:33

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)

EF CodeFirst系列(5)---FluentApi

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函数,生成了新的数据库,如下所示:

EF CodeFirst系列(5)---FluentApi

我们看到,通过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上判断是否并发              
        }
    }

执行程序后生成的数据库如下:

EF CodeFirst系列(5)---FluentApi