Code First约定-Fluent API配置

使用Fluent API 配置/映射属性和类型

简介

通常通过重写派生DbContext 上的OnModelCreating 方法来访问Code First Fluent API。以下示例旨在显示如何使用 Fluent API 执行各种任务,您可以将代码复制出来并进行自定义,使之适用于您的模型。

属性映射

使用Fluent API 配置/映射属性和类型

简介

通常通过重写派生DbContext 上的OnModelCreating 方法来访问Code First Fluent API。以下示例旨在显示如何使用 Fluent API 执行各种任务,您可以将代码复制出来并进行自定义,使之适用于您的模型。

属性映射

Property 方法用于为每个属于实体或复杂类型的属性配置特性。Property 方法用于获取给定属性的配置对象。配置对象上的选项特定于要配置的类型;例如,IsUnicode 只能用于字符串属性。

配置主键

要显式将某个属性设置为主键,可使用 HasKey 方法。在以下示例中,使用了 HasKey 方法对 OfficeAssignment 类型配置 InstructorID 主键。

modelBuilder.Entity<OfficeAssignment>().HasKey(t =>t.InstructorID);

配置组合主键

以下示例配置要作为Department 类型的组合主键的DepartmentID 和 Name 属性。

modelBuilder.Entity<Department>().HasKey(t => new { t.DepartmentID, t.Name });

关闭数值主键的标识

以下示例将DepartmentID 属性设置为System.ComponentModel.DataAnnotations.DatabaseGeneratedOption.None,以指示该值不由数据库生成。

modelBuilder.Entity<Department>().Property(t =>t.DepartmentID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);

指定属性的最大长度

在以下示例中,Name属性不应超过 50 个字符。如果其值超过 50 个字符,则出现 DbEntityValidationException 异常。如果 Code First 基于此模型创建数据库,它还会将 Name 列的最大长度设置为50 个字符。

modelBuilder.Entity<Department>().Property(t =>t.Name).HasMaxLength(50);

将属性配置为必需

在下面的示例中,Name属性是必需的。如果不指定 Name,则出现 DbEntityValidationException 异常。如果 Code First 基于此模型创建数据库,则用于存储此属性的列将不可为空。

modelBuilder.Entity<Department>().Property(t =>t.Name).IsRequired();

指定不将CLR 属性映射到数据库中的列

以下示例显示如何指定CLR 类型的属性不映射到数据库中的列。

modelBuilder.Entity<Department>().Ignore(t => t.Budget);

将CLR 属性映射到数据库中的特定列

以下示例将Name CLR 属性映射到DepartmentName 数据库列。

modelBuilder.Entity<Department>().Property(t =>t.Name).HasColumnName("DepartmentName");

重命名模型中未定义的外键

如果您选择不对CLR 类型定义外键,但希望指定它在数据库中应使用的名称,请编码如下:

modelBuilder.Entity<Course>()

.HasRequired(c => c.Department)

.WithMany(t => t.Courses)

.Map(m => m.MapKey("ChangedDepartmentID"));

配置字符串属性是否支持Unicode 内容

默认情况下,字符串为Unicode(SQLServer 中的nvarchar)。您可以使用IsUnicode 方法指定字符串应为varchar 类型。

modelBuilder.Entity<Department>()

.Property(t => t.Name)

.IsUnicode(false);

配置数据库列的数据类型

HasColumnType 方法支持映射到相同基本类型的不同表示。使用此方法并不支持在运行时执行任何数据转换。请注意,IsUnicode 是将列设置为 varchar 的首选方法,因为它与数据库无关。

modelBuilder.Entity<Department>()

.Property(p => p.Name)

.HasColumnType("varchar");

配置复杂类型的属性

对复杂类型配置标量属性有两种方法。

可以对ComplexTypeConfiguration 调用Property。

modelBuilder.ComplexType<Details>()

.Property(t => t.Location)

.HasMaxLength(20);

也可以使用点表示法访问复杂类型的属性。

modelBuilder.Entity<OnsiteCourse>()

.Property(t => t.Details.Location)

.HasMaxLength(20);

将属性配置为用作乐观并发令牌

要指定实体中的某个属性表示并发令牌,可使用 ConcurrencyCheck 特性或 IsConcurrencyToken 方法。

modelBuilder.Entity<OfficeAssignment>()

.Property(t => t.Timestamp)

.IsConcurrencyToken();

也可以使用IsRowVersion 方法将属性配置为数据库中的行版本。将属性设置为行版本会自动将它配置为乐观并发令牌。

modelBuilder.Entity<OfficeAssignment>()

.Property(t => t.Timestamp)

.IsRowVersion();

类型映射

将类指定为复杂类型

