浅谈Entity Framework中的数据加载方式

简介:

如果你还没有接触过或者根本不了解什么是Entity Framework,那么请看这里http://www.entityframeworktutorial.net/EntityFramework-Architecture.aspx,其中的一系列文章以非常简单易懂的形式一步步介绍了Entity Framework的一些基本概念和操作方法。一句话,Entity Framework是微软新的数据操作框架,在项目中引入Entity Framework可以极大的方便开发人员完成程序与数据库的各种操作。在早期的.NET应用程序中,我们直接通过SqlConnection,SqlDataAdapter,DataReader等一些基本对象来实现与数据库的操作,与数据库的每一次交互都需要专门编写大量的代码。为了将程序员的精力从这些看似无聊的代码中释放出来,让更多的人去关注程序业务方面的操作,后来出现了各种与数据库交互的类库,有一些是开源的,也有一些是程序员自己编写的通用类库。这里面比较著名的有NHibernate.NET——一个基于Mapping的关系型数据库框架。NHibernate功能强大,不过由于配置起来稍微有点复杂并且缺少默认的VS代码生成工具,在快速小型项目中往往难以得心应手。Entity Framework (以下简称EF)的出现从根本上解决了这一问题,使得程序员可以彻底从与数据库交互的底层代码中释放出来,与数据库的交互变得前所未有的简单。

  使用EF在与关系型数据库的交互中不可避免地需要加载数据,如何加载数据变得至关重要。你可以设想在一个包含数十万条数据的表中,你如何通过EF来加载数据呢?一次性将所有数据载入服务器内存或者在循环中一遍又一遍地分步加载数据?使用什么样的数据加载方式需要具体问题具体分析,我们不能在这里笼统地下决定说哪种方式好哪种方式不好。但有一点是需要遵循的,那就是如何提高数据加载的效率。EF提供了几种不同的数据加载方式,我们可以根据不同的需要灵活使用它们。

  先简单说一下如何创建环境。如果你对这些步骤了如指掌,请直接跳过。

1. 在Visual Studio中创建一个示例工程。最简单的莫过于ConsoleApplication

2. 在工程中添加ADO.NET Entity Data Model。如下图所示,

  选择其中的两个表作为示例,表Teacher和表Course,关系如下,

  添加edmx之后,Visual Studio为自动帮我们生成/添加所有需要的文件和内容,然后我们就可以开始在代码中操作数据库了。来看看在EF中几种不同的数据加载方式。

 

惰性加载(Lazy Loading)

  默认情况下,EF会使用惰性加载方式加载数据,即ctx.Configuration.LazyLoadingEnabled = true; 在下面的代码中,外层循环会执行一次查询,并将返回的结果存放在变量q中。而内层循环会在每一次循环过程中独立进行查询,所以,如果数据库表Teacher中有100条记录而Course有1000条记录,那么整个过程将产生1001次查询。

复制代码
using (var ctx = new SchoolDBEntities())
{
    var q = from t in ctx.Teachers
            select t;

    foreach (var teacher in q)
    {
        Console.WriteLine("Teacher : {0}", teacher.TeacherName);
        Console.WriteLine("Respective Courses...");

        foreach (var course in teacher.Courses)
        {
            Console.WriteLine("Course name : {0}", course.CourseName);
        }

        Console.WriteLine();
        Console.ReadKey();
    }
}
复制代码

  下面是程序执行的结果以及在SQL Server Profiler中的跟踪记录。可以清楚地看到,对表Teacher只进行了一次查询,由于该表只有8条记录,于是在内层循环中又分别产生了8次对表Course的查询。

  在某些场合下,这种情况是可以接受的。你完全可以根据需要来控制内层循环何时显示加载数据,或者根本不加载数据。但是,在分层结构的应用程序中,上述代码结构并不适用,因为内层循环需要依赖于外层的Context,也就是说它们是在同一个数据库上下文中完成的,如果尝试将内层循环的代码移到外面或者其它类中,则它将获取不到任何数据。

 

显式加载(Explicit Loading)

  如果你想人为控制惰性加载的行为,可以尝试使用下面的代码。首先需要手动关闭EF的惰性加载,通过代码ctx.Configuration.LazyLoadingEnabled = false;来完成。

复制代码
using (var ctx = new SchoolDBEntities())
{
    ctx.Configuration.LazyLoadingEnabled = false;

    var q = from t in ctx.Teachers
            select t;
    
    foreach (var teacher in q)
    {
        Console.WriteLine("Teacher : {0}", teacher.TeacherName);
        Console.WriteLine("Respective Courses...");

        // Conditionally load the child data
        if (true)
        {
            ctx.Entry(teacher).Collection(c => c.Courses).Load();
        }

        foreach (var course in teacher.Courses)
        {
            Console.WriteLine("Course name : {0}", course.CourseName);
        }

        Console.WriteLine();
        Console.ReadKey();
    }
}
复制代码

  注意内层循环只有在上面高亮显示部分的代码执行之后才会获取到数据,否则返回结果为0。通过添加判断条件,我们可以对数据加载方式进行控制,从而有效地减少程序与数据库交互的次数。大多数情况下,我们从数据库获取到的数据并不都是有用的,如果每次只有很少一部分数据有用,那么我们为什么不过滤掉那些无用的数据从而尽量较少数据交互的次数呢?

 

