热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

LazyT在EntityFramework中的性能优化实践(附源码)

在使用EF的过程中,导航属性的lazyload机制,能够减少对数据库的不必要的访问。只有当你使用到导航属性的时候,才会访问数据库。但是这个

在使用EF的过程中,导航属性的lazy load机制,能够减少对数据库的不必要的访问。只有当你使用到导航属性的时候,才会访问数据库。但是这个只是对于单个实体而言,而不适用于显示列表数据的情况。

这篇文章介绍的是,使用Lazy来提高显示列表页面的效率。

这里是相关的源代码 PerformanceTest.zip

阅读目录:

一、问题的描述

二、数据表和EF实体介绍

三、lazy load的性能

四、使用StudentExtensionRepository来提高效率

五、进一步改进,使用StudentExtensionRepository1来实现按需访问数据库

六、总结

一,问题的描述

在使用EF的过程中,导航属性的lazy load机制,能够减少对数据库的不必要的访问。只有当你使用到导航属性的时候,才会访问数据库。

比如有个学生Student实体,只有当我访问Student的StudentScore(成绩实体)导航属性的时候,才会去访问StudentScore表。

问题是导航属性只是解决了单个实体访问导航属性时候的性能问题,而在实际开发过程中,常常遇到的问题是,我要显示一个列表的Student的信息,并且每个Student都要获取StudentScore信息,怎么办?

也许有人会说,可以使用EF的eager loading, 在读出Student信息的时候,把StudentScore一起读取出来就可以了。

是的,就像下面这样就能解决当前的问题,但是会面临不够通用,无法应对变化的情况。

var students = context.Students.Include(b => b.StudentScore).ToList();

 如果遇到需求,不需要StudentScore信息的时候,我们就又要改回lazy load的方式.
如果遇到需求,需要显示Student另外一个关联属性StudentHealthy信息的时候,又必须让StudentScore用Lazy load方式,而StudentHealthy采用eager loading
下面就介绍如何使用Lazy来解决这个兼顾代码设计和性能的问题.

二, 数据表和EF实体介绍

如下图所示,一共用到3张表:

Student: 学生信息表
StudentScores:学生成绩表
StudentHealthies: 学生健康状况表

3张表设计的是1对1关系,现实开发中当然会比这个复杂的多,但是这个对于描述我们的问题已经足够了。

table

 

EF中对应于着3张表的Model代码:

[Table("Student")]public class Student{[Key]public int Id { get; set; }public string Name { get; set; }public int Age { get; set; }public virtual StudentScore Score { get; set; }public virtual StudentHealthy Healthy { get; set; }}public class StudentScore{public int Score { get; set; }public int StudentId { get; set; }[ForeignKey("StudentId")]public virtual Student Student { get; set; }}public class StudentHealthy{public int Score{ get; set; }public int StudentId { get; set; }[ForeignKey("StudentId")]public virtual Student Student { get; set; }}

三, lazy load的性能

下图中的页面,读取Student的列表,同时访问Student的导航属性Score来获取成绩信息。

从MiniProfiler能看到,页面一共执行了4个Sql, 其中第一个Sql是从Student表中取出了3条数据,

其余的3个sql是每个Student访问导航属性而导致的数据库访问.

这里由于Student表中只有3条数据,如果有100条的话,就会导致1+100*3次数据访问,所以这种使用导航属性来获取数据的方式是不适合这种列表数据显示的

这里是页面View的代码:

&#64;model IEnumerable<Student><h1>Student With Score(Lazy Load)h1>
<table border&#61;"1"><tr><td>IDtd><td>Nametd><td>Agetd><td>Scoretd>tr>&#64;foreach(var student in Model){<tr><td>&#64;student.Idtd><td>&#64;student.Nametd><td>&#64;student.Agetd><td>&#64;student.Score.Scoretd>tr>}
table>

lazy load

四, 使用StudentExtensionRepository来提高效率

StudentExtensionRepository解决效率问题的思路是&#xff0c;如果你取出3个student信息的话&#xff0c;它会同时把StudentScore信息取出来。

先来看看StudentExtension的定义

public class StudentExtension{public Student Student { get; set; }public StudentScore StudentScore { get; set; }}

上面的StudentExtension用来保存Student信息&#xff0c;以及和该Student相关的StudentScore数据。

下面是StudentExtensionRepository.cs的具体内容&#xff0c;GetStudents方法中&#xff0c;先取得Student信息&#xff0c;然后获取相关的Score信息。

