Fluent API配置:

前面我们已经了解到使用DataAnotations特性来覆写Code-First默认约定,现在我们来学习Fluent API。

Fluent API是另一种配置领域类的方法,它比DataAnnotations特性提供更多的配置方法,下表是Fluent API支持的类型映射。

映射种类 配置数据库
模型(Model-wide)映射
  • 设置默认架构
  • 设置自定义约定
实体(Entity)映射
  • 设置单表或多表和设置架构
  • 设置复杂类型
  • 设置继承层次结构
属性(Property)映射
  • 设置列,列名、类型、是否可空、大小、排序
  • 设置并发列
  • 设置外键列
  • 配置关系

下面,我们开始使用Fluent API来配置领域类。

我们首先创建Student和Standard两个领域类,同样也创建出DbContext类,DbContext类中有个OnModelCreating方法,这里我们在它的继承类中把它覆写出来。

代码如下:

public class SchoolContext: DbContext
{
public SchoolDBContext(): base()
{
} public DbSet<Student> Students { get; set; }
public DbSet<Standard> Standards { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//Configure domain classes using modelBuilder here base.OnModelCreating(modelBuilder);
}
}

使用Fluent API配置领域类时,所有的配置代码都要写在OnModelCreating方法里面,所有的领域类都可以在这个方法里面写上他们的初始化代码。程序初始化的时候,DataAnnotation和Fluent API的优先级是:Fluent API > DataAnnotations > 默认约定。

DbModelBuilder类包含了重要的用于配置的属性和方法,更多详情请翻阅MSDN文档。

接下来我们详细讲一些常用的Fluent API配置方法。

一、EntityTypeConfiguration类:

EntityTypeConfiguration类在Fluent API中有着重要的作用,它提供了一系列重要的方法和属性来覆写默认约定。

EntityTypeConfiguration类可以运行DbModelBuilder类的Entity<TEntity>()方法获得,如下所示:

EntityTypeConfiguration有下面这些重要的方法:

方法名 返回类型 描述
HasKey<TKey> EntityTypeConfiguration 为这个实体类型配置主键
HasMany<TTargetEntity> ManyNavigationPropertyConfiguration 为实体类型配置多对多关系
HasOptional<TTargetEntity> OptionalNavigationPropertyConfiguration

为实体类配置可选关系。没有指定关系的实体类型的实例会被存入数据库。数据库里外键可为空(nullable)。

HasRequired<TTargetEntity> RequiredNavigationPropertyConfiguration

为实体类型配置必须关系。除非关系确定,否则实体类型的实例不能存入数据库。数据库中的外键将不能为空(non-nullable)。

Ignore<TProperty> Void

从模型中排除一个属性,这个属性将不会映射到数据库。

Map EntityTypeConfiguration

允许高级配置有关该实体类型映射到数据库模式。

Property<T> StructuralTypeConfiguration

配置一个定义了这种类型的结构属性

ToTable Void

配置实体类型映射的表名

可以访问MSDN查询更多关于 EntityTypeConfiguration 类的信息。

下面介绍怎么用Fluent API配置实体类

二、Entity Mappings:

我们继续使用在学校应用里面的Student和Standard两个领域类

代码如下:

public class Student
{
public Student()
{ }
public int StudentID { 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 Standard Standard { get; set; }
} public class Standard
{
public Standard()
{ }
public int StandardId { get; set; }
public string StandardName { get; set; } public ICollection<Student> Students { get; set; } }
}

配置默认架构:

当我们想为一组特殊的表设置一个不同的构架时,可以使用HasDefaultSchema方法:

public class SchoolContext: DbContext
{
public SchoolDBContext(): base()
{
} public DbSet<Student> Students { get; set; }
public DbSet<Standard> Standards { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//Configure default schema
modelBuilder.HasDefaultSchema("Admin");
}
}

映射实体到表:

Code-First将会为context类中的所有DbSet属性创建数据库表,表名就是属性名,比如上面的Students和Standards。我们也可以给表配置一个不同于DbSet属性名的表名,如下代码所示:

namespace CodeFirst_FluentAPI_Tutorials
{ public class SchoolContext: DbContext
{
public SchoolDBContext(): base()
{
} public DbSet<Student> Students { get; set; }
public DbSet<Standard> Standards { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//Configure default schema
modelBuilder.HasDefaultSchema("Admin"); //Map entity to table
modelBuilder.Entity<Student>().ToTable("StudentInfo");
modelBuilder.Entity<Standard>().ToTable("StandardInfo","dbo"); }
}
}

如上代码,我们配置表名的ToTable方法是接在Entity<TEntity>()方法后面,上面代码已经把映射Student实体的表名改成StudentInfo,把映射Standard实体的表名改成了StandardInfo,特别留意的是,虽然我们把默认的架构名改成了Admin,但是Standard实体又特别指定了dbo架构,所以生成的数据库如下所示:

映射实体到多张表:

下面的代码演示了怎样把Student实体映射到数据库的多张表:

namespace CodeFirst_FluentAPI_Tutorials
{ public class SchoolContext: DbContext
{
public SchoolDBContext(): base()
{
} public DbSet<Student> Students { get; set; }
public DbSet<Standard> Standards { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Student>().Map(m =>
{
m.Properties(p => new { p.StudentId, p.StudentName});
m.ToTable("StudentInfo"); }).Map(m => {
m.Properties(p => new { p.StudentId, p.Height, p.Weight, p.Photo, p.DateOfBirth});
m.ToTable("StudentInfoDetail"); }); modelBuilder.Entity<Standard>().ToTable("StandardInfo"); }
}
}

如上所示,使用Map()方法可以映射Student实体的一些属性到StudentInfo表中,另一些属性到StudentInfoDetail表中,我们把Student实体分裂成了两张表,生成的数据库如下所示:

Map method need the delegate method as a parameter. You can pass  or in Map method, as shown below.

Map方法的传入参数是一个委托,具体可以参考 Action delegate 和 lambda expression

完成代码如下:

using System.Data.Entity.ModelConfiguration.Configuration;

namespace CodeFirst_FluentAPI_Tutorials
{ public class SchoolContext: DbContext
{
public SchoolDBContext(): base()
{
} public DbSet<Student> Students { get; set; }
public DbSet<Standard> Standards { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Student>().Map(delegate(EntityMappingConfiguration<Student> studentConfig)
{
studentConfig.Properties(p => new { p.StudentId, p.StudentName });
studentConfig.ToTable("StudentInfo");
}); Action<EntityMappingConfiguration<Student>> studentMapping = m =>
{
m.Properties(p => new { p.StudentId, p.Height, p.Weight, p.Photo, p.DateOfBirth });
m.ToTable("StudentInfoDetail");
};
modelBuilder.Entity<Student>().Map(studentMapping); modelBuilder.Entity<Standard>().ToTable("StandardInfo"); }
}
}

三、Property Mappings:

下面我们来介绍怎样用Fluent API配置实体类的属性

我们仍然使用学校的例子,如下两个Student和Standard领域类:

public class Student
{
public 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 Standard Standard { get; set; }
} public class Standard
{
public Standard()
{ }
public int StandardKey { get; set; }
public string StandardName { get; set; } public ICollection<Student> Students { get; set; } }
}

配置主键和混合主键:

上面的两个领域类,不能依据Code-First默认约定生成主键,因为它们没有Id或者{类名}+Id的属性,所以这里使用EntityTypeConfiguration类里的HasHey()方法来创建主键。

注意,modelBuilder.Entity<TEntity>()返回的是EntityTypeConfiguration对象。

public class SchoolContext: DbContext
{
public SchoolDBContext(): base()
{
} public DbSet<Student> Students { get; set; }
public DbSet<Standard> Standards { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//Configure primary key
modelBuilder.Entity<Student>().HasKey<int>(s => s.StudentKey);
modelBuilder.Entity<Standard>().HasKey<int>(s => s.StandardKey); //Configure composite primary key
modelBuilder.Entity<Student>().HasKey<int>(s => new { s.StudentKey, s.StudentName });
}
}

配置列名、数据类型和排序:

Code-First默认约定以属性名为列名,下面的代码覆写了这一约定:

