Entity Framework Code First实体关联数据加载
在项目过程中,两个实体数据之间在往往并非完全独立的,而是存在一定的关联关系,如一对一、一对多及多对多等关联。存在关联关系的实体,经常根据一个实体的实例来查询获取与之关联的另外实体的实例。
Entity Framework常用处理数据关联加载的方式有3种:延迟加载(Lazy Loading)、贪婪加载(Eager Loading)以及显示加载(Explicit Loading)。
1、延迟加载(Lazy Loading)
延迟加载是项目应用中常见的方式,Entity Framework在需要时可以自动为一个实体的实例获取关联的数据。
Entity Framework自动延迟加载需要满足的条件:
1>、POCO类必须是public而非sealed;
2>、集合属性必须的Virtual修饰的,这样Entity Framework才能Override以包含延迟加载的逻辑。
示例:
文件类Province.cs:
- using System;
- using System.Collections.Generic;
- namespace Portal.Models
- {
- public class Province
- {
- public Province()
- {
- this.Cities = new List<City>();
- }
- public int ProvinceID { get; set; }
- public string ProvinceNo { get; set; }
- public string ProvinceName { get; set; }
- public virtual ICollection<City> Cities { get; set; }
- }
- }
文件类City.cs:
- using System;
- using System.Collections.Generic;
- namespace Portal.Models
- {
- public class City
- {
- public int CityID { get; set; }
- public Nullable<int> ProvinceID { get; set; }
- public string CityNo { get; set; }
- public string CityName { get; set; }
- public virtual Province Province { get; set; }
- }
- }
文件类Program.cs:
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Data.Entity;
- using Portal.Models;
- namespace Portal
- {
- class Program
- {
- static void Main(string[] args)
- {
- using (var ctx = new PortalContext())
- {
- var province = ctx.Provinces.Find();
- foreach (var city in province.Cities)
- {
- Console.WriteLine(city.CityName);
- }
- }
- }
- }
- }
以上代码在运行之后,执行了两条SQL语句,分别用于读取单条Province记录及与该条记录相关联的City记录。
- exec sp_executesql N'SELECT
- [Limit1].[ProvinceID] AS [ProvinceID],
- [Limit1].[ProvinceNo] AS [ProvinceNo],
- [Limit1].[ProvinceName] AS [ProvinceName]
- FROM ( SELECT TOP (2)
- [Extent1].[ProvinceID] AS [ProvinceID],
- [Extent1].[ProvinceNo] AS [ProvinceNo],
- [Extent1].[ProvinceName] AS [ProvinceName]
- FROM [dbo].[Province] AS [Extent1]
- WHERE [Extent1].[ProvinceID] = @p0
- ) AS [Limit1]',N'@p0 int',@p0=3
- exec sp_executesql N'SELECT
- [Extent1].[CityID] AS [CityID],
- [Extent1].[ProvinceID] AS [ProvinceID],
- [Extent1].[CityNo] AS [CityNo],
- [Extent1].[CityName] AS [CityName]
- FROM [dbo].[City] AS [Extent1]
- WHERE [Extent1].[ProvinceID] = @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=3
延迟加载的不足:
延迟加载使用简单,应用程序不需要真正知道数据已经被从数据库中加载出来,但只要将可能导致大量的SQL查询被发送到数据库中执行,数据库进行了不必要的查询。
2、贪婪加载(Eager Loading)
贪婪加载:使用Include加载关联的数据,在Entity Framework进行查询时,即同时加载出关联的数据。Entity Framework贪婪加载将使用一条JOIN的SQL语句进行查询。
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Data.Entity;
- using Portal.Models;
- namespace Portal
- {
- class Program
- {
- static void Main(string[] args)
- {
- using (var ctx = new PortalContext())
- {
- var provinces = ctx.Provinces
- .Include(p => p.Cities);
- foreach (var province in provinces)
- {
- foreach (var city in province.Cities)
- {
- Console.WriteLine("{0}-{1}", province.ProvinceName, city.CityName);
- }
- }
- }
- }
- }
- }
运行代码所执行的SQL语句:
- SELECT
- [Project1].[ProvinceID] AS [ProvinceID],
- [Project1].[ProvinceNo] AS [ProvinceNo],
- [Project1].[ProvinceName] AS [ProvinceName],
- [Project1].[C1] AS [C1],
- [Project1].[CityID] AS [CityID],
- [Project1].[ProvinceID1] AS [ProvinceID1],
- [Project1].[CityNo] AS [CityNo],
- [Project1].[CityName] AS [CityName]
- FROM ( SELECT
- [Extent1].[ProvinceID] AS [ProvinceID],
- [Extent1].[ProvinceNo] AS [ProvinceNo],
- [Extent1].[ProvinceName] AS [ProvinceName],
- [Extent2].[CityID] AS [CityID],
- [Extent2].[ProvinceID] AS [ProvinceID1],
- [Extent2].[CityNo] AS [CityNo],
- [Extent2].[CityName] AS [CityName],
- CASE WHEN ([Extent2].[CityID] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1]
- FROM [dbo].[Province] AS [Extent1]
- LEFT OUTER JOIN [dbo].[City] AS [Extent2] ON [Extent1].[ProvinceID] = [Extent2].[ProvinceID]
- ) AS [Project1]
- ORDER BY [Project1].[ProvinceID] ASC, [Project1].[C1] ASC
Include语句可以在一次查询中使用多次。
- ctx.Categories
- .Include(c => c.Products)
- .Include(c => c.News);
贪婪加载的不足:
贪婪加载的优势在于仅执行1次SQL查询即返回所需要的结果。但使用JOIN查询在数据库记录条数较多时,多条简单的SQL查询往往比一条复杂的JOIN查询效率要好。
使用Include的LINQ查询
- var provinces = ctx.Provinces
- .Include(p => p.Cities)
- .Where(p => p.ProvinceID > );
- var provinces = from p in ctx.Provinces.Include(p => p.Cities)
- where p.ProvinceID >
- select p;
- var expr = from p in ctx.Provinces
- where p.ProvinceID >
- select p;
- var provinces = expr.Include(p => p.Cities);
3、显示加载(Explicit Loading)
显示加载与延迟加载一样,采用主数据与关联数据独立分开加载。显示加载与延迟加载的区别在于显示加载不会自动的加载关联数据,需要调用方法去加载。
显示加载是使用DbContext.Entry方法来实现的,Entry方法可以获取DbContext中的实体信息。在使用Entry获取实体信息之后,可以使用Collection或Reference方法获取和操作实体关联的集合属性。如使用Load方法查询集合属性。
示例1:显示加载,使用Collection获取集合属性
- using (var ctx = new PortalContext())
- {
- var province = ctx.Provinces.Find();
- ctx.Entry(province)
- .Collection(p => p.Cities)
- .Query()
- .Load();
- foreach (var city in province.Cities)
- {
- Console.WriteLine("{0}-{1}", province.ProvinceName, city.CityName);
- }
- }
上面的代码运行之后,执行的SQL语句:
- exec sp_executesql N'SELECT
- [Limit1].[ProvinceID] AS [ProvinceID],
- [Limit1].[ProvinceNo] AS [ProvinceNo],
- [Limit1].[ProvinceName] AS [ProvinceName]
- FROM ( SELECT TOP (2)
- [Extent1].[ProvinceID] AS [ProvinceID],
- [Extent1].[ProvinceNo] AS [ProvinceNo],
- [Extent1].[ProvinceName] AS [ProvinceName]
- FROM [dbo].[Province] AS [Extent1]
- WHERE [Extent1].[ProvinceID] = @p0
- ) AS [Limit1]',N'@p0 int',@p0=3
- exec sp_executesql N'SELECT
- [Extent1].[CityID] AS [CityID],
- [Extent1].[ProvinceID] AS [ProvinceID],
- [Extent1].[CityNo] AS [CityNo],
- [Extent1].[CityName] AS [CityName]
- FROM [dbo].[City] AS [Extent1]
- WHERE [Extent1].[ProvinceID] = @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=3
从代码运行所执行的SQL语句可以看出,其查询数据库的方式与延迟加载是相同的。
示例2:显示加载,使用Reference方法获取引用属性
- using (var ctx = new PortalContext())
- {
- var city = ctx.Cities.Find();
- ctx.Entry(city).Reference(c => c.Province);
- Console.WriteLine("{0}-{1}", city.Province.ProvinceName, city.CityName);
- }
上面的代码运行之后执行的SQL语句:
- exec sp_executesql N'SELECT
- [Limit1].[CityID] AS [CityID],
- [Limit1].[ProvinceID] AS [ProvinceID],
- [Limit1].[CityNo] AS [CityNo],
- [Limit1].[CityName] AS [CityName]
- FROM ( SELECT TOP (2)
- [Extent1].[CityID] AS [CityID],
- [Extent1].[ProvinceID] AS [ProvinceID],
- [Extent1].[CityNo] AS [CityNo],
- [Extent1].[CityName] AS [CityName]
- FROM [dbo].[City] AS [Extent1]
- WHERE [Extent1].[CityID] = @p0
- ) AS [Limit1]',N'@p0 int',@p0=10
- exec sp_executesql N'SELECT
- [Extent1].[ProvinceID] AS [ProvinceID],
- [Extent1].[ProvinceNo] AS [ProvinceNo],
- [Extent1].[ProvinceName] AS [ProvinceName]
- FROM [dbo].[Province] AS [Extent1]
- WHERE [Extent1].[ProvinceID] = @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=3
检查集合属性是否已经加载:
- using (var ctx = new PortalContext())
- {
- var province = ctx.Provinces.Find();
- Console.WriteLine("Before load:{0}", ctx.Entry(province).Collection(p => p.Cities).IsLoaded);
- ctx.Entry(province)
- .Collection(p => p.Cities)
- .Load();
- Console.WriteLine("After load:{0}", ctx.Entry(province).Collection(p => p.Cities).IsLoaded);
- }
4、集合属性查询
在使用Entry和Collection方法获取到实体集合属性之后,可以使用Query方法对集合属性进行查询。
示例:从内存中查询集合属性
- using (var ctx = new PortalContext())
- {
- var province = ctx.Provinces.Find();
- var cities = from c in province.Cities
- where c.CityID >
- select c;
- foreach (var city in cities)
- {
- Console.WriteLine("{0}-{1}", city.CityID, city.CityName);
- }
- }
代码运行之后执行的SQL语句:
- exec sp_executesql N'SELECT
- [Limit1].[ProvinceID] AS [ProvinceID],
- [Limit1].[ProvinceNo] AS [ProvinceNo],
- [Limit1].[ProvinceName] AS [ProvinceName]
- FROM ( SELECT TOP (2)
- [Extent1].[ProvinceID] AS [ProvinceID],
- [Extent1].[ProvinceNo] AS [ProvinceNo],
- [Extent1].[ProvinceName] AS [ProvinceName]
- FROM [dbo].[Province] AS [Extent1]
- WHERE [Extent1].[ProvinceID] = @p0
- ) AS [Limit1]',N'@p0 int',@p0=5
- exec sp_executesql N'SELECT
- [Extent1].[CityID] AS [CityID],
- [Extent1].[ProvinceID] AS [ProvinceID],
- [Extent1].[CityNo] AS [CityNo],
- [Extent1].[CityName] AS [CityName]
- FROM [dbo].[City] AS [Extent1]
- WHERE [Extent1].[ProvinceID] = @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=5
从对City表执行的SQL语句可以看出,其并对加入查询条件,仅只是对之前通过延长加载方式将Province实体的Cities集合属性载人到内存中,然后通过对内存中的Cities数据进行内存查询,并未生成新的包含查询条件的SQL语句。
示例:在数据库中查询集合属性
- using (var ctx = new PortalContext())
- {
- var province = ctx.Provinces.Find();
- var expr = ctx.Entry(province)
- .Collection(p => p.Cities)
- .Query();
- var cities = from c in expr
- where c.CityID >
- select c;
- foreach (var city in cities)
- {
- Console.WriteLine("{0}-{1}", city.CityID, city.CityName);
- }
- }
代码运行之后执行的SQL语句:
- exec sp_executesql N'SELECT
- [Limit1].[ProvinceID] AS [ProvinceID],
- [Limit1].[ProvinceNo] AS [ProvinceNo],
- [Limit1].[ProvinceName] AS [ProvinceName]
- FROM ( SELECT TOP (2)
- [Extent1].[ProvinceID] AS [ProvinceID],
- [Extent1].[ProvinceNo] AS [ProvinceNo],
- [Extent1].[ProvinceName] AS [ProvinceName]
- FROM [dbo].[Province] AS [Extent1]
- WHERE [Extent1].[ProvinceID] = @p0
- ) AS [Limit1]',N'@p0 int',@p0=5
- exec sp_executesql N'SELECT
- [Extent1].[CityID] AS [CityID],
- [Extent1].[ProvinceID] AS [ProvinceID],
- [Extent1].[CityNo] AS [CityNo],
- [Extent1].[CityName] AS [CityName]
- FROM [dbo].[City] AS [Extent1]
- WHERE ([Extent1].[ProvinceID] = @EntityKeyValue1) AND ([Extent1].[CityID] > 30)',N'@EntityKeyValue1 int',@EntityKeyValue1=5
集合属性Count查询
- using (var ctx = new PortalContext())
- {
- var province = ctx.Provinces.Find();
- var expr = ctx.Entry(province)
- .Collection(p => p.Cities)
- .Query();
- Console.WriteLine(expr.Count());
- }
代码运行生成的SQL语句:
- exec sp_executesql N'SELECT
- [GroupBy1].[A1] AS [C1]
- FROM ( SELECT
- COUNT(1) AS [A1]
- FROM [dbo].[City] AS [Extent1]
- WHERE [Extent1].[ProvinceID] = @EntityKeyValue1
- ) AS [GroupBy1]',N'@EntityKeyValue1 int',@EntityKeyValue1=5
显示加载集合属性的子集:
- using (var ctx = new PortalContext())
- {
- var province = ctx.Provinces.Find();
- ctx.Entry(province)
- .Collection(p => p.Cities)
- .Query()
- .Where(c => c.CityNo.Contains(""))
- .Load();
- }
代码运行后生成的SQL语句:
- exec sp_executesql N'SELECT
- [Extent1].[CityID] AS [CityID],
- [Extent1].[ProvinceID] AS [ProvinceID],
- [Extent1].[CityNo] AS [CityNo],
- [Extent1].[CityName] AS [CityName]
- FROM [dbo].[City] AS [Extent1]
- WHERE ([Extent1].[ProvinceID] = @EntityKeyValue1) AND ([Extent1].[CityNo] LIKE N''%3%'')',N'@EntityKeyValue1 int',@EntityKeyValue1=5
Entity Framework Code First实体关联数据加载的更多相关文章
- Entity Framework Code First -- 延迟加载和预先加载
还是以这两个表为例子 country包含零个或多个city, 这个外键关系是我后来加上去,原来没有. 然后再用Power Tool逆向, 产生如下代码 1: using System.Componen ...
- Entity Framework Code First实体对象变动跟踪
Entity Framework Code First通过DbContext.ChangeTracker对实体对象的变动进行跟踪,实现跟踪的方式有两种:变动跟踪快照和变动跟踪代理. 变动跟踪快照:前面 ...
- Entity Framework入门教程(8)---预先加载、延迟加载、显示加载
1.预先加载 预先加载:在对一种类型的实体进行查询时,将相关的实体作为查询的一部分一起加载.预先加载可以使用Include()方法实现. 1.加载一个相关实体类型 栗子:使用Include()方法从数 ...
- Entity Framework Code First Migrations--EF 的数据迁移
1. 为了演示方便,首先新建一个控制台项目,然后添加对entityframework的引用 使用nuget控制台执行: Install-Package EntityFramework 2.新建一个实体 ...
- Entity Framework Code First学习系列目录
Entity Framework Code First学习系列说明:开发环境为Visual Studio 2010 + Entity Framework 5.0+MS SQL Server 2012, ...
- Entity Framework Code First学习系列
Entity Framework Code First学习系列目录 Entity Framework Code First学习系列说明:开发环境为Visual Studio 2010 + Entity ...
- hibernate框架学习第六天:QBC、分页查询、投影、数据加载策略、二级缓存
QBC查询 1.简单查询 Criteria c = s.createCriteria(TeacherModel.class); 2.获取查询结果 多条:list 单挑:uniqueResult 3.分 ...
- Entity Framework关联查询以及数据加载(延迟加载,预加载)
数据加载分为延迟加载和预加载 EF的关联实体加载有三种方式:Lazy Loading,Eager Loading,Explicit Loading,其中Lazy Loading和Explicit Lo ...
- Entity Framework Code First添加修改及删除单独实体
对于一个单独实体的通常操作有3种:添加新的实体.修改实体以及删除实体. 1.添加新的实体 Entity Framework Code First添加新的实体通过调用DbSet.Add()方法来实现. ...
随机推荐
- Linux系统学习优缺点
Linux是一套操作系统,按照鸟哥的说法Linux提供了一个完整的操作系统当中最底层的硬件控制与资源管理的完整架构,这个架构是沿袭Unix良好的传统而来的,功能强大而且稳定性卓越.其实Torvalds ...
- ElasticSearch问题记录
1.Young GC导致集群master重新选举,一台server fail [2016-12-10 07:38:24,546][WARN ][transport ] [BFRD_1] Receive ...
- 双向链表、双向循环链表的JS实现
关于链表简介.单链表.单向循环链表.JS中的使用以及扩充方法: 单链表.循环链表的JS实现 关于四种链表的完整封装: https://github.com/zhuwq585/Data-Structu ...
- git上传代码到osc@git
1.get an account 2.get a ssh-key 3.git setting git config --global user.name "...." git co ...
- Java_类似java.lang.VerifyError: Expecting a stackmap frame at branch target 22 in method的解决方法
报异常的方法内使用了Java 7的新特性:自动资源释放,类似于try(){},即在try后面跟一括号,在括号里面对一些资源赋值,try里面的代码块执行完毕之后会自动释放try后面的括号中声明的资源. ...
- java-如何用eclipse打包jar
Eclipse通过导出的方式(右键单击项目,之后选择Export)打包java类文件生成jar包. 方法一:(在项目工程没有引用外部jar包时,直接导出) 选中工程---->右键,Export. ...
- 【http代理报文】通过发包实现代理请求网页内容
工作中,我们难免需要通过TCP/IP协议发送报文来直接请求网页内容(比如爬虫工具),有同学问如何通过HTTP代理来请求网页,其实我们只需要把报文稍稍修改下,发送给代理服务器即可实现. 基础不过关的朋友 ...
- C#简单的上位机制作之界面设计
今天开始打算正式在博客园落户了,写点有用的吧, 一个简单的C#上位机,也就是串口调试助手废话不多说,新建windows应用程序 到这人一个工程就算是新建完成了,然后就是组件的添加了,我们需要在里面添加 ...
- sql查询
结果集是 id name 1 张三2 张三3 李四4 王五5 王五我想查询出有多少不重名的人的数量,并显示在每一行结果集里面结果如下:num id name 3 1 张三3 2 ...
- Android+jsp +html 文件上传案例 已测试 成功通过
我文件上传一直是广大读者一个问题 今天就把成功案例写下 javaweb 网页前段 <%@ page language="java" import="java.uti ...