按约定,没有指定主键的类型将被视为复杂类型。在一些情况下,Code First 不会检测复杂类型(例如,如果您有名为“ID”的属性,但不想将它用作主键)。在此类情况下,您将使用 Fluent API 显式指定某类型是复杂类型。

modelBuilder.ComplexType<Details>();

指定不将CLR 实体类型映射到数据库中的表

以下示例显示如何排除一个 CLR 类型,使之不映射到数据库中的表。

modelBuilder.Ignore<OnlineCourse>();

将CLR 实体类型映射到数据库中的特定表

Department 的所有属性都将映射到名为 t_ Department 的表中的列。

modelBuilder.Entity<Department>().ToTable("t_Department");

您也可以这样指定架构名称:

modelBuilder.Entity<Department>().ToTable("t_Department", "school");

映射“每个层次结构一张表(TPH)”继承

在 TPH 映射情形下,继承层次结构中的所有类型都将映射到同一个表。鉴别器列用于标识每行的类型。使用 Code First 创建模型时,TPH 参与继承层次结构的类型所用的默认策略。默认情况下,鉴别器列将添加到名为“Discriminator”的表,且层次结构中每个类型的 CLR 类型名称都将用作鉴别器值。可以使用 Fluent API 修改默认行为。

modelBuilder.Entity<Course>()

.Map<Course>(m=> m.Requires("Type").HasValue("Course"))

.Map<OnsiteCourse>(m=> m.Requires("Type").HasValue("OnsiteCourse"));

映射“每个类型一张表(TPT)”继承

在 TPT 映射情形下,所有类型分别映射到不同的表。仅属于某个基类型或派生类型的属性存储在映射到该类型的一个表中。映射到派生类型的表还会存储一个将派生表与基表联接的外键。

modelBuilder.Entity<Course>().ToTable("Course");

modelBuilder.Entity<OnsiteCourse>().ToTable("OnsiteCourse");

映射“每个具体类一张表(TPC)”继承

在 TPC 映射情形下,层次结构中的所有非抽象类型分别映射到不同的表。映射到派生类的表与映射到数据库中基类的表并无关系。类的所有属性(包括继承属性)都将映射到相应表的列。

调用MapInheritedProperties 方法来配置每个派生类型。MapInheritedProperties 将继承自基类的所有属性重新映射到派生类的表中的新列。

注意:因为属于TPC 继承层次结构的表并不使用同一个主键,因此,如果您让数据库生成的值具有相同标识种子,则在映射到子类的表中执行插入操作时,会产生重复的实体键。要解决此问题,可以为每个表指定不同的初始种子值,或关闭主键属性的标识。当使用 Code First 时,标识就是整数键属性的默认值。

modelBuilder.Entity<Course>()

.Property(c => c.CourseID)

.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);

modelBuilder.Entity<OnsiteCourse>().Map(m =>

{

m.MapInheritedProperties();

m.ToTable("OnsiteCourse");

});

modelBuilder.Entity<OnlineCourse>().Map(m =>

{

m.MapInheritedProperties();

m.ToTable("OnlineCourse");

});

将实体类型的CLR 属性映射到数据库中的多个表(实体拆分)

实体拆分允许一个实体类型的属性分散在多个表中。在以下示例中,Department 实体拆分到两个表中:Department 和DepartmentDetails。实体拆分通过多次调用 Map 方法将一部分属性映射到特定表。

modelBuilder.Entity<Department>()

.Map(m=>

{

m.Properties(t => new{ t.DepartmentID, t.Name });

m.ToTable("Department");

})

.Map(m=>

{

m.Properties(t=> new { t.DepartmentID, t.Administrator,t.StartDate, t.Budget });

m.ToTable("DepartmentDetails");

});

将多个实体类型映射到数据库中的一个表(表拆分)

以下示例将使用同一个主键的两个实体类型映射到同一个表。

modelBuilder.Entity<OfficeAssignment>()

.HasKey(t => t.InstructorID);

modelBuilder.Entity<Instructor>()

.HasRequired(t => t.OfficeAssignment)

.WithRequiredPrincipal(t =>t.Instructor);

modelBuilder.Entity<Instructor>().ToTable("Instructor");

modelBuilder.Entity<OfficeAssignment>().ToTable("Instructor");

使用FluentAPI配置关系

简介

使用FluentAPI配置关系的时候,首先要获得一个EntityTypeConfiguration实例,然后使用其上的HasRequired, HasOptional或者 HasMany方法来指定当前实体参与的关系类型。HasRequired 和HasOptional方法需要一个lambda表达式来指定一个导航属性,HasMany方法需要一个lambda表达式指定一个集合导航属性。然后可以使用WithRequired, WithOptional和WithMany方法来指定反向导航属性,这些方法有不带参数的重载用来指定单向导航。

