要专业系统地学习EF前往《你必须掌握的Entity Framework 6.x与Core 2.0》这本书的作者(汪鹏,Jeffcky)的博客:https://www.cnblogs.com/CreateMyself/
EF数据加载三种方式:延迟加载、饥饿加载、显示加载
每种加载方式都有其应用场景,应用不当会导致性能问题
我刚刚有点整懵了,目前算是有点明白,现在只是初步了解一下,把我刚刚搞的东西整理一下
延迟加载(Lazy Loading) 和淘宝的商品列表一样,下拉刷新,按需加载
饥饿加载 (Eager Loading) 加载父对象时同时加载子对象
显式加载 (Explicitly Loading)当我们禁用了延迟加载,仍然可以通过显式加载来延迟加载相关实体
EF中默认是开启延迟加载,那么我们怎么知道自己的查询是延迟加载呢?办法就是查询EF生成的SQL语句,他什么时候生成SQL语句,在什么时候执行查询。
要追踪SQL命令的执行,在SQL server数据库管理工具里面-->工具-->SQL Server Porfiler,这东西不错
我们也可以用一个更简单的方式,EF上下文有个Database.Log Action<string>类型的属性
如果是控制台项目,那么可以讲Console.WriteLine赋值给他,然后当有SQL执行,会为我们打印一些信息
db.Database.Log = Console.WriteLine;
我自定义了一个打印方法,可以说是没什么意义
db.Database.Log = MyConsole; public static void MyConsole(string str) { Console.WriteLine($"四海的跟踪:{str}"); }
然后我对数据集查询
using (EFDbContext db = new EFDbContext()) { db.Database.Log = MyConsole; var res = db.Orders.FirstOrDefault(); }
然后我们看看打印了什么东西
延迟加载(Lazy Loading)
现在我们就来看延迟加载
我有两个model,Order订单类和Product产品类,一对多,一个订单包含多产品
现在延迟加载是显式开启的
public class EFDbContext:DbContext { public EFDbContext() { // 延迟加载 true:开启,false:关闭 Configuration.LazyLoadingEnabled = true; //Configuration.AutoDetectChangesEnabled = false; } }
然后查询订单集合中的第一个订单
var res = db.Orders.FirstOrDefault();
EF生成并执行了一条SQL语句
SELECT TOP (1) [c].[Id] AS [Id], [c].[OrderNO] AS [OrderNO], [c].[Description] AS [Description], [c].[AddTime] AS [AddTime] FROM [dbo].[tb_Orders] AS [c]
可以看到EF仅对Oders表进行了查询,没有查询该订单包含的产品
好,我现在要使用订单中的产品数据了,于是,我查询该订单的第一条产品
var pro = res.Products.FirstOrDefault();
生成的SQL语句如下
SELECT [Extent1].[Id] AS [Id], [Extent1].[Name] AS [Name], [Extent1].[Price] AS [Price], [Extent1].[Unit] AS [Unit], [Extent1].[FK_Order_Id] AS [FK_Order_Id], [Extent1].[AddTime] AS [AddTime] FROM [dbo].[tb_Products] AS [Extent1] WHERE [Extent1].[FK_Order_Id] = @EntityKeyValue1
这就是延迟查询了,当我用的时候才执行查询
饥饿加载(Eager Loading)
你不使用延迟加载那就使用饥饿加载,显示加载也是属于延迟加载,我一次性拿到所有的数据。饥饿查询使用Include()方法
现在我们查询订单集合中的第一个订单,并且要求包含的产品集合都给我
db.Orders.Include("Products").FirstOrDefault();
生成的SQL语句如下,很多
:SELECT [Project2].[C1] AS [C1], [Project2].[Id] AS [Id], [Project2].[OrderNO] AS [OrderNO], [Project2].[Description] AS [Description], [Project2].[AddTime] AS [AddTime], [Project2].[C2] AS [C2], [Project2].[Id1] AS [Id1], [Project2].[Name] AS [Name], [Project2].[Price] AS [Price], [Project2].[Unit] AS [Unit], [Project2].[FK_Order_Id] AS [FK_Order_Id], [Project2].[AddTime1] AS [AddTime1] FROM ( SELECT [Limit1].[Id] AS [Id], [Limit1].[OrderNO] AS [OrderNO], [Limit1].[Description] AS [Description], [Limit1].[AddTime] AS [AddTime], [Limit1].[C1] AS [C1], [Extent2].[Id] AS [Id1], [Extent2].[Name] AS [Name], [Extent2].[Price] AS [Price], [Extent2].[Unit] AS [Unit], [Extent2].[FK_Order_Id] AS [FK_Order_Id], [Extent2].[AddTime] AS [AddTime1], CASE WHEN ([Extent2].[Id] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C2] FROM (SELECT TOP (1) [Extent1].[Id] AS [Id], [Extent1].[OrderNO] AS [OrderNO], [Extent1].[Description] AS [Description], [Extent1].[AddTime] AS [AddTime], 1 AS [C1] FROM [dbo].[tb_Orders] AS [Extent1] ) AS [Limit1] LEFT OUTER JOIN [dbo].[tb_Products] AS [Extent2] ON [Limit1].[Id] = [Extent2].[FK_Order_Id] ) AS [Project2] ORDER BY [Project2].[Id] ASC, [Project2].[C2] ASC
然后我们把这段SQL拿到数据执行看一下
显式加载(Explicitly Loading)
显式加载,如果关闭了延迟查询,还可以用显式加载来延迟加载实体。那为什么要弄这么个东西出来呢。可能就是因为延迟加载的问题,使用不当会造成性能问题,具体的性能问题,我现在还没有太多认识。
我们先来看看,关闭延迟加载后,然后用延迟加载的方式来查询会是什么结果。
设置Configuration.LazyLoadingEnabled = false;
查询订单集合的第一个订单,查询该订单的第一个产品
var res = db.Orders.FirstOrDefault(); var pro = res.Products.FirstOrDefault();
查询订单有SQL语句,当查询产品就报错了,因为产品是null
行,来显式加载
通过DbEntityEntry<T>.Reference("").Load();加载实体
通过DbEntityEntry<T>.Collection("").Load();加载集合
var res = db.Orders.FirstOrDefault(); db.Entry(res).Collection(x =>x.Products).Load();
第一句只会对Order查询,第二句查询订单中的产品,这样就行了