《Entity Framework 6 Recipes》中文翻译系列 (29) ------ 第五章 加载实体和导航属性之过滤预先加载的实体集合和修改外键关联
翻译的初衷以及为什么选择《Entity Framework 6 Recipes》来学习,请看本系列开篇
5-13 过滤预先加载的实体集合
问题
你想过滤预先加载的实体集合,另外,你想使用Code-First来管理数据访问
解决方案
实体框架不支持直接使用Include()时过滤关联实体集合,但我们可以通过创建一个匿名类型来完成同样的事情,匿名类型包含实体和要过滤的关联实体集合。
假设你有如图5-28所示的概念模型

图5-28 一个包含movies(电影)和它的categories(目录)的模型
在Visual Studio中添加一个名为Recipe13的控制台应用,并确保引用了实体框架6的库,NuGet可以很好的完成这个任务。在Reference目录上右键,并选择 Manage NeGet Packages(管理NeGet包),在Online页,定位并安装实体框架6的包。这样操作后,NeGet将下载,安装和配置实体框架6的库到你的项目中。
接下来我们创建两个实体对象:Moviet和Category,复制代码清单5-33中的属性到这三个类中。
代码清单5-33. 实体类
public class Category
{
public Category()
{
Movies = new HashSet<Movie>();
} public int CategoryId { get; set; }
public string Name { get; set; }
public string ReleaseType { get; set; } public virtual ICollection<Movie> Movies { get; set; }
} public class Movie
{
public int MovieId { get; set; }
public string Name { get; set; }
public string Rating { get; set; }
public int CategoryId { get; set; } public virtual Category Category { get; set; }
}
接下来,创建一个名为Recipe13Context的类,并将代码清单5-34中的代码添加到其中,并确保其派生到DbContext类。
代码清单5-34. 上下文
public class Recipe13Context : DbContext
{
public Recipe13Context()
: base("Recipe13ConnectionString")
{
//禁用实体框架的模型兼容
Database.SetInitializer<Recipe13Context>(null);
} public DbSet<Category> Categories { get; set; }
public DbSet<Movie> Movies { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Category>().ToTable("Chapter5.Category");
modelBuilder.Entity<Movie>().ToTable("Chapter5.Movie");
}
}
接下来添加App.Config文件到项目中,并使用代码清单5-35中的代码添加到文件的ConnectionStrings小节下。
代码清单5-35. 连接字符串
<connectionStrings>
<add name="Recipe13ConnectionString"
connectionString="Data Source=.;
Initial Catalog=EFRecipes;
Integrated Security=True;
MultipleActiveResultSets=True"
providerName="System.Data.SqlClient" />
</connectionStrings>
为了过滤categoryes和与之关联的movies,请按代码清单5-36的方式进行。
代码清单5-36.过滤预先加载的实体集合
using (var context = new Recipe13Context())
{
var cat1 = new Category {Name = "Science Fiction", ReleaseType = "DVD"};
var cat2 = new Category {Name = "Thriller", ReleaseType = "Blu-Ray"};
var movie1 = new Movie {Name = "Return to the Moon", Category = cat1, Rating = "PG-13"};
var movie2 = new Movie {Name = "Street Smarts", Category = cat2, Rating = "PG-13"};
var movie3 = new Movie {Name = "Alien Revenge", Category = cat1, Rating = "R"};
var movie4 = new Movie {Name = "Saturday Nights", Category = cat1, Rating = "PG-13"};
context.Categories.Add(cat1);
context.Categories.Add(cat2);
context.Movies.Add(movie1);
context.Movies.Add(movie2);
context.Movies.Add(movie3);
context.Movies.Add(movie4);
context.SaveChanges();
} using (var context = new Recipe13Context())
{
// 通过ReleaseType和Rating过虑
// 创建匿名类型集合
var cats = from c in context.Categories
where c.ReleaseType == "DVD"
select new
{
category = c,
movies = c.Movies.Where(m => m.Rating == "PG-13")
}; Console.WriteLine("PG-13 Movies Released on DVD");
Console.WriteLine("============================");
foreach (var cat in cats)
{
var category = cat.category;
Console.WriteLine("Category: {0}", category.Name);
foreach (var movie in cat.movies)
{
Console.WriteLine("\tMovie: {0}", movie.Name);
}
}
} Console.WriteLine("Press <enter> to continue...");
Console.ReadLine();
代码清单5-36输出如下:
PG- Movies Released on DVD
============================
Category: Science Fiction
Movie: Return to the Moon
Movie: Saturday Nights
原理
代码清单5-36,先创建并初始化categoryes和movies。为了保持简短,我们只创建了两个目录和4部电影。
在查询中,我们创建一个匿名类型的集合,它包含category实例,和在目录中过滤后的movies实例。该查询也过滤了目录集合,只获取发行类型为“DVD"的目录。在示例中,只有一个目录的发行类型为“DVD"。这里我们依赖关系跨度(relationship span)将movies附加到categories。
这个方法凭借匿名对象帮助我们绕开了预先加载中的限制,预先加载不允许我们过滤预先加载的实体集合。注意,正如前面小节中的示例演示的那样,当我们显式加载时,我们能过虑一个预先加载的实体集合。记住,匿名类型对象的生命周期范围只在创建它的方法中,方法不能返回匿名类型。如果我们目标是返回一个应用后面要处理的实体集,那么我们需要创建一个确切的类型来存放数据,然后将它返回。在我们的示例中,这个确切的类应该是一个简单的类,它只创建两个属性:Category 和一个Movies集合。
5-14 修改外键关联
问题
你想修改外键关联。
解决方案
实体框架提供了两种方式来修改外键关联,你可以将一个关联的实体添加到导航属性集合或者赋值给导航属性。你还可以将关联实体的键值设置成外键值。
假设你有如图5-29所示的模型。

图5-29 一个包含client和Invoice的模型
按下面的步骤,使用两种不同的方式,修改client实体和nvoice实体间的外键关联:
1、右键你的项目,选择Add(添加) ➤ADO.NET Entity Data Model(ADO.NET 实体数据模型)。导入Client和Invoice表。确保勾上选项 Include foreign key columns in the model(包含外键列到模型),它默认是勾选上的。如图5-30所示。这样会从数据库中导入外键关联,它不是多对多关系。

图5-30 勾上选项 Include foreign key columns in the model(包含外键列到模型)将会为数据中的关系创建外键关联。数据中的关系不是多对多关系。
2、使用代码清单5-37演示修改外键关联的方法。
代码清单5-37. 演示修改外键关联
using (var context = new EFRecipesEntities())
{
var client1 = new Client {Name = "Karen Standfield", ClientId = };
var invoice1 = new Invoice {InvoiceDate = DateTime.Parse("4/1/10"), Amount = 29.95M};
var invoice2 = new Invoice {InvoiceDate = DateTime.Parse("4/2/10"), Amount = 49.95M};
var invoice3 = new Invoice {InvoiceDate = DateTime.Parse("4/3/10"), Amount = 102.95M};
var invoice4 = new Invoice {InvoiceDate = DateTime.Parse("4/4/10"), Amount = 45.99M}; // 添加一个invoice到client的导航属性集合Invoices
client1.Invoices.Add(invoice1); //直接分配一个外键值
invoice2.ClientId = ; //使用一个“假”实体附加一存在的行
context.Database.ExecuteSqlCommand("insert into chapter5.client values (2, 'Phil Marlowe')");
var client2 = new Client {ClientId = };
context.Clients.Attach(client2);
invoice3.Client = client2; // 使用Client引用
invoice4.Client = client1; //保存更改
context.Clients.Add(client1);
context.Invoices.Add(invoice2);
context.Invoices.Add(invoice3);
context.Invoices.Add(invoice4);
context.SaveChanges();
} using (var context = new EFRecipesEntities())
{
foreach (var client in context.Clients)
{
Console.WriteLine("Client: {0}", client.Name);
foreach (var invoice in client.Invoices)
{
Console.WriteLine("\t{0} for {1}", invoice.InvoiceDate.ToShortDateString(),
invoice.Amount.ToString("C"));
}
}
} Console.WriteLine("Press <enter> to continue...");
Console.ReadLine();
代码清单5-37的输出如下:
Client: Karen Standfield
// for $29.95
// for $45.99
// for $49.95
Client: Phil Marlowe
// for $102.95
原理
实体框架支持独立关联和外键关联。对于独立关联,由关联间的实体自行跟踪,修改关联的唯一方式是通过修改对象引用。
对于外键关联,你可以通过修改对象引用,或是直接修改外键属性值来修改关联。外键关联不能用作多对多关系。
注意 记住,外键关联简单易用,它是默认方法,也是实体框架开发团队推荐的方法。除非你有具体的业务要求使用独立关联。否则请使用外键关联。
表5-1展示了独立关联和外键关联之间的主要区别。
表5-1. 独立关联和外键关联之间的区别