public class SchoolContext: DbContext
{
public SchoolDBContext(): base()
{
} public DbSet<Student> Students { get; set; }
public DbSet<Standard> Standards { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//Configure Column
modelBuilder.Entity<Student>()
.Property(p => p.DateOfBirth)
.HasColumnName("DoB")
.HasColumnOrder()
.HasColumnType("datetime2");
}
}

如上所示,我们使用一个实体的Property()方法来配置列名、排序和数据类型

modelBuilder.Entity<TEntity>().Property(expression) 可以使用不同的方法来配置特别的属性,如下图所示:

为属性配置可空或不为空的列:

Code-First将为主键的数据类型创建一个不为空的值,因为主键本身就不能为空,除非它的属性上使用了?号或者标记了Nullable<T>。

使用IsOptional方法可以创建一个可为空的列,同样,使用IsRequired也可以创建一个不为空的列。

namespace CodeFirst_FluentAPI_Tutorials
{ public class SchoolContext: DbContext
{
public SchoolDBContext(): base()
{
} public DbSet<Student> Students { get; set; }
public DbSet<Standard> Standards { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//Configure Null Column
modelBuilder.Entity<Student>()
.Property(p => p.Heigth)
.IsOptional(); //Configure NotNull Column
modelBuilder.Entity<Student>()
.Property(p => p.Weight)
.IsRequired();
}
}
}

配置列的大小:

Code-First默认约定是给列创建最大的数据类型大小,用HasMaxLength()方法覆写之。

namespace CodeFirst_FluentAPI_Tutorials
{ public class SchoolContext: DbContext
{
public SchoolDBContext(): base()
{
} public DbSet<Student> Students { get; set; }
public DbSet<Standard> Standards { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//Set StudentName column size to 50
modelBuilder.Entity<Student>()
.Property(p => p.StudentName)
.HasMaxLength(); //Set StudentName column size to 50 and change datatype to nchar
//IsFixedLength() change datatype from nvarchar to nchar
modelBuilder.Entity<Student>()
.Property(p => p.StudentName)
.HasMaxLength().IsFixedLength(); //Set size decimal(2,2)
modelBuilder.Entity<Student>()
.Property(p => p.Height)
.HasPrecision(, );
}
}
}

IsFixedLength方法把列的类型从nvarchar转变为nchar,HasPrecision方法改变了数据类型为decimal列的精度。

配置并发列:

使用ConcurrencyToken方法把一个属性设置为并发列,代码如下:

namespace CodeFirst_FluentAPI_Tutorials
{ public class SchoolContext: DbContext
{
public SchoolDBContext(): base()
{
} public DbSet<Student> Students { get; set; }
public DbSet<Standard> Standards { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//Set StudentName as concurrency column
modelBuilder.Entity<Student>()
.Property(p => p.StudentName)
.IsConcurrencyToken();
}
}
}

如上代码所示,StudentName被设置成为了并发列,每当update和delete操作的时候都会把StudentName中的值加入到SQL语句中的"where"子句中。

对于byte[]类型的属性,我们也可以用IsRowVersion()方法来将其配置成并发列。

到此,Fluent API的主要内容就讲的差不多了,下面开始会讲一对一,一对多,多对多的领域类配置,和数据库迁移等。