之后还可以使用HasForeignKey方法来指定外键属性。

配置【必须-可选】关系(1-0..1)

OfficeAssignment的键属性不符合命名约定,所以需要我们显式指定。下面的关系表明,OfficeAssignment的Instructor必须存在,但是Instructor的OfficeAssignment不是必须存在的。

modelBuilder.Entity<OfficeAssignment>()

.HasKey(t => t.InstructorID);

// Map one-to-zero or one relationship

modelBuilder.Entity<OfficeAssignment>()

.HasRequired(t => t.Instructor)

.WithOptional(t => t.OfficeAssignment);

配置两端都是必须的关系(1-1)

大多数情况下,EF都能推断哪一个类型是依赖项或者是主体项。然而当关系的两端都是必须的或者都是可选的,那么EF就不能识别依赖项或者是主体项。如果关系两端都是必须的,那么在HasRequired方法后使用WithRequiredPrincipal或者WithRequiredDependent来确定主体。如果关系两端都是可选的,那么在HasRequired方法后使用WithOptionalPrincipal和WithOptionalDependent。

modelBuilder.Entity<OfficeAssignment>()

.HasKey(t => t.InstructorID);

modelBuilder.Entity<Instructor>()

.HasRequired(t => t.OfficeAssignment)

.WithRequiredPrincipal(t => t.Instructor);

配置多对多关系

下面的代码配置了一个多对多关系,CodeFirst会使用命名约定来创建连接表,命名约定会使用Course_CourseID 和 Instructor_InstructorID作为连接表的列。

modelBuilder.Entity<Course>()

.HasMany(t => t.Instructors)

.WithMany(t => t.Courses);

如果想指定连接表的表名和列名,需要使用Map方法,如下:

modelBuilder.Entity<Course>()

.HasMany(t => t.Instructors)

.WithMany(t => t.Courses)

.Map(m =>

{

m.ToTable("CourseInstructor");

m.MapLeftKey("CourseID");

m.MapRightKey("InstructorID");

});

配置单向导航属

所谓单向导航属性指的是只在关系的一端定义了导航属性。按照约定,CodeFirst将单向导航理解为一对多关系,如果需要一对一的单向导航属性,需要使用如下方法:

modelBuilder.Entity<OfficeAssignment>()

.HasKey(t => t.InstructorID);

modelBuilder.Entity<Instructor>()

.HasRequired(t => t.OfficeAssignment)

.WithRequiredPrincipal();

启用级联删除

使用WillCascadeOnDelete方法来配置关系是否允许级联删除。如果外键是不可空的,CodeFirst默认会设置级联删除;否则,不会设置级联删除,当主体被删除后,外键将会被置空。

可以使用如下代码移除此约定:

modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();

modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();

下面的代码片段配置为外键不能为空,而且禁用了级联删除。

modelBuilder.Entity<Course>()

.HasRequired(t => t.Department)

.WithMany(t => t.Courses)

.HasForeignKey(d => d.DepartmentID)

.WillCascadeOnDelete(false);

配置组合外键

下面的代码配置了组合外键

modelBuilder.Entity<Department>()

.HasKey(d => new{ d.DepartmentID, d.Name });

// Composite foreign key

modelBuilder.Entity<Course>()

.HasRequired(c => c.Department)

.WithMany(d => d.Courses)

.HasForeignKey(d => new { d.DepartmentID, d.DepartmentName });

配置不符合命名约定的外键属性

SomeDepartmentID属性不符合外键命名约定,需要使用如下方法将其设置为外键属性:

modelBuilder.Entity<Course>()

.HasRequired(c => c.Department)

.WithMany(d => d.Courses)

.HasForeignKey(c =>c.SomeDepartmentID);

展开全文