public class StudentExtensionRepository : IStudentExtensionRepository{private readonly IStudentRepository _studentRepository;private readonly IRepository _studentScoreRepository;public StudentExtensionRepository(IRepositoryCenter repositoryCenter){_studentRepository &#61; repositoryCenter.GetRepository();_studentScoreRepository &#61; repositoryCenter.GetRepository>();}public IEnumerable GetStudents(){var result &#61; new List();var students &#61; _studentRepository.GetStudents().ToList();//取得score信息var studentsIds &#61; students.Select(s &#61;> s.Id);var scores &#61; _studentScoreRepository.Filter(s &#61;> studentsIds.Contains(s.StudentId)).ToList();foreach (var student in students){var temp &#61; new StudentExtension{Student &#61; student,StudentScore &#61; scores.FirstOrDefault(s &#61;> s.StudentId &#61;&#61; student.Id)};result.Add(temp);}return result;}}

最后&#xff0c;使用新的StudentExtensionRepository&#xff0c;能够发现&#xff0c;显示同样的页面&#xff0c;只用了2个sql访问&#xff0c;减少来数据库的访问&#xff0c;提交了效率。

2

 

五, 进一步改进&#xff0c;使用StudentExtensionRepository1来实现按需访问数据库

上面的StudentExtensionRepository的实现&#xff0c;还是无法解决我们开始提出的问题&#xff1a;
如果遇到需求&#xff0c;不需要StudentScore信息的时候&#xff0c;我们就又要改回lazy load的方式.
如果遇到需求&#xff0c;需要显示Student另外一个关联属性StudentHealthy信息的时候&#xff0c;又必须让StudentScore用Lazy load方式&#xff0c;而StudentHealthy采用eager loading

如 果我们要显示Student的另外一个关联表StudentHealthy的数据&#xff0c;还是使用StudentExtensionRepository&#xff0c;会导 致对StudentScore表的访问&#xff0c;但是这是我们不需要的数据&#xff0c;如何改进StudentExtensionRepository的实现&#xff0c;来达到按需访 问数据库呢?
这个时候&#xff0c;就可以用到Lazy来实现Lazy属性&#xff0c;只有真正用到属性的时候&#xff0c;才会真正的访问数据库。
看看我们改造后的StudentExtension1类&#xff0c; 该类同时包含了Score和Healthy信息&#xff0c;但是都是Lazy加载的。

public class StudentExtension1{public Student Student { get; set; }//Lazy属性public Lazy StudentScoreLazy { get; set; }//非Lazy属性&#xff0c;从Lazy属性中取值。当真正用到该属性的时候&#xff0c;会触发数据库访问public StudentScore StudentScore { get { return StudentScoreLazy.Value; } }public Lazy StudentHealthyLazy { get; set; }public StudentHealthy StudentHealthy { get { return StudentHealthyLazy.Value; } }}

改造后的StudentExtensionRepository1

public IEnumerable GetStudents(){var result &#61; new List();var students &#61; _studentRepository.GetStudents().ToList();var studentsIds &#61; students.Select(s &#61;> s.Id);//存储Score查询的结果&#xff0c;避免多次访问数据库来获取Score信息List scoreListTemp &#61; null;Func<int, StudentScore> getScoreFunc &#61; id &#61;>{//第一个Student来获取Score信息的时候&#xff0c;scoreListTemp会是null, 这个时候&#xff0c;会去访问数据库获取Score信息//第二个以及以后的Student获取Score信息的时候&#xff0c;scoreListTemp已经有值了, 这样就不会再次访问数据库if (scoreListTemp &#61;&#61; null){scoreListTemp &#61;_studentScoreRepository.Filter(s &#61;> studentsIds.Contains(s.StudentId)).ToList();}return scoreListTemp.FirstOrDefault(s &#61;> s.StudentId &#61;&#61; id);};//存储Healthy查询的结果&#xff0c;避免多次访问数据库来获取Healthy信息List healthyListTemp &#61; null;Func<int, StudentHealthy> getHealthyFunc &#61; id &#61;>{if (healthyListTemp &#61;&#61; null){healthyListTemp &#61;_studentHealthyRepository.Filter(s &#61;> studentsIds.Contains(s.StudentId)).ToList();}returnhealthyListTemp.FirstOrDefault(s &#61;> s.StudentId &#61;&#61; id);};foreach (var student in students){var id &#61; student.Id;var temp &#61; new StudentExtension1{Student &#61; student,StudentScoreLazy &#61; new Lazy(() &#61;> getScoreFunc(id)),StudentHealthyLazy &#61; new Lazy(() &#61;> getHealthyFunc(id))};result.Add(temp);}return result;}}

接下来&#xff0c;创建2个不同的页面index2和index3, 他们的代码完全相同&#xff0c;只是View页面中访问的属性不同。

public ActionResult Index2(){var studentExtensionRepository1 &#61; _repositoryCenter.GetRepository();var students &#61; studentExtensionRepository1.GetStudents().ToList();return View(students);}public ActionResult Index3(){var studentExtensionRepository1 &#61; _repositoryCenter.GetRepository();var students &#61; studentExtensionRepository1.GetStudents().ToList();return View(students);}

index2.cshtml中&#xff0c;访问了StudentScore属性

<h1>Student With Score(Use the StudentExtensionRepository1)h1>
<table border&#61;"1"><tr><td>IDtd><td>Nametd><td>Agetd><td>Scoretd>tr>&#64;foreach(var student in Model){<tr><td>&#64;student.Student.Idtd><td>&#64;student.Student.Nametd><td>&#64;student.Student.Agetd><td>&#64;student.StudentScore.Scoretd>tr>}
table>

index3.cshtml&#xff0c;访问了StudentHealthy属性

<h1>Student With Healthy(Use the StudentExtensionRepository1)h1>
<table border&#61;"1"><tr><td>IDtd><td>Nametd><td>Agetd><td>Healthytd>tr>&#64;foreach(var student in Model){<tr><td>&#64;student.Student.Idtd><td>&#64;student.Student.Nametd><td>&#64;student.Student.Agetd><td>&#64;student.StudentHealthy.Scoretd>tr>}
table>

如下图&#xff0c;尽管Index2和Index3的代码中&#xff0c;获取数据的代码完全相同&#xff0c;但是实际的访问数据库的sql却是不同的。这是由于它们View中的显示数据的需求不同引起的。改造后的StudentExtensionRepository1能够根据所需来访问数据库&#xff0c; 来减少对于数据库的不必要的访问。同时它也能够适应更多的情况&#xff0c;无论是只显示Student表数据&#xff0c;还是要同时显示Student, StudentScore和StudentHealthy数据&#xff0c;StudentExtensionRepository1都能提供恰到好处的数据库访问。

 

3

 

六, 总结

以上是使用Lazy结合EF的一次性能优化的闭门造车的尝试&#xff0c;欢迎各位多提意见。如果有更好的方式来&#xff0c;欢迎指教



本文转自JustRun博客园博客&#xff0c;原文链接&#xff1a;http://www.cnblogs.com/JustRun1983/p/3391079.html&#xff0c;如需转载请自行联系原作者





推荐阅读
  • 前景:当UI一个查询条件为多项选择,或录入多个条件的时候,比如查询所有名称里面包含以下动态条件,需要模糊查询里面每一项时比如是这样一个数组条件:newstring[]{兴业银行, ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 本文介绍了一个在线急等问题解决方法,即如何统计数据库中某个字段下的所有数据,并将结果显示在文本框里。作者提到了自己是一个菜鸟,希望能够得到帮助。作者使用的是ACCESS数据库,并且给出了一个例子,希望得到的结果是560。作者还提到自己已经尝试了使用"select sum(字段2) from 表名"的语句,得到的结果是650,但不知道如何得到560。希望能够得到解决方案。 ... [详细]
  • 本文详细介绍了Spring的JdbcTemplate的使用方法,包括执行存储过程、存储函数的call()方法,执行任何SQL语句的execute()方法,单个更新和批量更新的update()和batchUpdate()方法,以及单查和列表查询的query()和queryForXXX()方法。提供了经过测试的API供使用。 ... [详细]
  • Android自定义控件绘图篇之Paint函数大汇总
    本文介绍了Android自定义控件绘图篇中的Paint函数大汇总,包括重置画笔、设置颜色、设置透明度、设置样式、设置宽度、设置抗锯齿等功能。通过学习这些函数,可以更好地掌握Paint的用法。 ... [详细]
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 目录实现效果:实现环境实现方法一:基本思路主要代码JavaScript代码总结方法二主要代码总结方法三基本思路主要代码JavaScriptHTML总结实 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • 本文介绍了在Vue项目中如何结合Element UI解决连续上传多张图片及图片编辑的问题。作者强调了在编码前要明确需求和所需要的结果,并详细描述了自己的代码实现过程。 ... [详细]
  • 本文介绍了Android 7的学习笔记总结,包括最新的移动架构视频、大厂安卓面试真题和项目实战源码讲义。同时还分享了开源的完整内容,并提醒读者在使用FileProvider适配时要注意不同模块的AndroidManfiest.xml中配置的xml文件名必须不同,否则会出现问题。 ... [详细]
  • web.py开发web 第八章 Formalchemy 服务端验证方法
    本文介绍了在web.py开发中使用Formalchemy进行服务端表单数据验证的方法。以User表单为例,详细说明了对各字段的验证要求,包括必填、长度限制、唯一性等。同时介绍了如何自定义验证方法来实现验证唯一性和两个密码是否相等的功能。该文提供了相关代码示例。 ... [详细]
author-avatar
多米音乐_54101533
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有