通过实体框架 Code First,可以使用您自己的域类表示 EF 执行查询、更改跟踪和更新函数所依赖的模型。Code First 利用称为“约定先于配置”的编程模式。这意味着 Code First 将假设类遵循 EF 用于概念模型的架构约定。在这种情况下,EF 将能够找出自己工作所需的详细信息。但是,如果您的类不遵守这些约定,则可以向类中添加配置,以向 EF 提供它需要的信息。

Code First 为您提供了两种方法来向类中添加这些配置。一种方法是使用名为 DataAnnotations 的简单特性,另一种方法是使用 Code First 的 Fluent API,该 API 向您提供了在代码中以命令方式描述配置的方法。

本文将重点讨论如何在 Fluent API 中优化关系。Code First 约定非常适合根据指向子集合或单个类的属性识别类之间的常见关系。当类不使用外键时,Code First 可以推断数据库外键。但有时类提供的信息不足,Code First 无法正常处理这些关系。

介绍模型

我将从两个简单的类 Blog 和 Post 开始,这里 Blog 与 Post 有一对多关系。

public class Blog      {          public int Id { get; set; }          public string Title { get; set; }          public string BloggerName { get; set; }          public virtual ICollection<Post> Posts { get; set; }      }      public class Post      {          public int Id { get; set; }          public string Title { get; set; }          public DateTime DateCreated { get; set; }          public string Content { get; set; }          public int BlogId { get; set; }          public Blog Blog { get; set; }      }

了解一对多关系的约定

在类中定义一对多关系的一种常见方法是在一个类中包含一个子集合,然后在子类中包含一个外键属性和一个导航属性。在上面的示例中,Blog 有一个 Posts 属性,它是 Post 类型的 ICollection。Post 有一个外键 BlogID 和一个导航属性 Blog,该导航属性指回其父 Blog。

此设置符合 Code First 约定的预期,因此,Code First 将创建以下数据库表:

图 1

请注意,Code First 将 BlogId 用作数据库外键(Posts.BlogId 和 Blogs.Id 之间定义了主键/外键约束),该值不可为 Null。这是 Code First 约定根据类确定的。

没有外键属性时使用 HasRequired 提供帮助

如果 Post 类中没有 BlogId 属性但有导航属性 Blog,该怎么办呢?Code first 仍然能够创建关系,因为它知道这一 Blog 属性指回 Blog 实体。因此它仍然会创建图 2 中所示的数据库外键 Posts.Blog_Id,以及将其链接到 Blog.Id 的约束。

图 2

但有一个重要区别,Blog_Id 可以为 Null。可以添加与 Blog 绑定的 Posts。Code First 约定就是这样解释类的,但这可能并不符合您的意图。您可以使用 Fluent API 进行修复。

Code First 从类中构建模型时,将应用 Fluent API 配置。通过重写这里显示的 DbContext 类的 OnModelCreating 方法,可以注入配置。

