system.data.objects dev guy(a developer in EF)的blog文why use Entity Framwok中对Nhibernate和Entity Framework比较所说的那样,“我不是要让大家放弃Nhibernate或其它的ORM框架或者说要让大家相信EF比Nhibernate或其它的框架要好,它们不同的地方就意味着你可以根据你的实际情况做出不同的决定来选择合适的工具”。
在我前一篇blog Lazy Loading(一)(延迟加载)。一些前辈说不喜欢EF的种种。所以须说明一点,我写关于Entity Framework的文章不是为了大家都接受EF。正如system.data.objects dev guy(a developer in EF)的blog文why use Entity Framwok中对Nhibernate和Entity Framework比较所说的那样,“我不是要让大家放弃Nhibernate或其它的ORM框架或者说要让大家相信EF比Nhibernate或其它的框架要好,它们不同的地方就意味着你可以根据你的实际情况做出不同的决定来选择合适的工具”。无所谓那种框架,适合即是最好的。
下面进入正题,为了更好地说明EF在加载实体时的不同特征,故引用Linq To Sql作为参照。
上面的模型比起上一篇文章中的模型多了2个实体,Order,Product。关系也简单叙述一下employeeTest:Order=>1:n,Order:Product=>n:n 多对多的关系映射到Order_Product由于不带负载就不必作为一个实体呈现出来,不明白看我EDM中多对多关系(Many-to-Many Acssociation)以及有效负载(Payloads)问题 这篇。

Code
1 var query = from e in context.Perosn.OfType<employeeTest>() where e.PersonID == guid select e;
2 EmployeeNo = query.FirstOrDefault().EmployeeNo;
3 var employee = context.Perosn.OfType<employeeTest>().Where(e => e.PersonID == guid);
4 //var employee = context.Perosn.OfType<employeeTest>().Where(e => e.PersonID == guid).FirstOrDefault();
5 EmployeeNo = employee.FirstOrDefault().EmployeeNo;
跟踪上面代码,你就知道Sql查询执行在FirstOrDefault()方法执行时,这点和Linq to sql是相同的。接下来我们通过employee来访问关联对象Order。

Code
1 int count = count = employee.Order.Count;
2 employee.Order.Load();
3 count = employee.Order.Count;
在没有执行 employee.Order.Load() 之前,employee.Order.Count是为0的,因为EF默认是不会隐式加载关联实体的,必须显式去加载(及时记载,Eager Loading)。即是不会在查询得到employee的同时,自动取出的关联对象Order的集合.在Linq to sql里面是隐式加载关联实体。 刚刚是通过Employee访问Order,下面反过来试试;

Code
1 Order order = context.Order.Where(o => o.OrderID == guid).FirstOrDefault();
2 if (order.employeeTestReference.IsLoaded)
3 order.employeeTestReference.Load();
2个实体相互访问的不同在于employeeTest 与Order是1:n的关系。除了上面的显式加载方式,在EF中实现Eager Laoding就是使用Include:

Code
1 employeeTest employee = context.Perosn.OfType<employeeTest>().Where(e => e.PersonID == guid).FirstOrDefault();
2 employeeTest employee = context.Perosn.OfType<employeeTest>().Include("Order").Where(q => q.PersonID == guid).FirstOrDefault();
3 IQueryable<employeeTest> list = context.Perosn.OfType<employeeTest>().Include("Order").Where<employeeTest>(q => q.PersonID == guid);
如3个查询语句:第一个的生成的Sql语句很复杂,那是因为我模型里继承用的比较多,导致生成sql判断比较多