预先加载(Eager Loading)

  如果你想让所有数据一次性全部加载到内存中,那么你需要使用.Include(Entity)方法。看下面的代码,

复制代码
using (var ctx = new SchoolDBEntities())
{
    var q = from t in ctx.Teachers.Include("Courses")
            select t;
    
    foreach (var teacher in q)
    {
        Console.WriteLine("Teacher : {0}", teacher.TeacherName);
        Console.WriteLine("Respective Courses...");

        foreach (var course in teacher.Courses)
        {
            Console.WriteLine("Course name : {0}", course.CourseName);
        }

        Console.WriteLine();
        Console.ReadKey();
    }
}
复制代码

  如果你查看SQl Server Profiler中的跟踪信息,你会发现只有一次数据交互过程,即程序只通过一次查询便获取到了所有需要的数据。在分层结构中,该方法是最容易的,我们可以将数据库底层获取到的结果返回给上层,它不具有任何依赖项。同时,它也可以减少程序与数据库的交互次数。不过仍然有缺点,那就是如果数据量较大,一次性将所有数据载入内存往往并不是最明智的选择。.Include(Entity)方法允许级联使用,你可以预先加载具有多层级结构的数据。

  就像前面所说,选择什么样的数据加载方式需要因时而异,每一种数据加载方式都有它存在的意义,但目的只有一个,那就是以最少的代价获取到需要的数据。

相关文章:Loading Related Objects (Entity Framework)


本文转自Jaxu博客园博客,原文链接:http://www.cnblogs.com/jaxu/p/3700511.html,如需转载请自行联系原作者

相关文章
|
人工智能 弹性计算 运维
云上创新丨云计算,如何从IT战略上升为企业核心战略?
云计算在中国发展十余年,越来越多行业开始用云,与此同时,云计算也已从企业IT战略上升为企业核心战略。未来,云计算一定是企业和开发者的首选,技术创新也一定优先诞生在云上。
云上创新丨云计算,如何从IT战略上升为企业核心战略?
|
负载均衡 网络协议 安全
负载均衡4层和7层区别
所谓四层就是基于IP+端口的负载均衡;七层就是基于URL等应用层信息的负载均衡
|
10月前
|
存储 并行计算 算法
散列函数密码存储
散列函数密码存储
154 6
|
人工智能 供应链 监控
供应链管理(SCM):从物流到信息流的技术融合之旅
【6月更文挑战第24天】供应链管理聚焦物流与信息流的融合,借助物联网、大数据及AI提升效率。物流技术进步增强透明度,信息流分析优化决策,两者结合实现供应链可视化和智能化,确保快速响应市场变化,增强企业竞争力。科技驱动的供应链未来将更加智能、自动化。
|
JavaScript Go 项目管理
云效常见问题之使用gitlab仓库将代码合并评审环节集成到云效如何解决
云效(CloudEfficiency)是阿里云提供的一套软件研发效能平台,旨在通过工程效能、项目管理、质量保障等工具与服务,帮助企业提高软件研发的效率和质量。本合集是云效使用中可能遇到的一些常见问题及其答案的汇总。
275 1
|
JavaScript 索引
Vue.js中循环语句的使用方法和相关技巧
Vue.js中循环语句的使用方法和相关技巧
194 1
Vue.js中循环语句的使用方法和相关技巧
|
存储 算法 Linux
VDO卷的配置
在CentOS 7环境下,VDO通过重删和压缩技术节省磁盘空间,使1T磁盘可存储1.5T数据,降低成本。创建VDO步骤包括:添加64GB新硬盘,安装vdo和kmod-kvdo软件,使用`vdo create`命令创建VDO卷,如`vdo create --name=myvdo --device=/dev/sdb --vdoLogicalSize=200G`。之后,可通过`vdo list`、`vdo status`和`vdostats`检查状态和空间使用。最后,格式化VDO卷,创建挂载点并挂载,完成设置。
177 0
|
安全 算法 编译器
【C++ 泛型编程 进阶篇】C++ 元模板推导函数调用的结果类型 std::result_of/std::invoke_result全面教程
【C++ 泛型编程 进阶篇】C++ 元模板推导函数调用的结果类型 std::result_of/std::invoke_result全面教程
883 0
|
监控 搜索推荐 测试技术
微服务可见可观测性
【2月更文挑战第29天】本文介绍了服务治理的反馈机制关键步骤:服务可见性、变更可见性和观测可见性。
|
SQL Java 数据库连接
Mybatis 查询出来的数据集合数量正确,但是具体数据为null
Mybatis 查询出来的数据集合数量正确,但是具体数据为null
283 1