在项目过程中,两个实体数据之间在往往并非完全独立的,而是存在一定的关联关系,如一对一、一对多及多对多等关联。存在关联关系的实体,经常根据一个实体的实例来查询获取与之关联的另外实体的实例。

  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实体关联数据加载的更多相关文章

  1. Entity Framework Code First -- 延迟加载和预先加载

    还是以这两个表为例子 country包含零个或多个city, 这个外键关系是我后来加上去,原来没有. 然后再用Power Tool逆向, 产生如下代码 1: using System.Componen ...

  2. Entity Framework Code First实体对象变动跟踪

    Entity Framework Code First通过DbContext.ChangeTracker对实体对象的变动进行跟踪,实现跟踪的方式有两种:变动跟踪快照和变动跟踪代理. 变动跟踪快照:前面 ...

  3. Entity Framework入门教程(8)---预先加载、延迟加载、显示加载

    1.预先加载 预先加载:在对一种类型的实体进行查询时,将相关的实体作为查询的一部分一起加载.预先加载可以使用Include()方法实现. 1.加载一个相关实体类型 栗子:使用Include()方法从数 ...

  4. Entity Framework Code First Migrations--EF 的数据迁移

    1. 为了演示方便,首先新建一个控制台项目,然后添加对entityframework的引用 使用nuget控制台执行: Install-Package EntityFramework 2.新建一个实体 ...

  5. Entity Framework Code First学习系列目录

    Entity Framework Code First学习系列说明:开发环境为Visual Studio 2010 + Entity Framework 5.0+MS SQL Server 2012, ...

  6. Entity Framework Code First学习系列

    Entity Framework Code First学习系列目录 Entity Framework Code First学习系列说明:开发环境为Visual Studio 2010 + Entity ...

  7. hibernate框架学习第六天:QBC、分页查询、投影、数据加载策略、二级缓存

    QBC查询 1.简单查询 Criteria c = s.createCriteria(TeacherModel.class); 2.获取查询结果 多条:list 单挑:uniqueResult 3.分 ...

  8. Entity Framework关联查询以及数据加载(延迟加载,预加载)

    数据加载分为延迟加载和预加载 EF的关联实体加载有三种方式:Lazy Loading,Eager Loading,Explicit Loading,其中Lazy Loading和Explicit Lo ...

  9. Entity Framework Code First添加修改及删除单独实体

    对于一个单独实体的通常操作有3种:添加新的实体.修改实体以及删除实体. 1.添加新的实体 Entity Framework Code First添加新的实体通过调用DbSet.Add()方法来实现. ...

随机推荐

  1. Mac无法写入移动硬盘

    1.使用mac 磁盘工具,格式化移动硬盘 频繁穿插于mac于pc之间者,大批量拷贝大型文件者,请用exfat 首先,排除Fat32,虽然这是兼容性最好的文件格式,但一个不支持4g以上文件的格式(现在随 ...

  2. NOI 题库 9272 题解

    9272   偶数个数字3 描述 在所有的N位数中,有多少个数中有偶数个数字3? 输入 一行给出数字N,N<=1000 输出 如题 样例输入 2 样例输出 73 Solution : 令f ( ...

  3. Day 2:增加SplashScreen

    If you want to add just single image, then create a pic in the size of 480*800 and name it as Splash ...

  4. appium过程中的问题

    1.在eclipse中点击Genymotion Virtual Device Manager ,选择虚拟设备,点击start后,无反应.    解决方法:Help/Install New Softwa ...

  5. ANGULAR $HTTP请求【转】

    angular使用post.get向后台传参的问题 一.问题的来源 我们都知道向后台传参可以使用get.put,其形式就类似于name=jyy&id=001.但是在ng中我却发现使用$http ...

  6. iOS开发之记录用户登录状态

    iOS开发之记录用户登录状态 我们知道:CoreData的配置和使用步骤还是挺复杂的.但熟悉CoreData的使用流程后,CoreData还是蛮好用的.今天要说的是如何记录我们用户的登陆状态.例如微信 ...

  7. 借助cookie实现子网页修改父网页内容遇到的问题:同一个浏览器访问相同页面,会互相影响。 (已解决)

    问题是这样的,  我把左侧菜单做成了网页, 然后点击左侧菜单选项会改变右侧内容, 也就是子网页访问并修改父网页的内容. 为了兼容性更好, 我没有使用farther,或者opener等方法,  而是用了 ...

  8. 通过修改i8042prt端口驱动中类驱动Kbdclass的回调函数地址,达到过滤键盘操作的例子

    同样也是寒江独钓的例子,但只给了思路,现贴出实现代码 原理是通过改变端口驱动中本该调用类驱动回调函数的地方下手 //替换分发函数 来实现过滤 #include <wdm.h> #inclu ...

  9. vs中“Stack around the variable was corrupted”的解决方案

    把 project->配置属性->c/c++->代码生成->基本运行时检查 为 默认值 就不会报本异常.具体原因正在研究中... 如果改为其他就有exception. exce ...

  10. SCRIPT65535: 意外地调用了方法或属性访问 ie下不兼容 解决

    一般有一下几种 $("#id").text("xxx")  改成 $("#id").attr("text"," ...