EF CodeFirst系列---FluentApi

Posted frank0812

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了EF CodeFirst系列---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 Gradeget;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上判断是否并发              
        
    
技术图片

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

技术图片

 

以上是关于EF CodeFirst系列---FluentApi的主要内容,如果未能解决你的问题,请参考以下文章

EF Codefirst学习系列三:种子数据

EF CodeFirst系列---配置1对1,1对多,多对多关系

EF-CodeFirst-1 玩起来

EF-CodeFirst-1 玩起来

EF-CodeFirst-3搞事

EF-CodeFirst-3搞事