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 = , 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
.IsOptional(); //可以为null //设置姓名
modelBuilder.Entity<Student>().Property(s => s.StudentName)
.HasMaxLength() //最长20
.IsRequired() //不能为null
.IsConcurrencyToken(); //用于乐观并发检测,delete或者update时,这个属性添加到where上判断是否并发
}
}
执行程序后生成的数据库如下: