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

使用实体框架 Code First 时,默认行为是使用一组 EF 中内嵌的约定将 POCO 类映射到表。但是,有时您无法或不想遵守这些约定,需要将实体映射到约定指示外的其他对象。

您主要可使用两种方法来配置 EF 使之在映射数据时绕开约定,这两种方法就是 注释和 EF Fluent API。注释仅涵盖一部分 Fluent API 功能,因此有一些情况是无法使用注释实现的。本文旨在演示如何使用 Fluent API 来配置属性。

除了控制映射之外,Fluent API 和注释还可用于配置约束,如字段长度或必需属性,此时这些配置的约束将影响 Code First 所创建的数据库以及实体框架所执行的验证。

内容

简介

通常通过重写派生 DbContext 上的 OnModelCreating 方法来访问 Code First Fluent API。以下示例旨在显示如何使用 Fluent API 执行各种任务,您可以将代码复制出来并进行自定义,使之适用于您的模型。如果您想查看可按原样使用这些示例的模型,本文末尾提供了该模型。

属性映射

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

配置主键

有关主键的实体框架约定如下:

1.       您的类定义名称为“ID”或“Id”的属性

2.       或类名后跟“ID”或“Id”。

要显式将某个属性设置为主键,可使用 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(SQL Server 中的 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");

示例中使用的模型

以下 Code First 模型用于本页的示例。

using System.Data.Entity; 
using System.Data.Entity.ModelConfiguration.Conventions; 
// add a reference to System.ComponentModel.DataAnnotations DLL 
using System.ComponentModel.DataAnnotations; 
using System.Collections.Generic; 
using System; 
 
public class SchoolEntities : DbContext 

    public DbSet<Course> Courses { get; set; } 
    public DbSet<Department> Departments { get; set; } 
    public DbSet<Instructor> Instructors { get; set; } 
    public DbSet<OfficeAssignment> OfficeAssignments { get; set; } 
 
    protected override void OnModelCreating(DbModelBuilder modelBuilder) 
    { 
        // Configure Code First to ignore PluralizingTableName convention 
        // If you keep this convention then the generated tables will have pluralized names. 
        modelBuilder.Conventions.Remove<PluralizingTableNameConvention>(); 
    } 

 
public class Department 

    public Department() 
    { 
        this.Courses = new HashSet<Course>(); 
    } 
    // Primary key 
    public int DepartmentID { get; set; } 
    public string Name { get; set; } 
    public decimal Budget { get; set; } 
    public System.DateTime StartDate { get; set; } 
    public int? Administrator { get; set; } 
 
    // Navigation property 
    public virtual ICollection<Course> Courses { get; private set; } 

 
public class Course 

    public Course() 
    { 
        this.Instructors = new HashSet<Instructor>(); 
    } 
    // Primary key 
    public int CourseID { get; set; } 
 
    public string Title { get; set; } 
    public int Credits { get; set; } 
 
    // Foreign key 
    public int DepartmentID { get; set; } 
 
    // Navigation properties 
    public virtual Department Department { get; set; } 
    public virtual ICollection<Instructor> Instructors { get; private set; } 

 
public partial class OnlineCourse : Course 

    public string URL { get; set; } 

 
public partial class OnsiteCourse : Course 

    public OnsiteCourse() 
    { 
        Details = new Details(); 
    } 
 
    public Details Details { get; set; } 

 
public class Details 

    public System.DateTime Time { get; set; } 
    public string Location { get; set; } 
    public string Days { get; set; } 

     
public class Instructor 

    public Instructor() 
    { 
        this.Courses = new List<Course>(); 
    } 
 
    // Primary key 
    public int InstructorID { get; set; } 
    public string LastName { get; set; } 
    public string FirstName { get; set; } 
    public System.DateTime HireDate { get; set; } 
 
    // Navigation properties 
    public virtual ICollection<Course> Courses { get; private set; } 

 
public class OfficeAssignment 

    // Specifying InstructorID as a primary 
    [Key()] 
    public Int32 InstructorID { get; set; } 
 
    public string Location { get; set; } 
 
    // When the Entity Framework sees Timestamp attribute 
    // it configures ConcurrencyCheck and DatabaseGeneratedPattern=Computed. 
    [Timestamp] 
    public Byte[] Timestamp { get; set; } 
 
    // Navigation property 
    public virtual Instructor Instructor { get; set; } 
}

