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 实现的更多关系映射。
Code First 关系 Fluent API的更多相关文章
- Code First约定-Fluent API配置
转自:http://blog.163.com/m13864039250_1/blog/static/2138652482015283397609/ 用Fluent API 配置/映射属性和类型 简介 ...
- code First 三 Fluent API
Entity Framework Fluent API用于配置域类以覆盖约定. 在实体框架6中,DbModelBuilder类充当Fluent API,我们可以使用它来配置许多不同的东西.它提供了比数 ...
- 关于CodeFirst异常:无法确定类型'XXX'和类型‘YYY’之间的关联的主体端,必须使用关系 Fluent API 或数据注释显式配置此关联的主体端。
此错误的原因是,你配置两个实体间的关系为一对一 然而我认为的一对一关系是,两者之间必须存在一个主体, 也就是说,你不能表1的外键是表2的主键并且表1的主键是表2的外键, 这样不符合数据库式吧? 我想多 ...
- Code First:Fluent API
DbContext类有一个OnModelCreating方法,可以在这里配置模型,该方法接收一个类型为DbModelBuilder的建造者,本文介绍的为Data Anotation的等价方法,这些代码 ...
- 1.【使用EF Code-First方式和Fluent API来探讨EF中的关系】
原文链接:http://www.c-sharpcorner.com/UploadFile/3d39b4/relationship-in-entity-framework-using-code-firs ...
- Entity Framework Code First关系映射约定
本篇随笔目录: 1.外键列名默认约定 2.一对多关系 3.一对一关系 4.多对多关系 5.一对多自反关系 6.多对多自反关系 在关系数据库中,不同表之间往往不是全部都单独存在,而是相互存在关联的.两个 ...
- 使用Fluent API 配置/映射属性和类型
Code First约定-Fluent API配置 使用Fluent API 配置/映射属性和类型 简介 通常通过重写派生DbContext 上的OnModelCreating 方法来访问Code F ...
- Entity Framework Code First关系映射约定【l转发】
本篇随笔目录: 1.外键列名默认约定 2.一对多关系 3.一对一关系 4.多对多关系 5.一对多自反关系 6.多对多自反关系 在关系数据库中,不同表之间往往不是全部都单独存在,而是相互存在关联的.两个 ...
- Fluent API 配置
EF里实体关系配置的方法,有两种: Data Annotation方式配置 也可以 Fluent API 方式配置 Fluent API 配置的方法 EF里的实体关系 Fluent API 配置分为H ...
随机推荐
- java多线程synchronized底层实现
一直想把这个特别重要的关键词的底层实现搞明白.(当然现在也没有完全明白,如果有错误以后修改这篇文章) 首先,这个关键词synchronize可以说是个语法糖,它的具体用法网上很多博客都讲的比较明了了. ...
- Struts中Action三种接收参数的方式?
前言: 前面已经有一篇随笔介绍了Struts2的大概原理.本文就Struts2中Action与jsp页面进行数据对接时介绍几种常见方法! 值栈ValueStack 3个Action Action1 p ...
- C++ 异常机制分析
C++异常机制概述 异常处理是C++的一项语言机制,用于在程序中处理异常事件.异常事件在C++中表示为异常对象.异常事件发生时,程序使用throw关键字抛出异常表达式,抛出点称为异常出现点,由操作系统 ...
- [日常训练]school
Description 众所周知,家离学校很远.于是,每天算准了时间出发,以保证能在上课铃响前 秒到达学校. 不幸的是,市最近正在修路.这就导致有些路可能无法通行,因而可能导致迟到. 不打算改变他的出 ...
- 研究dotnet动态语言IronPython(对应Java的Groovy)
Java的标配动态语言Groovy,两者搭配可以说是完美!大规模运用的项目,如:Jenkins,通过动态语言可以弥补先天系统缺陷的bug,再者就是加强自己的业务逻辑等. 那么换过dotnet上,对应的 ...
- Jenkins环境拓扑及部署流程
环境拓扑图: 部署流程:
- HDU1150 Machine Schedule
匈牙利算法 目前为止还是半懂不懂的状态 #include<iostream> #include<cstdio> #include<cstring> using na ...
- crawler: 爬虫的基本结构
目前我所知道的爬虫在获取页面信息上,分为静态爬虫和动态爬虫:静态爬虫主要用于获取静态页面,获取速度一般也比较快:但是现在很多网站的页面都是采用动态页面,当我们用爬虫去获取信息的时候,页面的信息可能还没 ...
- 数据结构算法C语言实现(二十七)--- 7.2图的遍历
一.简述 栈与队列,DFS与BFS.仅以连接表为例实现. 二.头文件 BFS要用到的头文件 //3_4_part1.h /** author:zhaoyu email:zhaoyu1995.com@g ...
- [SVN Mac的SVN使用]
在Windows环境中,我们一般使用TortoiseSVN来搭建svn环境.在Mac环境下,由于Mac自带了svn的服务器端和客户端功能,所以我们可以在不装任何第三方软件的前提下使用svn功能,不过还 ...