Code
1 exec sp_executesql N'SELECT
2 [Limit1].[C6] AS [C1],
3 [Limit1].[C1] AS [C2],
4 [Limit1].[EmployeeID] AS [EmployeeID],
5 [Limit1].[Name] AS [Name],
6 [Limit1].[Age] AS [Age],
7 [Limit1].[Country] AS [Country],
8 [Limit1].[City] AS [City],
9 [Limit1].[BirthDay] AS [BirthDay],
10 [Limit1].[EmployeeNo] AS [EmployeeNo],
11 [Limit1].[C2] AS [C3],
12 [Limit1].[C3] AS [C4],
13 [Limit1].[C4] AS [C5],
14 [Limit1].[C5] AS [C6],
15 [Limit1].[Rank] AS [Rank],
16 [Limit1].[TypeOfDepartment] AS [TypeOfDepartment]
17 FROM ( SELECT TOP (1)
18 [Extent1].[EmployeeID] AS [EmployeeID],
19 [Extent1].[EmployeeNo] AS [EmployeeNo],
20 [Extent1].[TypeOfDepartment] AS [TypeOfDepartment],
21 [Extent1].[Rank] AS [Rank],
22 [Extent2].[Name] AS [Name],
23 [Extent2].[Age] AS [Age],
24 [Extent2].[Country] AS [Country],
25 [Extent2].[City] AS [City],
26 [Extent2].[BirthDay] AS [BirthDay],
27 CASE WHEN (((CASE WHEN ([Extent1].[TypeOfEmployee] = ''Hour'') THEN cast(1 as bit) ELSE cast(0 as bit) END) <> cast(1 as bit)) AND ((CASE WHEN ([Extent1].[TypeOfEmployee] = ''Month'') THEN cast(1 as bit) ELSE cast(0 as bit) END) <> cast(1 as bit))) THEN ''0X0X'' WHEN ((CASE
28 WHEN ([Extent1].[TypeOfEmployee] = ''Hour'') THEN cast(1 as bit) ELSE cast(0 as bit) END) = 1) THEN ''0X0X0X'' ELSE ''0X0X1X'' END AS [C1],
29 CASE WHEN (((CASE WHEN ([Extent1].[TypeOfEmployee] = ''Hour'') THEN cast(1 as bit) ELSE cast(0 as bit) END) <> cast(1 as bit)) AND ((CASE WHEN ([Extent1].[TypeOfEmployee] = ''Month'') THEN cast(1 as bit) ELSE cast(0 as bit) END) <> cast(1 as bit))) THEN CAST(NULL AS float)
30 WHEN ((CASE WHEN ([Extent1].[TypeOfEmployee] = ''Hour'') THEN cast(1 as bit) ELSE cast(0 as bit) END) = 1) THEN [Extent1].[Rate] END AS [C2],
31 CASE WHEN (((CASE WHEN ([Extent1].[TypeOfEmployee] = ''Hour'') THEN cast(1 as bit) ELSE cast(0 as bit) END) <> cast(1 as bit)) AND ((CASE WHEN ([Extent1].[TypeOfEmployee] = ''Month'') THEN cast(1 as bit) ELSE cast(0 as bit) END) <> cast(1 as bit))) THEN CAST(NULL AS int)
32 WHEN ((CASE WHEN ([Extent1].[TypeOfEmployee] = ''Hour'') THEN cast(1 as bit) ELSE cast(0 as bit) END) = 1) THEN [Extent1].[Hour] END AS [C3],
33 CASE WHEN (((CASE WHEN ([Extent1].[TypeOfEmployee] = ''Hour'') THEN cast(1 as bit) ELSE cast(0 as bit) END) <> cast(1 as bit)) AND ((CASE WHEN ([Extent1].[TypeOfEmployee] = ''Month'') THEN cast(1 as bit) ELSE cast(0 as bit) END) <> cast(1 as bit))) THEN CAST(NULL AS float)
34 WHEN ((CASE WHEN ([Extent1].[TypeOfEmployee] = ''Hour'') THEN cast(1 as bit) ELSE cast(0 as bit) END) = 1) THEN [Extent1].[Salary] END AS [C4],
35 CASE WHEN (((CASE WHEN ([Extent1].[TypeOfEmployee] = ''Hour'') THEN cast(1 as bit) ELSE cast(0 as bit) END) <> cast(1 as bit)) AND ((CASE WHEN ([Extent1].[TypeOfEmployee] = ''Month'') THEN cast(1 as bit) ELSE cast(0 as bit) END) <> cast(1 as bit))) THEN CAST(NULL AS float)
36 WHEN ((CASE WHEN ([Extent1].[TypeOfEmployee] = ''Hour'') THEN cast(1 as bit) ELSE cast(0 as bit) END) = 1) THEN CAST(NULL AS float) ELSE [Extent1].[Salary] END AS [C5],
37 1 AS [C6]
38 FROM [dbo].[employeeTest] AS [Extent1]
39 INNER JOIN [dbo].[Perosn] AS [Extent2] ON [Extent1].[EmployeeID] = [Extent2].[PersonID]
40 WHERE [Extent1].[EmployeeID] = @p__linq__1
41 ) AS [Limit1]',N'@p__linq__1 uniqueidentifier',@p__linq__1='1174CD94-102B-4419-B55D-513497A240E1'
与第2条生成的sql查询