EntityFramework Code-First 简易教程(七)-------领域类配置之Fluent API的更多相关文章

  1. EntityFramework Code-First 简易教程(六)-------领域类配置之DataAnnotations

    EF Code-First提供了一个可以用在领域类或其属性上的DataAnnotation特性集合,DataAnnotation特性会覆盖默认的EF约定. DataAnnotation存在于两个命名空 ...

  2. EntityFramework Code-First 简易教程(五)-------领域类配置

    前言:在前篇中,总是把领域类(Domain Class)翻译成模型类,因为我的理解它就是一个现实对象的抽象模型,不知道对不对.以防止将来可能的歧义,这篇开始还是直接对Domain Class直译. 前 ...

  3. Entity Frame Code First 简易教程

    简介 什么是ORM 搭建Entity FrameWork CodeFirst应用 数据库迁移 表属性常见配置 Entity FrameWork 一对多.多对多 一.简介 Entity Framewor ...

  4. EntityFramework Code-First—领域类配置之DataAnnotations

    本文出自:https://www.cnblogs.com/tang-tang/p/5510574.html 一.摘要 EF Code-First提供了一个可以用在领域类或其属性上的DataAnnota ...

  5. WebGL简易教程(七):绘制一个矩形体

    目录 1. 概述 2. 示例 2.1. 顶点索引绘制 2.2. MVP矩阵设置 2.2.1. 模型矩阵 2.2.2. 投影矩阵 2.2.3. 视图矩阵 2.2.4. MVP矩阵 3. 结果 4. 参考 ...

  6. Spring Boot2 系列教程(七)理解自动化配置的原理

    Spring Boot 中的自动化配置确实够吸引人,甚至有人说 Spring Boot 让 Java 又一次焕发了生机,这话虽然听着有点夸张,但是不可否认的是,曾经臃肿繁琐的 Spring 配置确实让 ...

  7. EntityFramework Code-First 简易教程(十一)-------从已存在的数据库中映射出表

    怎样从一个已存在的数据库中映射表到 entity 实体? Entity Framework 提供了一个简便方法,可以为已存在的数据库里的所有表和视图创建实体类(entity class),并且可以用 ...

  8. Dart 语言简易教程系列

    google Fuchsia系统 及 dart语言简介 在 InteIIiJ IDEA 中搭建 Dart 的开发环境 Dart Linux 开发环境搭建 Dart 语言简易教程(一) Dart 语言简 ...

  9. EntityFramework 系列:实体类配置-根据依赖配置关系和关联

    EF实体类的配置可以使用数据注释或Fluent API两种方式配置,Fluent API配置的关键在于搞清实体类的依赖关系,按此方法配置,快速高效合理.为了方便理解,我们使用简化的实体A和B以及A.B ...

随机推荐

  1. Ubuntu18.04 下修改 root密码

    首先打开终端输入命令 sudo passwd root 然后依次是当前用户密码,将要设置root密码,确认root密码.切换root看一下 备注: #符号 是系统用户 root$符号 是你创建的用户 ...

  2. python异步编程--回调模型(selectors模块)

    目录 0. 参考地址 1. 前言 2. 核心类 3. SelectSelector核心函数代码分析 3.1 注册 3.2 注销 3.3 查询 4. 别名 5. 总结 6. 代码报错问题 1. 文件描述 ...

  3. c/c++本地时间获取

    在记录程序日志时,需要记录时间.如下: #include <iostream> #include <time.h> #include <windows.h> usi ...

  4. Android Systom

  5. Python 【web框架】之Flask

    flask 是Python实现的轻量级web框架.没有表单,orm等,但扩展性很好.很多Python web开发者十分喜欢.本篇介绍flask的简单使用及其扩展. 文中示例源码已经传到github:h ...

  6. vue 动态组件、父子组件传参

    1.vue中的自定义属性并获得属性的值 自定义属性::data-id语法为 :data-属性  获取属性的值:ev.target.dataset.id 2.vue父子组件传值 3.动态组件使用

  7. 深入学习使用ocr算法识别图片中文字的方法

    公司有个需求,简单点说需要从一张图片中识别出中文,通过python来实现,当然其他程序也行,只要能实现,而小编主要学习python,所以就提了python.一个小白在网上遨游了一天,终于找到一丝丝思绪 ...

  8. Java提高篇之抽象类与接口

    接口和内部类为我们提供了一种将接口与实现分离的更加结构化的方法. 抽象类与接口是java语言中对抽象概念进行定义的两种机制,正是由于他们的存在才赋予java强大的面向对象的能力.他们两者之间对抽象概念 ...

  9. js------保留指定位数小数

    // tofix.js文件// params// val: 要处理的数据,Number | String// len: 保留小数位数,位数不足时,以0填充// side: 1|-1 对应 入|舍exp ...

  10. TCP/IP 详解

    分层 每一层负责不同的功能:     链路层 有时也称作数据链路层或网络接口层, 通常包括操作系统中的设备驱动程序和计算机 中对应的网络接口卡.它们一起处理与电缆(或其他任何传输媒介)的物理接口细节. ...