zoukankan      html  css  js  c++  java
  • 《Entity Framework 6 Recipes》中文翻译系列 (16) 第三章 查询之左连接和在TPH中通过派生类排序

    翻译的初衷以及为什么选择《Entity Framework 6 Recipes》来学习,请看本系列开篇

    3-10应用左连接

    问题

      你想使用左外连接来合并两个实体的属性。

    解决方案

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

     

    图3-11 一个包含Product和TopSelling的模型

       畅销产品有一个与之关联的TopSelling实体。当然,不是所有的产品都是畅销产品。这就是为什么关系为零或者1。当一个产品为畅销产品时,与之关联的topSelling实体包含一个用户评级。你想查找和呈现所有的产品,和与之关联的热销榜单实体,即使产品不是一个畅销产品。如果产品没有相关联的热销榜单,我们就简单地用0来设置用户评级。  在数据库中,这叫做左外连接。

      代码清单3-22演示了三种略有不同的方法来解决这个问题。

    代码清单3-22. 两个实体间的左外连接

     1  using (var context = new EFRecipesEntities())
     2             {
     3                 // 删除之前的测试数据
     4                 context.Database.ExecuteSqlCommand("delete from chapter3.topselling");
     5                 context.Database.ExecuteSqlCommand("delete from chapter3.product");
     6                 // 添加新的测试数据
     7                 // 注意p1没有与之关联的TopSelling实体
     8                 var p1 = new Product { Name = "Trailrunner Backpack" };
     9                 var p2 = new Product
    10                 {
    11                     Name = "Green River Tent",
    12                     TopSelling = new TopSelling { Rating = 3 }
    13                 };
    14                 var p3 = new Product
    15                 {
    16                     Name = "Prairie Home Dutch Oven",
    17                     TopSelling = new TopSelling { Rating = 4 }
    18                 };
    19                 var p4 = new Product
    20                 {
    21                     Name = "QuickFire Fire Starter",
    22                     TopSelling = new TopSelling { Rating = 2 }
    23                 };
    24                 context.Products.Add(p1);
    25                 context.Products.Add(p2);
    26                 context.Products.Add(p3);
    27                 context.Products.Add(p4);
    28                 context.SaveChanges();
    29             }
    30 
    31             using (var context = new EFRecipesEntities())
    32             {
    33                 var products = from p in context.Products
    34                                orderby p.TopSelling.Rating descending
    35                                select p;
    36                 Console.WriteLine("All products, including those without ratings");
    37 
    38                 foreach (var product in products)
    39                 {
    40                     Console.WriteLine("\t{0} [rating: {1}]", product.Name,
    41                         product.TopSelling == null ? "0"
    42                             : product.TopSelling.Rating.ToString());
    43                 }
    44             }
    45 
    46             using (var context = new EFRecipesEntities())
    47             {
    48                 var products = from p in context.Products
    49                                join t in context.TopSellings on
    50                                    //注意,我们如何将结果集投影到另一个名为'g'的序列中,以及应用DefaultIfEmpty方法
    51                                   p.ProductID equals t.ProductID into g
    52                                from tps in g.DefaultIfEmpty()
    53                                orderby tps.Rating descending
    54                                select new
    55                                {
    56                                    Name = p.Name,
    57                                    Rating = tps.Rating == null ? 0 : tps.Rating
    58                                };
    59 
    60                 Console.WriteLine("\nAll products, including those without ratings");
    61                 foreach (var product in products)
    62                 {
    63                     Console.WriteLine("\t{0} [rating: {1}]", product.Name,
    64                         product.Rating.ToString());
    65                 }
    66             }
    67 
    68             using (var context = new EFRecipesEntities())
    69             {
    70                 var esql = @"select value p from products as p
    71                  order by case when p.TopSelling is null then 0
    72                                     else p.TopSelling.Rating end desc";
    73                 var products = ((IObjectContextAdapter)context).ObjectContext.CreateQuery<Product>(esql);
    74                 Console.WriteLine("\nAll products, including those without ratings");
    75                 foreach (var product in products)
    76                 {
    77                     Console.WriteLine("\t{0} [rating: {1}]", product.Name,
    78                         product.TopSelling == null ? "0"
    79                             : product.TopSelling.Rating.ToString());
    80                 }
    81             }
    82 
    83             Console.WriteLine("\nPress <enter> to continue...");
    84             Console.ReadLine();
    85         }

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

    Top selling products sorted by rating
    Prairie Home Dutch Oven [rating: 4]
    Green River Tent [rating: 3]
    QuickFire Fire Starter [rating: 2]
    Trailrunner Backpack [rating: 0]Top selling products sorted by rating
    Prairie Home Dutch Oven [rating: 4]
    Green River Tent [rating: 3]
    QuickFire Fire Starter [rating: 2]
    Trailrunner Backpack [rating: 0]Top selling products sorted by rating
    Prairie Home Dutch Oven [rating: 4]
    Green River Tent [rating: 3]
    QuickFire Fire Starter [rating: 2]
    Trailrunner Backpack [rating: 0]

    原理

      在代码清单3-22中,我们展示了三种不同的方法来处理这个问题。第一种方法最简单。因为实体框架会为关联的实体自动创建join连接,它是基于模型创建时被创建的导航属性。这两个实体的关联为1-0...1,这意味着,实体框架在自动生成SQL查询时,会在这两个实体间包含一个左外连接。当product实体被实例化时,任何与之关联的topSelling也会被实例化。导航属性topSelling也会被设置为相关联的实体,如是不存在topSelling就为null。如果给定的产品不存在与之关联的topSelling实体(也就是说,它没有被评为畅销产品)。我们会简单的分配一个0值给产品的评级。

      在某些情况下,你想连接的两个实体间可能没有一个关系(对实例而言,是一个导航属性)。这样的话,你可以显式连接实体。把结果集投影到一个匿名类型。我们之所以需要将结果集投影到匿名类型,是因为没有关联的两个实体间没有导航属性。否则我们不可以引用相关联的实体。

      在第二段查询的代码中演示了这种方法。通过键ProductId连接两个实体,然后把结果集投影到一个新的名为'g'的序列中。随后,我们在g上应用DefaultIfEmpty()方法,当g为空时,它会用null值填充。毫无疑问,当SQL语句被产生时,两个实体间将会包含一个左外连接。我们通过一个orderby从句,让结果集根据rating排序。最后,我们将结果投影到一个匿名类型。

      在第三中方法中,我们展示了在Entity SQL中,如何更明确地使用左外连接。在查询中嵌入一个Entity SQL语句。

    3-11通过派生类排序

    问题

      当你使用TPH继承映射时,你想通过派生类来给结果集排序。

    解决方案

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

    图3-12 使用TPH继承映射的模型,包含三个派生类弄型

       这个模型使用TPH继承映射,它是实体框架一个特性,TPH创建这样的一个继承结构,父类和子类都源至数据库中的同一张表。

      在这个示例中,Media实体有一个属性名为Mediatype,它被用作TPH结构中的鉴别属性。MediaType的值决定着数据库表中的行被映射到哪个派生类型(Article,Picture,或者Video)。鉴别列的值为1时代表Article类型,为2时代表Video类型,为3时代表Picture类型。因为这个属性被用来决定派生类型,它将不在Media实体中显示。

      我们在代码清单3-23中使用Code-First,创建了实体类,为了保持示例的简单性,我们只创建了空的派生子对象。因为我只是为了演示在查询中通过派生类型来排序。

    代码清单3-23. 父类和子类实体类型

     1 public class Media
     2 {
     3     public int MediaId { get; set; }
     4     public string Title { get; set; }
     5 }
     6 public class Article : Media
     7 {
     8 }
     9 public class Picture : Media
    10 {
    11 }
    12 public class Video : Media
    13 {
    14 }

      接下来,在代码清单3-24中,我们创建了数据库上下文对象,它是我们凭借实体框架中Code-First方法的入口。注意,如何在OnModelCreating方法中,使用FluentAPI(换句话说就是将扩展方法链接起来创建一个操作)。显式映射鉴别列,MediaType,到子类型。

    代码清单3-24 DbContext对象

     1  public class EFRecipesEntities : DbContext
     2     {
     3         public EFRecipesEntities()
     4             : base("ConnectionString") {}
     5 
     6         public DbSet<Media> Media { get; set; }
     7 
     8         protected override void OnModelCreating(DbModelBuilder modelBuilder)
     9         {
    10             modelBuilder.Entity<Media>().ToTable("Chapter3.Media");
    11 
    12 
    13             modelBuilder.Entity<Media>().Map<Article>(x => x.Requires("MediaType").HasValue(1));
    14             modelBuilder.Entity<Media>().Map<Picture>(x => x.Requires("MediaType").HasValue(2));
    15             modelBuilder.Entity<Media>().Map<Video>(x => x.Requires("MediaType").HasValue(3));
    16 
    17             base.OnModelCreating(modelBuilder);
    18         }
    19     }

      随着Code-First的构件被创建,我们将查询模型中所有的media,并通过派生类型:Article,Video,和Picture进行排序。如代码清单3-25所示。

    代码清单3-25.在TPH继承映射中通过类型排序

     1 using (var context = new EFRecipesEntities())
     2             {
     3                 //删除之前的测试数据
     4                 context.Database.ExecuteSqlCommand("delete from chapter3.Media");
     5                 // 添加新的测试数据
     6                 context.Media.Add(new Article
     7                 {
     8                     Title = "Woodworkers' Favorite Tools"
     9                 });
    10                 context.Media.Add(new Article
    11                 {
    12                     Title = "Building a Cigar Chair"
    13                 });
    14                 context.Media.Add(new Video
    15                 {
    16                     Title = "Upholstering the Cigar Chair"
    17                 });
    18                 context.Media.Add(new Video
    19                 {
    20                     Title = "Applying Finish to the Cigar Chair"
    21                 });
    22                 context.Media.Add(new Picture
    23                 {
    24                     Title = "Photos of My Cigar Chair"
    25                 });
    26                 context.Media.Add(new Video
    27                 {
    28                     Title = "Tour of My Woodworking Shop"
    29                 });
    30                 context.SaveChanges();
    31             }
    32 
    33             using (var context = new EFRecipesEntities())
    34             {
    35                 var allMedium = from m in context.Media
    36                     let mediumtype = m is Article
    37                         ? 1
    38                         : m is Video ? 2 : 3
    39                     orderby mediumtype
    40                     select m;
    41                 Console.WriteLine("All Medium sorted by type...\n");
    42                 foreach (var medium in allMedium)
    43                 {
    44                     Console.WriteLine("Title: {0} [{1}]", medium.Title, medium.GetType().Name);
    45                 }
    46             }
    47 
    48             Console.WriteLine("\nPress <enter> to continue...");
    49             Console.ReadLine();
    50         }

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

    1 All Media sorted by type...
    2 Title: Woodworkers' Favorite Tools [Article]
    3 Title: Building a Cigar Chair [Article]
    4 Title: Upholstering the Cigar Chair [Video]
    5 Title: Applying Finish to the Cigar Chair [Video]
    6 Title: Tour of My Woodworking Shop [Video]
    7 Title: Photos of My Cigar Chair [Picture]

    原理

      当我们使用TPH继承映射时,我们凭借表中一列来区别给定的行代表的派生类型。这一列,通常被称作鉴别列。它不能被映射到实体中的属性。因为我们没有属性包含鉴别值 ,所以,需要创建一个变量来保存鉴别值以便我们进行排序。为了实现这个目的,我们使用了LINQ中的let从句。它创建了一个mediatype变量。我们通过条件语句来将整型的鉴别值分配给这个变量。Article类型的分配1,Video类型的分配2,其它类型的分配3。这里的其它类型其实就是Picture类型,因为没有别的派生类型了。

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

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

    版权所有,转载请注明出处 付灿的技术博客
  • 相关阅读:
    【STM32F429开发板用户手册】第46章 STM32F429的DMA2D应用之刷色块,位图和Alpha混合
    【STM32F429开发板用户手册】第45章 STM32F429的图形加速器DMA2D的基础知识和HAL库API
    【STM32F429开发板用户手册】第44章 STM32F429的LTDC应用之LCD电阻触摸和电容触摸
    【STM32F429开发板用户手册】第43章 STM32F429的LTDC应用之汉字小字库和全字库制作
    【STM32F429开发板用户手册】第42章 STM32F429的LTDC应用之点阵字体和字符编码(重要)
    【STM32F429开发板用户手册】第41章 STM32F429的LTDC应用之LCD汉字显示和2D图形显示
    【STM32F429开发板用户手册】第40章 STM32F429的LCD控制器LTDC基础知识和HAL库API
    【STM32F429开发板用户手册】第39章 STM32F429的FMC总线应用之SDRAM
    【STM32F429开发板用户手册】第38章 STM32F429的FMC总线应用之是32路高速IO扩展
    MDK,IAR,GCC生成库文件的方法(2020-08-04)
  • 原文地址:https://www.cnblogs.com/VolcanoCloud/p/4512954.html
Copyright © 2011-2022 走看看