public class BlogContext : DbContext       {          public DbSet<Blog> Blogs { get; set; }          public DbSet<Post> Posts { get; set; }          protected override void OnModelCreating(DbModelBuilder modelBuilder)          {           //使用 Fluent API 配置模型          }

DbModelBuilder 提供了一个配置挂钩。在这里,我们可以告诉模型生成器我们需要影响其中一个实体,您可以使用泛型指定是哪个实体,这里是 Post。访问它后,可以使用 HasRequired 方法(特定于关系)指定需要一个导航属性,在本例中为 Blog 属性。

modelBuilder.Entity<Post>().HasRequired(p => p.Blog);

这会产生两方面影响。首先,数据库中的 Blog_Id 将再次变为不可为 Null。实体框架仍将根据需要或在将更改保存回数据库前执行验证,以确保满足此要求。

配置非常规外键名称

类中有外键时,属性名称必须始终符合 Code First 约定。约定是:键是它所指向的类的名称(例如 Blog)和 _Id 或 Id 的组合。这就是 Code First 能够使用类 BlogId 中的原始属性的原因。

如果属性不符合约定,会怎么样呢?也许您使用了“FK”+ 父级 +“Id”?

public int FKBlogId { get; set; }

Code first 根本不知道 FKBlogId 应是外键。它将创建一个标准列来显示该属性,还将创建 Blog_Id 外键,因为它根据 Blog 属性确定需要这个外键。

图 3

此外,在代码中使用 Blog 和 Post 时,根本不会将 FKBlogId 识别为指回 Blog 的外键。如果有修复双向关系的代码,它将不会使用 FKBlogId 属性执行其任务。

可以使用 Fluent API 修复此问题,告诉 Code First 您的真实意图,使用 FKBlogId 作为与 Blog 的关系中的外键属性。可以从现成的配置开始。

在此配置中,将定义关系的两端:Blog 中指向多关系 (Posts) 的属性以及 Post 中指回父级 (Blog) 的属性。

首先需要添加 WithMany 方法,使用该方法可以指示 Blog 中的哪个属性包含 Many 关系。

modelBuilder.Entity<Post>().HasRequired(p => p.Blog)                  .WithMany(b => b.Posts)

然后可以向其添加 HasForeignKey 方法,指示 Post 的哪个属性是指回 Blog 的外键。最后,完整的映射如下:

modelBuilder.Entity<Post>().HasRequired(p => p.Blog)                  .WithMany(b => b.Posts)                  .HasForeignKey(p => p.FKBlogId);

现在,Code First 获得了需要的信息,能够创建正确的数据库架构(或正确映射到现有数据库),可以在与双向关系有关的应用程序中提供预期的行为。

图 4

在多对多关系中定义联接表架构

在类中,可以通过指向彼此的属性方便地描述多对多关系。例如,如果在模型中添加了一个 Tag 类来跟踪文章的标记,您需要它们之间有多对多关系。

下面是新的 Tag 类:

public class Tag      {          public int TagId{ get; set; }          public string Name { get; set; }          public ICollection<Post> Posts { get; set; }      }

下面是为 Post 类添加的新属性:

public ICollection<Tag> Tags { get; set; }

Code first 需要联接表的命名是将这两个类的名称组合在一起并包含外键属性,其中每个属性都是类名称和键属性的组合。在本例中,我使用 Post.Id 和 Tag.TagId。如果使用 Code First 创建数据库,使用 Code First 约定的表应类似下面这样:

图 5

使用 Code First 构建数据库时,这可能不是问题。但如果映射到现有数据库,这样的命名可能根本无法对齐表。可以使用 Fluent API 描述表名和两个列名,可以显式命名一个、两个或全部三个。

下面介绍如何实现此映射。我们使用示例,其中需要定义所有三个名称。表应为 PostJoinTag,列应为 TagId 和 PostId。

您需要从 Entity 映射方法开始,可以选择从 Post 或 Tag 开始。这里我将使用 Post。然后需要指定关系的两端。与在上例中指定一对多关系的方式类似,可以使用 HasMany 和 WithMany 进行。在这里,我通过 Post 实体的 Tags 属性指示它具有“多”关系,因此,Tag 通过其 Posts 属性与其“多”关系有关系。

modelBuilder.Entity<Post>()                  .HasMany(p => p.Tags)                  .WithMany(t => t.Posts)                  .Map(mc =>                     {                         mc.ToTable("PostJoinTag");                         mc.MapLeftKey("PostId");                         mc.MapRightKey("TagId");                     });

图 6

指定哪个是 MapLeftKey 和哪个是 MapRightKey 时需要小心。左键应为所指向的第一个类的键,即 Post,右键为关系的另一侧。如果搞反了方向,数据将不能正确存储,用户会很迷惑。

摘要

您已经了解了使用 Code First 的关系 Fluent API 可以描述的关系映射的一些可能情况。这里我用一对多关系和多对多关系修复了约定对我意图的误解。您还可以使用其他映射,可以单独使用或组合使用。尽管它们开始看起来可能令人迷惑和多余,例如 IsRequired 和 HasRequired 或 WithMany 和 HasMany。但现在您已经了解到这些映射有非常明确的作用,它们的不同是有原因的。

请查阅实体框架 4.1 MSDN 文档  http://msdn.microsoft.com/library/gg696172(v=VS.10).aspx 和实体框架团队博客 ( http://blogs.msdn.com/adonet ),了解可以使用 Fluent API 实现的更多关系映射。

Code First 关系 Fluent API的更多相关文章

  1. Code First约定-Fluent API配置

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

  2. code First 三 Fluent API

    Entity Framework Fluent API用于配置域类以覆盖约定. 在实体框架6中,DbModelBuilder类充当Fluent API,我们可以使用它来配置许多不同的东西.它提供了比数 ...

