zoukankan      html  css  js  c++  java
  • 《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 (var context = new EFRecipesEntities())
     2             {
     3                 // 删除之前的测试数据
     4                 context.Database.ExecuteSqlCommand("delete from chapter3.book");
     5                 context.Database.ExecuteSqlCommand("delete from chapter3.category");
     6                 // 添加新的测试数据            
     7                 var cat1 = new Category {Name = "Programming"};
     8                 var cat2 = new Category {Name = "Databases"};
     9                 var cat3 = new Category {Name = "Operating Systems"};
    10                 context.Books.Add(new Book {Title = "F# In Practice", Category = cat1});
    11                 context.Books.Add(new Book {Title = "The Joy of SQL", Category = cat2});
    12                 context.Books.Add(new Book
    13                 {
    14                     Title = "Windows 7: The Untold Story",
    15                     Category = cat3
    16                 });
    17                 context.SaveChanges();
    18             }
    19 
    20             using (var context = new EFRecipesEntities())
    21             {
    22                 Console.WriteLine("Books (using LINQ)");
    23                 var cats = new List<string> {"Programming", "Databases"};
    24                 var books = from b in context.Books
    25                     where cats.Contains(b.Category.Name)
    26                     select b;
    27                 foreach (var book in books)
    28                 {
    29                     Console.WriteLine("'{0}' is in category: {1}", book.Title,
    30                         book.Category.Name);
    31                 }
    32             }
    33 
    34             using (var context = new EFRecipesEntities())
    35             {
    36                 Console.WriteLine("\nBooks (using eSQL)");
    37                 var esql = @"select value b from Books as b
    38                  where b.Category.Name in {'Programming','Databases'}";
    39                 var books = ((IObjectContextAdapter) context).ObjectContext.CreateQuery<Book>(esql);
    40                 foreach (var book in books)
    41                 {
    42                     Console.WriteLine("'{0}' is in category: {1}", book.Title,
    43                         book.Category.Name);
    44                 }
    45             }
    46 
    47             Console.WriteLine("\nPress <enter> to continue...");
    48             Console.ReadLine();
    49         }

    代码清单3-16的输出如下:

    Books (using LINQ)
    'F# In Practice' is in category: Programming
    'The Joy of SQL' is in category: Databases
    Books (using ESQL)
    'F# In Practice' is in category: Programming
    'The Joy of SQL' is in category: Databases

    原理

      在LINQ查询中,我构造了一个简单的目录名列表,它使用LINQ查询操作符Contains。细心的读者可以注意了,我使用cats集合,并判断它是否包含某一目录名。实体框架将Containts从句转换成SQL语句中的in从句。如代码清单3-17所示:

    代码清单3-17.  代码清单3-16中LINQ查询表达式对应的SQL语句

    1 SELECT
    2 [Extent1].[BookId] AS [BookId],
    3 [Extent1].[Title] AS [Title],
    4 [Extent1].[CategoryId] AS [CategoryId]
    5 FROM [chapter3].[Books] AS [Extent1]
    6 LEFT OUTER JOIN [chapter3].[Category] AS [Extent2] ON [Extent1].[CategoryId] = [Extent2].[CategoryId]
    7 WHERE [Extent2].[Name] IN (N'Programming',N'Databases')

      有趣的是,代码清单3-17中的SQL语句,没有为从句的项使用参数。 这不同于LINQ to SQL中产生的代码,列表中的项会被参数化。超过SQL Server的参数限制的风险,会使代码不能运行。

      如果我们需要查找出给定目录列表中的所有书,列表目录中包含未分类的目录,我们只需在目录列表中简单地使用null值。产生的SQL语句,如代码清单3-18所示。

    代码清单3-18.代码清单3-16中LINQ查询表达式对应的SQL语句,但是目录列表中多了一上null值。

    1 SELECT
    2 [Extent1].[BookId] AS [BookId],
    3 [Extent1].[Title] AS [Title],
    4 [Extent1].[CategoryId] AS [CategoryId]
    5 FROM [chapter3].[Books] AS [Extent1]
    6 LEFT OUTER JOIN [chapter3].[Category] AS [Extent2] ON [Extent1].[CategoryId] = [Extent2].[CategoryId]
    7 WHERE [Extent2].[Name] IN (N'Programming',N'Databases')
    8 OR [Extent2].[Name] IS NULL

      相同地,我们包含了一个使用Entity SQL的查询版本,它显式地包含了一个SQL IN 从句。

    3-9过滤关联实体

    问题

      你想获取一部分,不是全部关联实体。

    解决方案

      假设你有如图3-10所示的模型。

    图3-10 包含Worker和他们的Accidents的模型

      在模型中,一个Worker有零个或是多个accidents.每个事故按严重性分类,我们要获取所有的工人,对于与之关联的事故,只对严重事故感兴趣,事故的严重性大于2.

      在示例中我们使用了Code-First,在代码清单3-19中,创建了实体类Worker和Accidentd.

    代码清单3-19. 实体类

     1  public class Worker
     2     {
     3         public Worker()
     4         {
     5             Accidents = new HashSet<Accident>();
     6         }
     7 
     8         public int WorkerId { get; set; }
     9         public string Name { get; set; }
    10 
    11         public virtual ICollection<Accident> Accidents { get; set; }
    12     }
    13 
    14 
    15  public class Accident
    16     {
    17         public int AccidentId { get; set; }
    18         public string Description { get; set; }
    19         public int? Severity { get; set; }
    20         public int WorkerId { get; set; }
    21 
    22         public virtual Worker Worker { get; set; }
    23     }

      接下来,我们在代码清单3-20中创建Code-First中使用的上下文对象。

    代码清单3-20. 上下文对象

     1 public class EFRecipesEntities : DbContext
     2     {
     3         public EFRecipesEntities()
     4             : base("ConnectionString") {}
     5 
     6         public DbSet<Accident> Accidents { get; set; }
     7         public DbSet<Worker> Workers { get; set; }
     8 
     9         protected override void OnModelCreating(DbModelBuilder modelBuilder)
    10         {
    11             modelBuilder.Entity<Accident>().ToTable("Chapter3.Accident");
    12             modelBuilder.Entity<Worker>().ToTable("Chapter3.Worker");
    13             base.OnModelCreating(modelBuilder);
    14         }
    15     }

      使用代码清单3-21中的模式,获取所有的worders,和严重事故。

    代码清单3-21 使用CreateSourceQuery和匿名类型获取严重事故

     1 using (var context = new EFRecipesEntities())
     2             {
     3                 // 删除之前的测试数据
     4                 context.Database.ExecuteSqlCommand("delete from chapter3.accident");
     5                 context.Database.ExecuteSqlCommand("delete from chapter3.worker");
     6                 // 添加新的测试数据
     7                 var worker1 = new Worker { Name = "John Kearney" };
     8                 var worker2 = new Worker { Name = "Nancy Roberts" };
     9                 var worker3 = new Worker { Name = "Karla Gibbons" };
    10                 context.Accidents.Add(new Accident
    11                 {
    12                     Description = "Cuts and contusions",
    13                     Severity = 3,
    14                     Worker = worker1
    15                 });
    16                 context.Accidents.Add(new Accident
    17                 {
    18                     Description = "Broken foot",
    19                     Severity = 4,
    20                     Worker = worker1
    21                 });
    22                 context.Accidents.Add(new Accident
    23                 {
    24                     Description = "Fall, no injuries",
    25                     Severity = 1,
    26                     Worker = worker2
    27                 });
    28                 context.Accidents.Add(new Accident
    29                 {
    30                     Description = "Minor burn",
    31                     Severity = 3,
    32                     Worker = worker2
    33                 });
    34                 context.Accidents.Add(new Accident
    35                 {
    36                     Description = "Back strain",
    37                     Severity = 2,
    38                     Worker = worker3
    39                 });
    40                 context.SaveChanges();
    41             }
    42 
    43             using (var context = new EFRecipesEntities())
    44             {
    45                 // 显式禁用延迟加载
    46                 context.Configuration.LazyLoadingEnabled = false;
    47                 var query = from w in context.Workers
    48                             select new
    49                             {
    50                                 Worker = w,
    51                                 Accidents = w.Accidents.Where(a => a.Severity > 2)
    52                             };
    53                 query.ToList();
    54                 var workers = query.Select(r => r.Worker);
    55                 Console.WriteLine("Workers with serious accidents...");
    56                 foreach (var worker in workers)
    57                 {
    58                     Console.WriteLine("{0} had the following accidents", worker.Name);
    59                     if (worker.Accidents.Count == 0)
    60                         Console.WriteLine("\t--None--");
    61                     foreach (var accident in worker.Accidents)
    62                     {
    63                         Console.WriteLine("\t{0}, severity: {1}",
    64                               accident.Description, accident.Severity.ToString());
    65                     }
    66                 }
    67             }
    68 
    69             Console.WriteLine("\nPress <enter> to continue...");
    70             Console.ReadLine();
    71         }

    下面是代码清单3-21的输出:

    Workers with serious accidents...
    John Kearney had the following accidents
    Cuts and contusions, severity: 3
    Broken foot, severity: 4
    Nancy Roberts had the following accidents
    Minor burn, severity: 3
    Karla Gibbons had the following accidents
    --None--

    原理

      正如你在随后的第五章中看到的那样,我们需要立即加载一个关联集合,我们经常使用Include()方法和一个查询路径。(Include()方法在单个查询中返回父对象和所有的子对象)。然后,Include()方法不允许过滤关联的子实体对象,在本节中,我们展示了,通过轻微的改变,让你可以加载并过滤相关联的子实体对象。

      在代码块中,我们创建了一些workers,并分配给它们相关的不同等级的accidents。不得不承认,分配事故给人,有点毛骨悚然!但,这只是为了得到一些可以让我们继续工作的数据。

      在随后的查询中,我们获取所有的工人并将结果投射到匿名对象上,这个类型包含了一个worker和一个accidents集合。对于accidents,应该注意,我们是如何过滤集合,只获取严重事故的。

      接下来的一行,非常重要!(译注:也就是query.ToList();),我们通过调用ToList()方法,强制查询求值。(记住,LINQ查询默认为延迟加载。意思是,直到结果被使用时,查询才被真正地执行。ToList()方法能强制查询执行)。在Dbcontext(译注:其实是它的子类EFRecipesEntities)中枚举所有的工人和所有的相关的严重事故。 匿名类型不会把accidents附加到workers上,但是通过把它们带到上下文中,实体框架会填充导航属性,将每一个严重事故集合accidents附加到合适的worker上。这个过程一般叫做:Entity Span。这是一个强大而微妙的,发生在实体框架实例化实体类型及它们之间关系的幕后的副作用。

      我们关闭了延迟加载,以便让accidents在我们的过滤查询中加载(我们将在第5章讨论延迟加载)。如果打开延迟加载,所有的accidents将在我们引用worker的accidents时才加载。这将导致过虑失败。

      我们一旦有了结果集合,就可以枚举并打印出每个worker和它的accidents信息。如果一个worker没有任何严重的accidents,我们打印none来指示他们的安全记录。

      

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

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

    版权所有,转载请注明出处 付灿的技术博客
  • 相关阅读:
    Win7+Ubuntu11.10(EasyBCD硬盘安装)
    hdu 3661 Assignments
    hdu 1128 Self Numbers
    CF 152 B. Chilly Willy
    hdu 1754 I Hate It
    A survey of wireless network simulation and/or emulation software for use in higher education
    How to Fix Packet Loss: Causes, Simple Solutions & Top Tools
    getchar函数
    C++“左值”和“右值”
    stdio.h中的stdio
  • 原文地址:https://www.cnblogs.com/VolcanoCloud/p/4511789.html
Copyright © 2011-2022 走看看