Code
1
exec sp_executesql N'SELECT
2
[Project2].[EmployeeID] AS [EmployeeID],
3
[Project2].[PersonID] AS [PersonID],
4
[Project2].[C1] AS [C1],
5
[Project2].[Name] AS [Name],
6
[Project2].[Age] AS [Age],
7
[Project2].[Country] AS [Country],
8
[Project2].[City] AS [City],
9
[Project2].[BirthDay] AS [BirthDay],
10
[Project2].[EmployeeNo] AS [EmployeeNo],
11
[Project2].[C2] AS [C2],
12
[Project2].[C3] AS [C3],
13
[Project2].[C4] AS [C4],
14
[Project2].[C5] AS [C5],
15
[Project2].[C6] AS [C6],
16
[Project2].[Rank] AS [Rank],
17
[Project2].[TypeOfDepartment] AS [TypeOfDepartment],
18
[Project2].[C8] AS [C7],
19
[Project2].[C7] AS [C8],
20
[Project2].[OrderID] AS [OrderID],
21
[Project2].[OrderNo] AS [OrderNo],
22
[Project2].[Quantity] AS [Quantity],
23
[Project2].[Price] AS [Price],
24
[Project2].[EmployeeID1] AS [EmployeeID1]
25
FROM ( SELECT
26
[Limit1].[EmployeeID] AS [EmployeeID],
27
[Limit1].[EmployeeNo] AS [EmployeeNo],
28
[Limit1].[TypeOfDepartment] AS [TypeOfDepartment],
29
[Limit1].[Rank] AS [Rank],
30
[Limit1].[PersonID] AS [PersonID],
31
[Limit1].[Name] AS [Name],
32
[Limit1].[Age] AS [Age],
33
[Limit1].[Country] AS [Country],
34
[Limit1].[City] AS [City],
35
[Limit1].[BirthDay] AS [BirthDay],
36
[Limit1].[C1] AS [C1],
37
[Limit1].[C2] AS [C2],
38
[Limit1].[C3] AS [C3],
39
[Limit1].[C4] AS [C4],
40
[Limit1].[C5] AS [C5],
41
[Limit1].[C6] AS [C6],
42
[Extent3].[OrderID] AS [OrderID],
43
[Extent3].[OrderNo] AS [OrderNo],
44
[Extent3].[Quantity] AS [Quantity],
45
[Extent3].[Price] AS [Price],
46
[Extent3].[EmployeeID] AS [EmployeeID1],
47
CASE WHEN ([Extent3].[OrderID] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C7],
48
CASE WHEN ([Extent3].[OrderID] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C8]
49
FROM (SELECT TOP (1)
50
[Extent1].[EmployeeID] AS [EmployeeID],
51
[Extent1].[EmployeeNo] AS [EmployeeNo],
52
[Extent1].[TypeOfDepartment] AS [TypeOfDepartment],
53
[Extent1].[Rank] AS [Rank],
54
[Extent2].[PersonID] AS [PersonID],
55
[Extent2].[Name] AS [Name],
56
[Extent2].[Age] AS [Age],
57
[Extent2].[Country] AS [Country],
58
[Extent2].[City] AS [City],
59
[Extent2].[BirthDay] AS [BirthDay],
60
CASE WHEN (((CASE WHEN ([Extent1].[TypeOfEmployee] = ''Hour'') THEN cast(1 as bit) ELSE cast(0 as bit) END) <> cast(1 as bit)) AND ((CASE WHEN ([Extent1].[TypeOfEmployee] = ''Month'') THEN cast(1 as bit) ELSE cast(0 as bit) END) <> cast(1 as bit))) THEN ''0X0X'' WHEN
61
((CASE WHEN ([Extent1].[TypeOfEmployee] = ''Hour'') THEN cast(1 as bit) ELSE cast(0 as bit) END) = 1) THEN ''0X0X0X'' ELSE ''0X0X1X'' END AS [C1],
62
CASE WHEN (((CASE WHEN ([Extent1].[TypeOfEmployee] = ''Hour'') THEN cast(1 as bit) ELSE cast(0 as bit) END) <> cast(1 as bit)) AND ((CASE WHEN ([Extent1].[TypeOfEmployee] = ''Month'') THEN cast(1 as bit) ELSE cast(0 as bit) END) <> cast(1 as bit))) THEN CAST(NULL AS
63
float) WHEN ((CASE WHEN ([Extent1].[TypeOfEmployee] = ''Hour'') THEN cast(1 as bit) ELSE cast(0 as bit) END) = 1) THEN [Extent1].[Rate] END AS [C2],
64
CASE WHEN (((CASE WHEN ([Extent1].[TypeOfEmployee] = ''Hour'') THEN cast(1 as bit) ELSE cast(0 as bit) END) <> cast(1 as bit)) AND ((CASE WHEN ([Extent1].[TypeOfEmployee] = ''Month'') THEN cast(1 as bit) ELSE cast(0 as bit) END) <> cast(1 as bit))) THEN CAST(NULL AS
65
int) WHEN ((CASE WHEN ([Extent1].[TypeOfEmployee] = ''Hour'') THEN cast(1 as bit) ELSE cast(0 as bit) END) = 1) THEN [Extent1].[Hour] END AS [C3],
66
CASE WHEN (((CASE WHEN ([Extent1].[TypeOfEmployee] = ''Hour'') THEN cast(1 as bit) ELSE cast(0 as bit) END) <> cast(1 as bit)) AND ((CASE WHEN ([Extent1].[TypeOfEmployee] = ''Month'') THEN cast(1 as bit) ELSE cast(0 as bit) END) <> cast(1 as bit))) THEN CAST(NULL AS
67
float) WHEN ((CASE WHEN ([Extent1].[TypeOfEmployee] = ''Hour'') THEN cast(1 as bit) ELSE cast(0 as bit) END) = 1) THEN [Extent1].[Salary] END AS [C4],
68
CASE WHEN (((CASE WHEN ([Extent1].[TypeOfEmployee] = ''Hour'') THEN cast(1 as bit) ELSE cast(0 as bit) END) <> cast(1 as bit)) AND ((CASE WHEN ([Extent1].[TypeOfEmployee] = ''Month'') THEN cast(1 as bit) ELSE cast(0 as bit) END) <> cast(1 as bit))) THEN CAST(NULL AS
69
float) WHEN ((CASE WHEN ([Extent1].[TypeOfEmployee] = ''Hour'') THEN cast(1 as bit) ELSE cast(0 as bit) END) = 1) THEN CAST(NULL AS float) ELSE [Extent1].[Salary] END AS [C5],
70
1 AS [C6]
71
FROM [dbo].[employeeTest] AS [Extent1]
72
INNER JOIN [dbo].[Perosn] AS [Extent2] ON [Extent1].[EmployeeID] = [Extent2].[PersonID]
73
WHERE [Extent1].[EmployeeID] = @p__linq__2 ) AS [Limit1]
74
LEFT OUTER JOIN (SELECT
75
[Order].[OrderID] AS [OrderID],
76
[Order].[OrderNo] AS [OrderNo],
77
[Order].[Quantity] AS [Quantity],
78
[Order].[Price] AS [Price],
79
[Order].[EmployeeID] AS [EmployeeID]
80
FROM [dbo].[Order] AS [Order]) AS [Extent3] ON [Limit1].[EmployeeID] = [Extent3].[EmployeeID]
81
) AS [Project2]
82
ORDER BY [Project2].[EmployeeID] ASC, [Project2].[PersonID] ASC, [Project2].[C8] ASC',N'@p__linq__2 uniqueidentifier',@p__linq__2='1174CD94-102B-4419-B55D-513497A240E1'
比较很容易发现,使用了Include("Order") sql就相应添加了LEFT OUTER JOIN Order 表。不过我没弄明白第3个查询没有立即执行 IQueryable<employeeTest> 明明已经已经指明了加载类型employeeTest.我的疑惑是因为我发现除了以上2种Eager Loading 的方式还有另外一种方式:
var query = from Order o in context.Order where o.OrderID == guid select o;在这个Linq查询中 我指明了类型,query就不再是延迟加载了,而是Egaer Loaidng.
最后再看看关于Include加载多极关联对象的示例代码:

Code
1 employeeTest employee = context.Perosn.OfType<employeeTest>().Include("Order").Include("Order.Product").Where(q => q.PersonID == guid).FirstOrDefault();
2 employee = context.Perosn.OfType<employeeTest>().Include("TypeOfRank").Include("Order").Include("Order.Product").Where(q => q.PersonID == guid).FirstOrDefault();
3 employee = context.Perosn.OfType<employeeTest>().Include("TypeOfRank").Include("Order").Include("Order.Product").First(q => q.PersonID == guid);
4
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 周边上新:园子的第一款马克杯温暖上架
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
· 使用C#创建一个MCP客户端