使用Fluent API 配置/映射属性和类型的更多相关文章

  1. 使用 Fluent API 配置/映射属性和类型(摘自微软Data Access and Storage)

    使用 Fluent API 配置/映射属性和类型 使用实体框架 Code First 时,默认行为是使用一组 EF 中内嵌的约定将 POCO 类映射到表.但是,有时您无法或不想遵守这些约定,需要将实体 ...

  2. 使用 Fluent API 配置/映射属性和类型

    使用 Fluent API 配置/映射属性和类型 使用实体框架 Code First 时,默认行为是使用一组 EF 中内嵌的约定将 POCO 类映射到表.但是,有时您无法或不想遵守这些约定,需要将实体 ...

  3. 使用 Fluent API 配置/映射属性和类型2

    1.将多个实体类映射到数据库中的一个表 要将多个实体映射到一个数据库表需要满足: a. 两个实体必须是一对一关系 b.两个实体共享一个主键 public class MyContext:DbConte ...

  4. EF使用Fluent API配置映射关系

    定义一个继承自EntityTypeConfiguration<>泛型类的类来定义domain中每个类的数据库配置,在这个自定义类的构造函数中使用我们上次提到的那些方法配置数据库的映射. 映 ...

  5. Code First约定-Fluent API配置

    转自:http://blog.163.com/m13864039250_1/blog/static/2138652482015283397609/ 用Fluent API 配置/映射属性和类型 简介 ...

  6. Entity Framework Code First (五)Fluent API - 配置关系

    上一篇文章我们讲解了如何用 Fluent API 来配置/映射属性和类型,本文将把重点放在其是如何配置关系的. 文中所使用代码如下 public class Student { public int ...

  7. Entity Framework Code First (五)Fluent API - 配置关系 转载 https://www.cnblogs.com/panchunting/p/entity-framework-code-first-fluent-api-configuring-relationships.html

    上一篇文章我们讲解了如何用 Fluent API 来配置/映射属性和类型,本文将把重点放在其是如何配置关系的. 文中所使用代码如下 public class Student { public int ...

  8. Entity Framework 实体框架的形成之旅--Code First模式中使用 Fluent API 配置(6)

    在前面的随笔<Entity Framework 实体框架的形成之旅--Code First的框架设计(5)>里介绍了基于Code First模式的实体框架的经验,这种方式自动处理出来的模式 ...

  9. EF里的默认映射以及如何使用Data Annotations和Fluent API配置数据库的映射

    I.EF里的默认映射 上篇文章演示的通过定义实体类就可以自动生成数据库,并且EF自动设置了数据库的主键.外键以及表名和字段的类型等,这就是EF里的默认映射.具体分为: 数据库映射:Code First ...

随机推荐

  1. Web 安全:隐藏服务版本号

    前述 为了防止一些恶意用户对 apache.nginx.php 等显示出来的版本信息进行攻击,生产环境需要关闭 web 服务的版本信息: apache 隐藏版本号 修改 httpd.conf,添加如下 ...

  2. asp.net MVC中使用entity framework出现从 datetime2 数据类型到 datetime 数据类型的转换产生一个超出范围的值”的处理

    方法一: 使用DateTime类型的字段在作为参数传入到数据库前记得赋值,并日期要大于1753年1月1日. 方法二: 将DateTime类型的字段修改为DateTime?类型,由于可空类型的默认值都是 ...

  3. MVP模式

    一.软件设计鼻祖MVC 1.1.MVC 第一次听到MVC这个名词是在C#中,相信对于MVC大家都已经很熟悉了,作为一种软件设计模式,MVC这个概念已经诞生好多年了. 如果你已经开发一段时间的iOS应用 ...

  4. android开发之自定义组件

    android开发之自定义组件 一:自定义组件: 我认为,自定义组件就是android给我们提供的的一个空白的可以编辑的图片,它帮助我们实现的我们想要的界面,也就是通过自定义组件我们可以把我们要登入的 ...

  5. Oracle数据库中调用Java类开发存储过程、函数的方法

    Oracle数据库中调用Java类开发存储过程.函数的方法 时间:2014年12月24日  浏览:5538次 oracle数据库的开发非常灵活,不仅支持最基本的SQL,而且还提供了独有的PL/SQL, ...

  6. Java多线程与并发库高级应用-同步集合

    ArrayBlockingQueue LinkedBlockingQueue 数组是连续的一片内存 链表是不连续的一片内存  传统方式下用Collections工具类提供的synchronizedCo ...

  7. java的 clone方法

    1.java语言中没有明确提供指针的概念与用法,而实质上每个new语句返回的都是一个指针的引用,只不过在大部分情况下开发人员不需要关心如果取操作这个指针而已. 2.在java中处理基本数据类型时,都是 ...

  8. 【bzoj2818】 Gcd

    http://www.lydsy.com/JudgeOnline/problem.php?id=2818 (题目链接) 题意 求给定整数N,求1<=x,y<=N且Gcd(x,y)为素数的数 ...

  9. Java包的命名规范

    indi : 个体项目,指个人发起,但非自己独自完成的项目,可公开或私有项目,版权主要属于发起者. 包名为indi.发起者名.项目名.模块名*.*.* pers : 个人项目,指个人发起,独自完成,可 ...

  10. SqlServerException:拒绝对表对象的select,insert权限解决(新建账号导致的问题)

    继上一篇文章所述的问题,这次又出现了不能插入的问题.经过定位,也是由于我多选择了一个数据库用户角色的权限导致的,下面是详细的操作步骤 SqlServerException:拒绝了对对象 '...'(数 ...