至此第5章结束,下篇我们将进入第六章,感谢你的阅读。觉得有帮助的话,点击下边的推荐以示支持。谢谢!
实体框架交流QQ群: 458326058,欢迎有兴趣的朋友加入一起交流
谢谢大家的持续关注,我的博客地址:http://www.cnblogs.com/VolcanoCloud/
《Entity Framework 6 Recipes》中文翻译系列 (29) ------ 第五章 加载实体和导航属性之过滤预先加载的实体集合和修改外键关联的更多相关文章
- 《Entity Framework 6 Recipes》中文翻译系列 (28) ------ 第五章 加载实体和导航属性之测试实体是否加载与显式加载关联实体
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 5-11 测试实体引用或实体集合是否加载 问题 你想测试关联实体或实体集合是否已经 ...
- 《Entity Framework 6 Recipes》中文翻译系列 (15) -----第三章 查询之与列表值比较和过滤关联实体
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 3-8与列表值比较 问题 你想查询一个实体,条件是给定的列表中包含指定属性的值. 解 ...
- 《Entity Framework 6 Recipes》中文翻译系列 (22) -----第五章 加载实体和导航属性之延迟加载
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 第五章 加载实体和导航属性 实体框架提供了非常棒的建模环境,它允许开发人员可视化地使 ...
- 《Entity Framework 6 Recipes》中文翻译系列 (23) -----第五章 加载实体和导航属性之预先加载与Find()方法
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 5-2 预先加载关联实体 问题 你想在一次数据交互中加载一个实体和与它相关联实体. ...
- 《Entity Framework 6 Recipes》中文翻译系列 (24) ------ 第五章 加载实体和导航属性之查询内存对象
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 5-4 查询内存对象 问题 你想使用模型中的实体对象,如果他们已经加载到上下文中, ...
- 《Entity Framework 6 Recipes》中文翻译系列 (25) ------ 第五章 加载实体和导航属性之加载完整的对象图和派生类型上的导航属性
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 5-5 加载完整的对象图 问题 你有一个包含许多关联实体的模型,你想在一次查询中, ...
- 《Entity Framework 6 Recipes》中文翻译系列 (26) ------ 第五章 加载实体和导航属性之延缓加载关联实体和在别的LINQ查询操作中使用Include()方法
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 5-7 在别的LINQ查询操作中使用Include()方法 问题 你有一个LINQ ...
- 《Entity Framework 6 Recipes》中文翻译系列 (27) ------ 第五章 加载实体和导航属性之关联实体过滤、排序、执行聚合操作
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 5-9 关联实体过滤和排序 问题 你有一实体的实例,你想加载应用了过滤和排序的相关 ...
- 《Entity Framework 6 Recipes》翻译系列 (1) -----第一章 开始使用实体框架之历史和框架简述
微软的Entity Framework 受到越来越多人的关注和使用,Entity Framework7.0版本也即将发行.虽然已经开源,可遗憾的是,国内没有关于它的书籍,更不用说好书了,可能是因为EF ...
随机推荐
- jq size()与length的区别
size()跟length同样的功能,都是取元素的个数,那么他们的区别是什么呢,一个是方法一个是属性? 从图中可以看到size()方法比length慢38%,原因何在? size: function ...
- Android 谈谈封装那些事 --BaseActivity 和 BaseFragment(二)
1.前言 昨天谈了BaseActivity的封装,Android谈谈封装那些事--BaseActivity和BaseFragment(一)有很多小伙伴提了很多建议,比如: 通用标题栏可以自定义Vi ...
- 进击的Python【第六章】:Python的高级应用(三)面向对象编程
Python的高级应用(三)面向对象编程 本章学习要点: 面向对象编程介绍 面向对象与面向过程编程的区别 为什么要用面向对象编程思想 面向对象的相关概念 一.面向对象编程介绍 面向对象程序设计(英语: ...
- python strip()函数 介绍
python strip()函数 介绍,需要的朋友可以参考一下 函数原型 声明:s为字符串,rm为要删除的字符序列 s.strip(rm) 删除s字符串中开头.结尾处,位于 rm删除 ...
- mac+phpstorm+xampp断点调试
1.下载xdebug文件 http://xdebug.org/wizard.php 将phpinfo()的源代码复制到文本框中,xdebug会提示如何配置和下载哪个版本的xdebug. 全部下载地址: ...
- LeetCode 258. Add Digits
Problem: Given a non-negative integer num, repeatedly add all its digits until the result has only o ...
- 丢手帕问题即约瑟夫问题的PHP解法
问题描述:n个人排成一圈.从某个人开始,依次报数,数到m的人被杀死.下一个人重新从1开始报数,数到m的人被杀死.直到剩下最后一个人. 解决思路:从数学角度去看,每一次报数决定谁去死是一个n.m的求余数 ...
- 技能收获与C语言学习
你有什么技能比大多人(超过90%以上)更好? 我会的东西很多,喜欢的东西太多,但是很遗憾广而不专,会而不精.学了很多东西我都是为了娱乐,因为以前我们那里过于强调学习,很多爱好也都被扼杀在摇篮里.我觉得 ...
- SpringMVC -- 注解
@Entity -- 实体类@Table(name = "hat_province", catalog = "news") -- 对应的表name -- 表名c ...
- Synchronized同步性与可见性
Synchronized是具有同步性与可见性的,那么什么是同步性与可见性呢? (1)同步性:同步性就是一个事物要么一起成功,要么一起失败,可谓是有福同享有难同当,就像A有10000去银行转5000给身 ...