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的主要内容,如果未能解决你的问题,请参考以下文章