《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. 上下文

 1  public class Recipe13Context : DbContext
 2     {
 3         public Recipe13Context()
 4             : base("Recipe13ConnectionString")
 5         {
 6             //禁用实体框架的模型兼容
 7             Database.SetInitializer<Recipe13Context>(null);
 8         }
 9
10         public DbSet<Category> Categories { get; set; }
11         public DbSet<Movie> Movies { get; set; }
12
13         protected override void OnModelCreating(DbModelBuilder modelBuilder)
14         {
15             modelBuilder.Entity<Category>().ToTable("Chapter5.Category");
16             modelBuilder.Entity<Movie>().ToTable("Chapter5.Movie");
17         }
18     }

接下来添加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.过滤预先加载的实体集合

 1  using (var context = new Recipe13Context())
 2             {
 3                 var cat1 = new Category {Name = "Science Fiction", ReleaseType = "DVD"};
 4                 var cat2 = new Category {Name = "Thriller", ReleaseType = "Blu-Ray"};
 5                 var movie1 = new Movie {Name = "Return to the Moon", Category = cat1, Rating = "PG-13"};
 6                 var movie2 = new Movie {Name = "Street Smarts", Category = cat2, Rating = "PG-13"};
 7                 var movie3 = new Movie {Name = "Alien Revenge", Category = cat1, Rating = "R"};
 8                 var movie4 = new Movie {Name = "Saturday Nights", Category = cat1, Rating = "PG-13"};
 9                 context.Categories.Add(cat1);
10                 context.Categories.Add(cat2);
11                 context.Movies.Add(movie1);
12                 context.Movies.Add(movie2);
13                 context.Movies.Add(movie3);
14                 context.Movies.Add(movie4);
15                 context.SaveChanges();
16             }
17
18             using (var context = new Recipe13Context())
19             {
20                 // 通过ReleaseType和Rating过虑
21                 // 创建匿名类型集合
22                 var cats = from c in context.Categories
23                            where c.ReleaseType == "DVD"
24                            select new
25                                {
26                                    category = c,
27                                    movies = c.Movies.Where(m => m.Rating == "PG-13")
28                                };
29
30                 Console.WriteLine("PG-13 Movies Released on DVD");
31                 Console.WriteLine("============================");
32                 foreach (var cat in cats)
33                 {
34                     var category = cat.category;
35                     Console.WriteLine("Category: {0}", category.Name);
36                     foreach (var movie in cat.movies)
37                     {
38                         Console.WriteLine("\tMovie: {0}", movie.Name);
39                     }
40                 }
41             }
42
43             Console.WriteLine("Press <enter> to continue...");
44             Console.ReadLine();

代码清单5-36输出如下:

PG-13 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. 演示修改外键关联

 1  using (var context = new EFRecipesEntities())
 2             {
 3                 var client1 = new Client {Name = "Karen Standfield", ClientId = 1};
 4                 var invoice1 = new Invoice {InvoiceDate = DateTime.Parse("4/1/10"), Amount = 29.95M};
 5                 var invoice2 = new Invoice {InvoiceDate = DateTime.Parse("4/2/10"), Amount = 49.95M};
 6                 var invoice3 = new Invoice {InvoiceDate = DateTime.Parse("4/3/10"), Amount = 102.95M};
 7                 var invoice4 = new Invoice {InvoiceDate = DateTime.Parse("4/4/10"), Amount = 45.99M};
 8
 9                 // 添加一个invoice到client的导航属性集合Invoices
10                 client1.Invoices.Add(invoice1);
11
12                 //直接分配一个外键值
13                 invoice2.ClientId = 1;
14
15                 //使用一个“假”实体附加一存在的行
16                 context.Database.ExecuteSqlCommand("insert into chapter5.client values (2, ‘Phil Marlowe‘)");
17                 var client2 = new Client {ClientId = 2};
18                 context.Clients.Attach(client2);
19                 invoice3.Client = client2;
20
21                 // 使用Client引用
22                 invoice4.Client = client1;
23
24                 //保存更改
25                 context.Clients.Add(client1);
26                 context.Invoices.Add(invoice2);
27                 context.Invoices.Add(invoice3);
28                 context.Invoices.Add(invoice4);
29                 context.SaveChanges();
30             }
31
32             using (var context = new EFRecipesEntities())
33             {
34                 foreach (var client in context.Clients)
35                 {
36                     Console.WriteLine("Client: {0}", client.Name);
37                     foreach (var invoice in client.Invoices)
38                     {
39                         Console.WriteLine("\t{0} for {1}", invoice.InvoiceDate.ToShortDateString(),
40                                           invoice.Amount.ToString("C"));
41                     }
42                 }
43             }
44
45             Console.WriteLine("Press <enter> to continue...");
46             Console.ReadLine();

  代码清单5-37的输出如下:

Client: Karen Standfield
        4/1/2010 for $29.95
        4/4/2010 for $45.99
        4/2/2010 for $49.95
Client: Phil Marlowe
        4/3/2010 for $102.95

原理

  实体框架支持独立关联和外键关联。对于独立关联,由关联间的实体自行跟踪,修改关联的唯一方式是通过修改对象引用

  对于外键关联,你可以通过修改对象引用,或是直接修改外键属性值来修改关联。外键关联不能用作多对多关系

注意  记住,外键关联简单易用,它是默认方法,也是实体框架开发团队推荐的方法。除非你有具体的业务要求使用独立关联。否则请使用外键关联。

  表5-1展示了独立关联和外键关联之间的主要区别。

表5-1. 独立关联和外键关联之间的区别

 至此第5章结束,下篇我们将进入第六章,感谢你的阅读。觉得有帮助的话,点击下边的推荐以示支持。谢谢!

实体框架交流QQ群:  458326058,欢迎有兴趣的朋友加入一起交流

谢谢大家的持续关注,我的博客地址:http://www.cnblogs.com/VolcanoCloud/

时间: 2024-10-26 18:47:00

《Entity Framework 6 Recipes》中文翻译系列 (29) ------ 第五章 加载实体和导航属性之过滤预先加载的实体集合和修改外键关联的相关文章

《Entity Framework 6 Recipes》中文翻译系列 (28) ------ 第五章 加载实体和导航属性之测试实体是否加载与显式加载关联实体

翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 5-11  测试实体引用或实体集合是否加载 问题 你想测试关联实体或实体集合是否已经加载到上下文中,另外你想使用Code-First来管理数据访问. 解决方案 假设你有如图5-26所示的概念模型 图5-26 一个包含projects,managers和contractors的模型 在Visual Studio中添加一个名为Recipe11的控制台应用,并确保引用了实体框架6的库,NuGet可

《Entity Framework 6 Recipes》中文翻译系列 (23) -----第五章 加载实体和导航属性之预先加载与Find()方法

翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 5-2  预先加载关联实体 问题 你想在一次数据交互中加载一个实体和与它相关联实体. 解决方案 假设你有如图5-2所示的模型. 图5-2 包含Customer和与它相关联信息的实体 和5-1节一样,在模型中,有一个Customer实体,一个与它关联的CustomerType和多个与它关联的CustomerEamil.它与CustomerType的关系是一对多关系,这是一个实体引用(译注:Cu

《Entity Framework 6 Recipes》中文翻译系列 (26) ------ 第五章 加载实体和导航属性之延缓加载关联实体和在别的LINQ查询操作中使用Include()方法

翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 5-7  在别的LINQ查询操作中使用Include()方法 问题 你有一个LINQ查询,使用了类似这样的操作 group by,join,和where:你想使用Include()方法预先加载额外的实体.另外你想使用Code-First来管理数据访问. 解决方案 假设你有如图5-22所示的概念模型 图5-22 一个简单的包含Club和Event以及它们之间一对多关联的模型 在Visual S

《Entity Framework 6 Recipes》中文翻译系列 (15) -----第三章 查询之与列表值比较和过滤关联实体

翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 3-8与列表值比较 问题 你想查询一个实体,条件是给定的列表中包含指定属性的值. 解决方案 假设你有如图3-9所示的模型. 图3-9 包含books和它的categoryes的模型 你想查找给定目录列表中的所有图书.在代码清单3-16中使用LINQ和Entity SQL来实现这上功能. 代理清单3-16. 使用LINQ和Entity SQL来查找给定目录列表中的所有图书 1 using (v

《Entity Framework 6 Recipes》中文翻译系列 (25) ------ 第五章 加载实体和导航属性之加载完整的对象图和派生类型上的导航属性

翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 5-5  加载完整的对象图 问题 你有一个包含许多关联实体的模型,你想在一次查询中,加载完整的对象图实例.一般地,当一个页面视图需要呈现关联实体集时,你会选择这种方法,而不是延迟加载,因为延迟加载是通过一系列的短小查询来获取关联实体的. 解决方案 假设你有如图5-20所示的概念模型.每门课程有很多节,每一节由一个老师教多名学习. 图5-20 一个包含许多关联实体的模型 使用Include()

《Entity Framework 6 Recipes》中文翻译系列 (22) -----第五章 加载实体和导航属性之延迟加载

翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 第五章 加载实体和导航属性 实体框架提供了非常棒的建模环境,它允许开发人员可视化地使用映射到数据库中的表.视图.存储过程以及关系中的实体类型.本节将向你展示如何控制查询操作中的关联实体的加载. 实体框架的默认行为是只加载应用程序直接需要的实体.通常情况下,这正是你需要的.如果实体框架通过一个或多个关联积极地加载关联实体,最终,你很有可能得到超过你需求的实体.这不但增加了内存占用,而且还影响了

《Entity Framework 6 Recipes》中文翻译系列 (27) ------ 第五章 加载实体和导航属性之关联实体过滤、排序、执行聚合操作

翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 5-9  关联实体过滤和排序 问题 你有一实体的实例,你想加载应用了过滤和排序的相关实体. 解决方案 假设你有如图5-24所示的概念模型 图5-24 一个酒店预定系统的模型 假设我们有一个酒店(Hotel)实体,使用代码清单5-22,获取酒店的商务套房(executive suite),查看是否被预定,并按房价排序. 代码清单5-22.通过方法Entry()和Query()显式加载实体集合,

《Entity Framework 6 Recipes》翻译系列 (1) -----第一章 开始使用实体框架之历史和框架简述 (转)

微软的Entity Framework 受到越来越多人的关注和使用,Entity Framework7.0版本也即将发行.虽然已经开源,可遗憾的是,国内没有关于它的书籍,更不用说好书了,可能是因为EF版本更新太快,没人愿意去花时间翻译国外关于EF的书籍.使用Entity Framework开发已经有3年多了,但用得很肤浅,最近想深入学习,只好找来英文书<Entity Framework 6 Recipes>第二版,慢慢啃.首先需要说明的是,我英文不好,只是为了学习EF.把学习的过程写成博客,一

《Entity Framework 6 Recipes》翻译系列 (4) -----第二章 实体数据建模基础之从已存在的数据库创建模型 (转)

不知道对EF感兴趣的并不多,还是我翻译有问题(如果是,恳请你指正),通过前几篇的反馈,阅读这个系列的人不多.不要这事到最后成了吃不讨好的事就麻烦了,废话就到这里,直奔主题. 2-2 从已存在的数据库创建模型 问题 有一个存在的数据库,它拥有表.也许还有视图.外键.你想通过它来创建一个模型. 解决方案 让我们设想,你拥有一个描述诗人(Poet)以及他们的诗(Poem),还有他们之间关系的数据库.如图2-7所示. 图2-7 一个关于诗人及他们的诗的简单数据库 从上图可以看出,一个诗人可能是一首或多首