使用 Fluent API 配置/映射属性和类型(摘自微软Data Access and Storage)的更多相关文章

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

    Code First约定-Fluent API配置 使用Fluent API 配置/映射属性和类型 简介 通常通过重写派生DbContext 上的OnModelCreating 方法来访问Code F ...

  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. EF CodeFirst方式 Fluent Api配置

    一.One-to-One Relationship[一对一关系] 两个表之间,只能由一个记录在另外一个表中.每一个主键的值,只能关联到另外一张表的一条或者零条记录.请记住,这个一对一的关系不是非常的普 ...

  8. 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 ...

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

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

随机推荐

  1. Devexpress Winform Gridcontrol 中根据条件单元格的值改变单元格的颜色等属性。

    提供一下三种方法 1.使用设计器 点击gridcontrol控件,run designer,format Condtions, add,然后进行各种条件的设置. 2.用代码代替设计器. 实例代码: p ...

  2. could not build module 'XXXXXXXX'或者error: expected identifier or '(' 。一堆奇怪的错误————错误根源

    一堆奇怪的错误:1⃣️could not build module 'XXXXXXXX' 2⃣️error: expected identifier or '(' 3⃣️EDIT Setting Pr ...

  3. 软件测试第六周学习笔记之“Win8 APP应用程序的白盒测试”

    这周的学习笔记我想写点自己关于实验中碰到的问题和感想. 因为这次做的是白盒测试,所以我决定去测试一下上回测试的app的功能函数. 这次我用的是单元测试项目来做的白盒测试: 创建单元测试的步骤: 1.点 ...

  4. vs2012 opencv 配置

    一直没有学习C++,以为该语言太过old,所以要学习新的咚咚.一番学习归来,在进行OpenCV的时候,还是要用到这个C++.几次琢磨,终于能够配置好相关的开发环境和问题初步处理,有些内容得赶快记录下来 ...

  5. jQuery lazyload 懒加载

    Lazy Load 是一个用 JavaScript 编写的 jQuery 插件. 它可以延迟加载长页面中的图片. 在浏览器可视区域外的图片不会被载入, 直到用户将页面滚动到它们所在的位置. 这与图片预 ...

  6. 安全协议系列(三)----CCMP与WPA-PSK

    本节讨论 CCM 在 WiFi 中的实际应用 -- CCMP 协议 根据 RFC 3610,完成 CCMP 报文的加解密,需要提供:分组密钥(K).随机数(Nonce).附加认证数据(AAD),这三个 ...

  7. C语言程序设计第5堂作业

    一.本次课学习主要内容及知识结构点: 二.实验内容:(60分) 1. 求奇数和.输入一批正整数(以零或负数为结束标志),求其中的奇数和.试编写相应程序. 2. 展开式求和.输入一个实数 x,计算并输出 ...

  8. IOS BLE蓝牙4.0

    前言: 自己做的项目里面有这么一个功能,总结归纳一下. 先导入必要的框架  CoreBluetooth.framework 在要用到蓝牙的文件里面导入以下头文件 #import <CoreBlu ...

  9. Eclipse/JavaWeb (一)三大框架之struts框架 持续更新中...

    最近主要把javaweb的三大框架过一遍. (一)发展历史 传统的Java Web应用程序是采用JSP+Servlet+Javabean来实现的,这种模式实现了最基本的MVC分层,使得程序分为几层,有 ...

  10. [转] Jenkins实战演练之Windows系统节点管理

    [前提] 通过<Jenkins实战演练之Windows服务器快速搭建>(http://my.oschina.net/iware/blog /191818)和<Jenkins实战演练之 ...