EF CodeFirst系列(5)---FluentApi
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上判断是否并发
}
}
执行程序后生成的数据库如下:
EF CodeFirst系列(5)---FluentApi的更多相关文章
- EF CodeFirst系列(3)---EF中的继承策略(暂存)
我们初始化数据库一节已经知道:EF为每一个具体的类生成了数据库的表.现在有了一个问题:我们在设计领域类时经常用到继承,这能让我们的代码更简洁且容易管理,在面向对象中有“has a”和“is a”关系 ...
- EF CodeFirst系列(6)---配置1对1,1对多,多对多关系
这一节介绍EF CodeFirst模式中的1对0/1,1对多,多对多关系的配置,只有梳理清楚实体间的关系,才能进行愉快的开发,因此这节虽然很简单但是还是记录了一下. 1. 1对0/1关系配置 1. 通 ...
- EF CodeFirst系列(7)---FluentApi配置存储过程
FluentApi配置存储过程 1.EF自动生成存储过程 EF6的CodeFirst开发模式支持给实体的CUD操作配置存储过程,当我们执行SaveChanges()方法时EF不在生成INSERT,UP ...
- EF CodeFirst系列(8)--- FluentApi配置单个实体
我们已经知道了在OnModelCreating()方法中可以通过FluentApi对所有的实体类进行配置,然而当实体类很多时,我们把所有的配置都放在OnModelCreating()方法中很难维护.E ...
- 2.简单的Code First例子(EF Code-First系列)
现在假想,我们想要为讴歌学校创建一个应用程序,这个程序需要能够来添加或者更新学生,分数,教师还有课程信息. 代替之前我们的做法:先是创建数据库,现在我们不这么做,我们先来创建领域类,首先我来创建两个简 ...
- 3.Code-First 约定(EF Code-First系列)
前面,我们已经了解了Code-First利用领域类,怎么为我们创建数据库的简单示例.现在我们来学习一下Code-First约定吧. 什么是约定 约定说白了,就是基于一套规矩办事,这里就是基于你定义好的 ...
- 6.Configure Domain Classes(配置领域类)【EF Code-First 系列】
在前面的部分中,我们学习了Code-First默认约定,Code-First使用默认的约定,根据你的领域类,然后生成概念模型. Code-First模式,发起了一种编程模式:约定大于配置.这也就是说, ...
- EF CodeFirst系列(1)---CodeFirst简单入门
1.什么是CodeFirst 从EF4.1开始,EF可以支持CodeFirst开发模式,这种开发模式特别适用于领域驱动设计(Domain Driven Design,大名鼎鼎的DDD).在CodeFi ...
- EF CodeFirst系列(2)---CodeFirst的数据库初始化
1. CodeFirst的默认约定 1.领域类和数据库架构的映射约定 在介绍数据库的初始化之前我们需要先了解领域类和数据库之间映射的一些约定.在CodeFirst模式中,约定指的是根据领域类(如Stu ...
随机推荐
- 在搭建tesseract-OCR环境中遇到问题和反省
Tesseract,一款由HP实验室开发由Google维护的开源OCR(Optical Character Recognition , 光学字符识别)引擎,特点是开源,免费,支持多语言,多平台. 在搭 ...
- JDBC获得连接时报connection refused
1,检查数据库服务器的IP是否正确. 2,检查用户名密码是否正确. 3,检查SID,获selecte instance_name from v$instance;
- topjui中datagrid增删改查
1.掌握datagrid的创建方式在html中直接定义与在js中定义 可参考easyui的官方文档:http://www.jeasyui.net/plugins/183.html 2.实现代码如下:重 ...
- 【Python 补充01】Python运算符
Python运算符 举个简单的例子 4 +5 = 9 . 例子中,4 和 5 被称为操作数,"+" 称为运算符. 1.算术运算符 + - * / # 加减乘除 % # 取模(返回除 ...
- 初识shell编程
1.shell编程之为什么学.怎么学 为什么学shell编程 Linux系统批量管理 提升工作效率,减少重复工作 学好shell编程所需要的基础知识 熟悉使用vim编辑器 熟悉SSH终端 熟练掌握Li ...
- SQL 数据类型
Microsoft Access.MySQL 以及 SQL Server 所使用的数据类型和范围. Microsoft Access 数据类型 数据类型 描述 存储 Text 用于文本或文本与数字的组 ...
- JS检测浏览器是否最大化
function isFullScreen (){ if( window.outerHeight === screen.availHeight ){ i ...
- Java面试准备之多线程
什么叫线程安全?举例说明 多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程将如何交替执行,并且在主调代码中不需要任何额外的同步或者协同,这个类都能表现出正确的行为,那么就称这个类是线程 ...
- SystemCheckError: System check identified some issues: ERRORS: users.Test.groups: (fields.E304) Reverse accessor for 'Test.groups' clashes with reverse accessor for 'User.groups'.
Error Msg: SystemCheckError: System check identified some issues: ERRORS: users.Test.groups: (fields ...
- whereis、which、find的区别
which用于查找可执行文件的目录,我们平时执行的命令实际上是一个可执行文件,如ls命令实际上是/usr/bin/目录下的一个可执行文件.它实际上是通过 PATH环境变量来查找的. whereis用于 ...