  3. 关于CodeFirst异常:无法确定类型'XXX'和类型‘YYY’之间的关联的主体端,必须使用关系 Fluent API 或数据注释显式配置此关联的主体端。

    此错误的原因是,你配置两个实体间的关系为一对一 然而我认为的一对一关系是,两者之间必须存在一个主体, 也就是说,你不能表1的外键是表2的主键并且表1的主键是表2的外键, 这样不符合数据库式吧? 我想多 ...

  4. Code First:Fluent API

    DbContext类有一个OnModelCreating方法,可以在这里配置模型,该方法接收一个类型为DbModelBuilder的建造者,本文介绍的为Data Anotation的等价方法,这些代码 ...

  5. 1.【使用EF Code-First方式和Fluent API来探讨EF中的关系】

    原文链接:http://www.c-sharpcorner.com/UploadFile/3d39b4/relationship-in-entity-framework-using-code-firs ...

  6. Entity Framework Code First关系映射约定

    本篇随笔目录: 1.外键列名默认约定 2.一对多关系 3.一对一关系 4.多对多关系 5.一对多自反关系 6.多对多自反关系 在关系数据库中,不同表之间往往不是全部都单独存在,而是相互存在关联的.两个 ...

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

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

  8. Entity Framework Code First关系映射约定【l转发】

    本篇随笔目录: 1.外键列名默认约定 2.一对多关系 3.一对一关系 4.多对多关系 5.一对多自反关系 6.多对多自反关系 在关系数据库中,不同表之间往往不是全部都单独存在,而是相互存在关联的.两个 ...

  9. Fluent API 配置

    EF里实体关系配置的方法,有两种: Data Annotation方式配置 也可以 Fluent API 方式配置 Fluent API 配置的方法 EF里的实体关系 Fluent API 配置分为H ...

随机推荐

  1. 【codevs1743】 反转卡片

    http://codevs.cn/problem/1743/ (题目链接) 题意 给出一个序列{a1,a2,a3···},要求维护这样一种操作:将前a1个数反转,若第a1等于1,则停止操作. Solu ...

  2. 【poj1014】 Dividing

    http://poj.org/problem?id=1014 (题目链接) 题意 给出有分别价值为1,2,3,4,5,6的6种物品,输入6个数字,表示相应价值的物品的数量,问一下能不能将物品分成两份, ...

  3. ini_set()函数的使用 以及 post_max_size,upload_max_filesize的修改方法

    Apache服务器处理: ini_set('display_errors', 'Off');ini_set('memory_limit', -1); //-1 / 10240Mini_set(&quo ...

  4. Linux Network Related Drive

    catalog . 通过套接字通信 . 网络实现的分层模型 . 网络命名空间 . 套接字缓冲区 . 网络访问层 . 网络层 . 传输层 . 应用层 . 内核内部的网络通信 1. 通过套接字通信 Lin ...

  5. centos卸载console-kit-da

    最近发现系统多出来 很多 console-kit-da 及它的子进程 占用了不少资源 which console-kit-da(很奇怪 为什么找不到执行文件) rpm -qa | grep -i co ...

  6. iOS “智慧气象”APP中用到的第三方框架汇总

    “智慧气象”是我最近在公司接手的项目,已经完成最新版本的更新并上架,在此分享下其中用到的第三方框架的使用. 应用地址:APP商店搜索“智慧气象” MJRefresh(下拉刷新)业界知名下拉刷新框架就不 ...

  7. Guava集合-BiMap

    在本篇文章中我们将介绍Guava集合中的BiMap这个接口. com.google.common.collect Interface BiMap<K,V> BiMap接口的父接口是Map& ...

  8. 【Alpha阶段】第二次Scrum例会

    燃尽图软件存在bug,正在排查修复:(已修复)由于时区设置到了美国,图表显示有问题. 会议信息 时间:2016.10.18 22:00 时长:1h 地点:大运村1号公寓5楼楼道 类型:设计阶段阶段性会 ...

  9. OpenGLES入门笔记四

    原文参考地址:http://www.cnblogs.com/zilongshanren/archive/2011/08/08/2131019.html 一.编译Vertex Shaders和Fragm ...

  10. BZOJ3813: 奇数国

    传送门 欧拉函数+线段树 因为只有60个素数,所以把状态压成long long的形式.用线段树维护区间和和区间和中有多少个质数.然后xjb搞搞就行了,具体参见代码. //BZOJ 3813 //by ...