EF6学习笔记九:初识延迟加载、饥饿加载、显式加载

要专业系统地学习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查询,第二句查询订单中的产品,这样就行了

原文地址:https://www.cnblogs.com/anyihen/p/12818871.html