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


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

    5-5  加载完整的对象图

    问题

      你有一个包含许多关联实体的模型,你想在一次查询中,加载完整的对象图实例。一般地,当一个页面视图需要呈现关联实体集时,你会选择这种方法,而不是延迟加载,因为延迟加载是通过一系列的短小查询来获取关联实体的。

    解决方案

      假设你有如图5-20所示的概念模型。每门课程有很多节,每一节由一个老师教多名学生。

    图5-20 一个包含许多关联实体的模型

       使用Include()方法和查询路径参数,通过一个查询,从数据库中获取所有的课程,课时,老师和学生,如代码清单5-12所示。

     1  using (var context = new EFRecipesEntities())
     2             {
     3                 var course = new Course {Title = "Biology 101"};
     4                 var fred = new Instructor {Name = "Fred Jones"};
     5                 var julia = new Instructor {Name = "Julia Canfield"};
     6                 var section1 = new Section {Course = course, Instructor = fred};
     7                 var section2 = new Section {Course = course, Instructor = julia};
     8 
     9                 var jim = new Student {Name = "Jim Roberts"};
    10                 jim.Sections.Add(section1);
    11 
    12                 var jerry = new Student {Name = "Jerry Jones"};
    13                 jerry.Sections.Add(section2);
    14 
    15                 var susan = new Student {Name = "Susan O'Reilly"};
    16                 susan.Sections.Add(section1);
    17 
    18                 var cathy = new Student {Name = "Cathy Ryan"};
    19                 cathy.Sections.Add(section2);
    20 
    21                 course.Sections.Add(section1);
    22                 course.Sections.Add(section2);
    23 
    24                 context.Students.Add(jim);
    25                 context.Students.Add(jerry);
    26                 context.Students.Add(susan);
    27                 context.Students.Add(cathy);
    28 
    29                 context.Courses.Add(course);
    30                 context.SaveChanges();
    31             }
    32 
    33             //字符串查询路径参数
    34             using (var context = new EFRecipesEntities())
    35             {
    36                 var graph = context.Courses
    37                                    .Include("Sections.Instructor")
    38                                    .Include("Sections.Students");
    39                 Console.WriteLine("Courses");
    40                 Console.WriteLine("=======");
    41 
    42                 foreach (var course in graph)
    43                 {
    44                     Console.WriteLine("{0}", course.Title);
    45                     foreach (var section in course.Sections)
    46                     {
    47                         Console.WriteLine("	Section: {0}, Instrutor: {1}", section.SectionId, section.Instructor.Name);
    48                         Console.WriteLine("	Students:");
    49                         foreach (var student in section.Students)
    50                         {
    51                             Console.WriteLine("		{0}", student.Name);
    52                         }
    53                         Console.WriteLine("
    ");
    54                     }
    55                 }
    56             }
    57 
    58             // 强类型查询路径参数
    59             using (var context = new EFRecipesEntities())
    60             {
    61                 var graph = context.Courses
    62                                    .Include(x => x.Sections.Select(y => y.Instructor))
    63                                    .Include(x => x.Sections.Select(z => z.Students));
    64 
    65                 Console.WriteLine("Courses");
    66                 Console.WriteLine("=======");
    67 
    68                 foreach (var course in graph)
    69                 {
    70                     Console.WriteLine("{0}", course.Title);
    71                     foreach (var section in course.Sections)
    72                     {
    73                         Console.WriteLine("	Section: {0}, Instrutor: {1}", section.SectionId, section.Instructor.Name);
    74                         Console.WriteLine("	Students:");
    75                         foreach (var student in section.Students)
    76                         {
    77                             Console.WriteLine("		{0}", student.Name);
    78                         }
    79                         Console.WriteLine("
    ");
    80                     }
    81                 }
    82             }
    83 
    84             Console.WriteLine("Press <enter> to continue...");
    85             Console.ReadLine();

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

    Courses
    Courses
    =======
        Biology 101
            Section: 19, Instructor: Fred Jones
            Students:
                Jim Roberts
                Susan O'Reilly
            Section: 20, Instructor: Julia Canfield
            Students:
                Jerry Jones
                Cathy Ryan

    原理

      分别给Include()方法传递字符串和强类型形式的查询路径参数。查询路径代表我们想使用Include()方法加载的整个对象图路径。Include()方法,扩展查询包含查询路径中指定的实体对象。

      在代码清单5-12中,我们最先演示基于字符串形式参数的Include()方法。Include()方法第一次调用,通过表示对象图部分对象的查询路径将Secion扩展至Instructor。这使得查询包含了所有的课时(Sections)和它们的教师(Instructors)。然后在第一个Include()方法后链接另一个Include()方法,它包含一个将Secton扩展到Student的查询路径,这将使得查询包含所有课时(Sections)和它们的学生(Students)。最终结果就是,实例化了一个完整的对象图,它包含所有的Course实体和在模型中每个与它关联的实体。

      在代码的第二部分,我们演示了使用强类型查询路径参数的Include()方法,注意这两个Include()方法是如何结合一个参数,x.Sections,并使用Select()方法整合关联实体Instructor和Students的。

    注意  接受强类型参数的重载方法Include(),是在命名空间System.Data.Entity中公布的扩展方法。要使用这个方法,你需要在你的类中使用using引用这个命名空间。

      你可以使用导航属性构造任何深度的查询路径,这给你加载部分或是完整的对象图带来很大的灵活性。实体框架会通过修剪掉重叠和重复的查询路径来优化查询

      Include()方法的语法和主义都看似简单。但不要被这种简单迷惑了,以为使用Include()方法是不需要付出性能代价的。调用多个Includ()方法预先加载数据,会迅速地增加生成的SQL语句的复杂性,同时,从数据库中返回的数据量也急剧上升。复杂的查询语句会导致产生低性能的查询计划,大数据量的返回,也会让实体框架花费大量的时间来移除重复的数据。所以你应该明智地使用Include()方法,确保它不会给你的应用带来潜在的性能问题。

    5-6  加载派生类型上的导航属性

    问题

      你有这样一个模型,有一个或多个派生类,它们在一个或多个实体的Has-a关系中(一个对象是另一个对象的一部分)。你想在一次数据库交互中预先加载所有关联的实体。

    解决方案

      假设你有如图5-21所示的模型。

     

    图5-21 一个包含 Plumbers(管道工人)、他们的JobSite(工作场所)和其它关联实体的模型

       在模型中,管道工人(Plumber)实体继承至手艺人(Tradesman)实体,一个管道工人(Plumber)有一个工作场所(JobSite),这是通过一个一对多关联表示的。工作场所(JobSite)类型继承至地点(Location)实体。Location有一个Phone,这是通过一个一对多关联表示的。最后,一个JobSite可能拥有0或者多个工头(Formen),也是通过一个一对多关联表示的。

      假设你想获取一个管道工人,他的工作场所,工作场所的电话,以及工作场所全部的工头。你想在一次数据库交互中返回这些数据。

      代码清单5-13演示,在一个查询中通过使用Includ()方法预先加载关联实体。

    代码清单5-13.使用Include()方法在一次数据库交互中预先加载关联实体  

     1 using (var context = new EFRecipesEntities())
     2             {
     3                 var foreman1 = new Foreman {Name = "Carl Ramsey"};
     4                 var foreman2 = new Foreman {Name = "Nancy Ortega"};
     5                 var phone = new Phone {Number = "817 867-5309"};
     6                 var jobsite = new JobSite
     7                     {
     8                         JobSiteName = "City Arena",
     9                         Address = "123 Main",
    10                         City = "Anytown",
    11                         State = "TX",
    12                         ZIPCode = "76082",
    13                         Phone = phone
    14                     };
    15                 jobsite.Foremen.Add(foreman1);
    16                 jobsite.Foremen.Add(foreman2);
    17                 var plumber = new Plumber {Name = "Jill Nichols", Email = "JNichols@plumbers.com", JobSite = jobsite};
    18                 context.Tradesmen.Add(plumber);
    19                 context.SaveChanges();
    20             }
    21 
    22             using (var context = new EFRecipesEntities())
    23             {
    24                 var plumber =
    25                     context.Tradesmen.OfType<Plumber>().Include("JobSite.Phone").Include("JobSite.Foremen").First();
    26                 Console.WriteLine("Plumber's Name: {0} ({1})", plumber.Name, plumber.Email);
    27                 Console.WriteLine("Job Site: {0}", plumber.JobSite.JobSiteName);
    28                 Console.WriteLine("Job Site Phone: {0}", plumber.JobSite.Phone.Number);
    29                 Console.WriteLine("Job Site Foremen:");
    30                 foreach (var boss in plumber.JobSite.Foremen)
    31                 {
    32                     Console.WriteLine("	{0}", boss.Name);
    33                 }
    34             }
    35 
    36             Console.WriteLine("Press <enter> to continue...");
    37             Console.ReadLine();

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

    Plumber's Name: Jill 
    Job Site: City Arena
    Job Site Phone: 817 8
    Job Site Foremen:
        Carl Ramsey
        Nancy Ortega

    原理

      我们的查询以获取派生类型Plumber开始,为了获取它们,我们使用了OfType<Plumber>()方法,OfType<>()方法,从实体集中获取给定子类型的实例。

      我们想从Plumber中加载与之关联的实体JobSite和与JobSite关联的实体Phone。注意JobSite实体并没有导航属性Phone。但是JobSite派生至Location,Location有导航属性Phone。因为Phone是基类属性,所以派生类同样能使用,这就是使用继承的好处。它使查询路径可以简单地表示为:JobSite.Phone。

      然后,我再次使用查询路径和Include()方法,从JobSite实体中获取Foreman实体.JobSite和Foreman有一个一对多关联。注意导航属性的复数形式(从Foreman变成Foremen)。

      最后,我们使用First()方法选择第一个Plumber实体,这样将返回一个Plumber类型,而不是一个Plumber对象集合。

      生成的查询有点复杂,涉及了几个Join连接和子查询。与此对应的,使用延迟加载,将会需要多次数据库交互,这样会带来性能损失。特别是加载多个Plumbers时。

     

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

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

  • 相关阅读:
    DBA_VMware虚拟机安装和简介(案例)
    DBA_Oracle基本体系内存和进程结构(概念)
    IREP_SOA Integration SOAP概述(概念)
    IREP_SOA Integration WSDL概述(概念)
    IREP_SOA Integration程序注释语法Annotations(概念)
    DBA_Oracle日志文件
    BPEL_Oracle BPEL新一代工作流介绍(概念)
    DBA_Oracle基本体系架构(概念)
    DBA_Tablespace表空间的概念和管控(概念)
    WebADI_Oracle ERP R12使用前WebADI设定(案例)
  • 原文地址:https://www.cnblogs.com/VolcanoCloud/p/4521791.html
Copyright © 2020-2023  润新知