Code First 关系 Fluent API

通过实体框架 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 实现的更多关系映射。

时间: 2024-11-01 09:30:29

Code First 关系 Fluent API的相关文章

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

此错误的原因是,你配置两个实体间的关系为一对一 然而我认为的一对一关系是,两者之间必须存在一个主体, 也就是说,你不能表1的外键是表2的主键并且表1的主键是表2的外键, 这样不符合数据库式吧? 我想多数人犯这个错误是无意的,并不是想表1的外键是表2的主键并且表1的主键是表2的外键, 怎么改呢?确定主体! 主体就是你要把其他实体的主键存进来的实体. 把非实体的导航属性删除就ok了. 关于CodeFirst异常:无法确定类型'XXX'和类型'YYY'之间的关联的主体端,必须使用关系 Fluent A

Code First约定-Fluent API配置

转自:http://blog.163.com/m13864039250_1/blog/static/2138652482015283397609/ 用Fluent API 配置/映射属性和类型 简介 通常通过重写派生DbContext 上的OnModelCreating 方法来访问Code First Fluent API.以下示例旨在显示如何使用 Fluent API 执行各种任务,您可以将代码复制出来并进行自定义,使之适用于您的模型. 属性映射 Property 方法用于为每个属于实体或复杂

Code First 中 Fluent API 的作用

Code First 的使用:http://www.cnblogs.com/sword-successful/archive/2012/12/31/2840391.html(写的很明白) Fluent API 的用法:https://msdn.microsoft.com/zh-cn/data/jj591617.aspx:http://www.dozer.cc/2012/09/entity-framework-code-first-configuring-property/

1.Relationship in Entity Framework Using Code First Approach With Fluent API【使用EF Code-First方式和Fluent API来探讨EF中的关系】

In this article, you will learn about relationships in Entity Framework using the Code First Approach with Fluent API. 在这篇文章中,你将会学习到使用EF Code-First方式和Fluent API来探讨EF中的关系(一对一,一对多,多对多). Introduction[介绍] A relationship, in the context of databases, is a

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

Code First约定-Fluent API配置 使用Fluent API 配置/映射属性和类型 简介 通常通过重写派生DbContext 上的OnModelCreating 方法来访问Code First Fluent API.以下示例旨在显示如何使用 Fluent API 执行各种任务,您可以将代码复制出来并进行自定义,使之适用于您的模型. 属性映射 使用Fluent API 配置/映射属性和类型 简介 通常通过重写派生DbContext 上的OnModelCreating 方法来访问Co

Entity Framework Code First关系映射约定

Entity Framework Code First关系映射约定 本篇随笔目录: 1.外键列名默认约定 2.一对多关系 3.一对一关系 4.多对多关系 5.一对多自反关系 6.多对多自反关系 在关系数据库中,不同表之间往往不是全部都单独存在,而是相互存在关联的.两个不同表之间可以存在外键依赖关系,一个表自身也可以有自反关系(表中的一个字段引用主键,从而也是外键字段). Entity Framework Code First默认多重关系的一些约定规则: 一对多关系:两个类中分别包含一个引用和一个

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

本篇随笔目录: 1.外键列名默认约定 2.一对多关系 3.一对一关系 4.多对多关系 5.一对多自反关系 6.多对多自反关系 在关系数据库中,不同表之间往往不是全部都单独存在,而是相互存在关联的.两个不同表之间可以存在外键依赖关系,一个表自身也可以有自反关系(表中的一个字段引用主键,从而也是外键字段). Entity Framework Code First默认多重关系的一些约定规则: 一对多关系:两个类中分别包含一个引用和一个集合属性,也可以是一个类包含另一个类的引用属性,或一个类包含另一个类

Fluent API 配置

EF里实体关系配置的方法,有两种: Data Annotation方式配置 也可以 Fluent API 方式配置 Fluent API 配置的方法 EF里的实体关系 Fluent API 配置分为Has和With系列的方法: Optional 可选的 Required 必须的 Many 多个 [举例]: A.HasRequired(a => a.B).WithOptional(b => b.A); 这里的a=>a.B是lambda表示写法,就是找到A类里的导航属性B(习惯问题:命名a不

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

上一篇文章我们讲解了如何用 Fluent API 来配置/映射属性和类型,本文将把重点放在其是如何配置关系的. 文中所使用代码如下 public class Student { public int ID { get; set; } public string Name { get; set; } public DateTime EnrollmentDate { get; set; } // Navigation properties public virtual Address Address