EF CodeFirst系列(5)---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)

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上判断是否并发
        }
    }

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

原文地址:https://www.cnblogs.com/frank0812/p/11150242.html

时间: 2024-11-10 07:09:19

EF CodeFirst系列(5)---FluentApi的相关文章

Code-First 约定(EF Code-First系列)

前面,我们已经了解了Code-First利用领域类,怎么为我们创建数据库的简单示例.现在我们来学习一下Code-First约定吧. 什么是约定 约定说白了,就是基于一套规矩办事,这里就是基于你定义好的领域类,然后根据默认的规矩来配置概念模型.Code-First约定定义在这个命名空间下面: System.Data.Entity.ModelConfiguration.Conventions 现在来大致浏览一下都有哪些约定吧: 类型发现 在前面的章节中,我们在上下文类中创建了DbSet属性的类集合,

DB Initialization(数据库初始化)[EF Code-First系列]

前面的例子中,我们已经看到了Code-First自动为我们创建数据库的例子. 这里我们将要学习的是,当初始化的时候,Code-First是怎么决定数据库的名字和服务的呢??? 下面的图,解释了这一切!!! 这个图解释了,数据库初始化的流程,是基于我们在上下文类中的构造函数中传递的参数. 在上面的图中,context类中的base构造器中,可以填入下面的参数: 无参数(No Parameter) 数据库的名字(Database Name) 连接字符串的名字(Connection String Na

[.NET领域驱动设计实战系列]专题一:前期准备之EF CodeFirst

一.前言 从去年已经接触领域驱动设计(Domain-Driven Design)了,当时就想自己搭建一个DDD框架,所以当时看了很多DDD方面的书,例如领域驱动模式与实战,领域驱动设计:软件核心复杂性应对之道和领域驱动设计C# 2008实现等书,由于当时只是看看而已,并没有在自己代码中进行实现,只是初步了解一些DDD分层的思想和一些基本概念,例如实体,聚合根.仓储等概念,今年有机会可以去试试面试一个架构岗位的时候,深受打击,当面试官问起是否在项目中使用过DDD思想来架构项目时,我说没有,只是了解

9.11 翻译系列:数据注解特性之--Timestamp【EF 6 Code-First系列】

9.11 翻译系列:数据注解特性之--Timestamp[EF 6 Code-First系列] 原文链接:https://www.entityframeworktutorial.net/code-first/TimeStamp-dataannotations-attribute-in-code-first.aspx EF 6和EF Core都包含TimeStamp数据注解特性.它只能用在实体的byte数组类型的属性上,并且只能用在一个byte数组类型的属性上.然后在数据库中,创建timestam

20.1翻译系列:EF 6中自动数据迁移技术【EF 6 Code-First系列】

原文链接:https://www.entityframeworktutorial.net/code-first/automated-migration-in-code-first.aspx EF 6 Code-First系列文章目录: 1 翻译系列:什么是Code First(EF 6 Code First 系列) 2.翻译系列:为EF Code-First设置开发环境(EF 6 Code-First系列) 3.翻译系列:EF Code-First 示例(EF 6 Code-First系列) 4

EF中的开放式并发(EF基础系列)

好久没更新EF这个系列了,现在又重新开始. 这次学习,开放式并发.首先拿出数据库脚本: 说明一下,这个数据库脚本是之前的章节中稍作修改的: USE [SchoolDB] GO /****** Object: Table [dbo].[Student] Script Date: 11/30/2015 21:42:07 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE TABLE [dbo].[Student]( [Stu

Configure Many-to-Many(配置多对多关系)【Code-First系列】

现在学习EF Code-First多对多的配置. 这里我们举例:学生和班级实体,一个学生可以选修多个课程,多个学生也可以选修同一个课程. 一.使用数据注解特性,配置多对多的关系 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace EF7 { public class Student { public

EF CODEFIRST WITH ORACLE 存储过程

EF  CODEFIRST WITH ORACLE 解决存储过程一直没找到解决方案 所以最后也没办法还是用了最基本的解决方案 采用Oracle.ManagedDataAccess提供的ADO基础访问类,不需要再次额外引用第三方类库了. using Oracle.ManagedDataAccess.Client; public object[] ExecuteProc(string procName, params DbParameter[] parms) { MyDbContext dbCont

EF CodeFirst 如何通过配置自动创建数据库&lt;当模型改变时&gt;

最近悟出来一个道理,在这儿分享给大家:学历代表你的过去,能力代表你的现在,学习代表你的将来. 十年河东十年河西,莫欺少年穷 学无止境,精益求精    本篇为进阶篇,也是弥补自己之前没搞明白的地方,惭愧惭愧. 如有不明白,请参考:EF CodeFirst 创建数据库 及 EF CodeFirst增删改查之'CRUD' 话不多说,直接上代码: using System; using System.Collections.Generic; using System.Linq; using System