延迟加载与惰性加载


  延迟加载与惰性加载(也叫贪婪加载)都是为了减少不必要的性能损耗。


1、延迟加载

  延迟加载即返回实体时,其他关联实体并不返回,在使用其关联实体时,才重新打开数据库连接,查询后返回。 与 Entity Framework 不同的是,在使用延迟加载返回实体时,EntityContext 并不需要处于开启状态。

[TestMethod]
public void TestLazyLoad()
{
    OrderDetails detail;
    using (var db = new DbContext())
    {
        detail = db.OrderDetails.FirstOrDefault();
    }

    Assert.AreEqual("VINET", detail.Orders.CustomerID);
}

  同样也实用于子实体集的加载。

[TestMethod]
public void TestLazyLoadDetails()
{
    Orders order;
    using (var db = new DbContext())
    {
        order = db.Orders.FirstOrDefault();
    }

    Assert.AreEqual(3, order.OrderDetailses.Count);
}

  应注意延迟加载的性能影响,在处理集合的循环中,如果使用关联实体,将产生严重的性能影响,相当于循环里对每一个实体进行了一次查询。

[TestMethod]
public void TestLazyLoadLoop()
{
    using (var db = new DbContext())
    {
        //将生产 1 + 5 次查询
        foreach (var detail in db.OrderDetails.Take(5))
        {
            Assert.IsFalse(string.IsNullOrEmpty(detail.Orders.CustomerID));
        }
    }
}

  为了避免这样的情况,我们建议你使用惰性加载。


2、惰性加载

  正在上节所说一样,在循环处理集合时,如果使用了关联实体,那么将对性能产生严重影响。惰性加载是在查询时将关联实体一并返回,这样循环时不会产生延迟加载。 使用 Include 方法,指定需要加载的关联实体,你可以使用多次指定,最终结果都能够将关联实体一起返回。

[TestMethod]
public void TestInclude()
{
    using (var db = new DbContext())
    {
        var details = db.OrderDetails
                    .Include(s => s.Products)
                    .Include(s => s.Orders);

        foreach (var detail in details)
        {
            Console.WriteLine(detail.Products.ProductName);
            Console.WriteLine(detail.Orders.OrderDate);
        }
    }
}

  Include 还可以使用多层级联,跟踪 SQL 你会发现只产生了一次查询。

[TestMethod]
public void TestIncludeCascade()
{
    using (var db = new DbContext())
    {
        var details = db.OrderDetails
                    .Include(s => s.Orders.Customers)
                    .Take(3);

        foreach (var detail in details)
        {
            Console.WriteLine(detail.Orders.Customers.Address);
        }
    }
}

  有时候在返回单个实体时,会使用到延迟加载技术返回关联实体,为了提高效率,也可以使用 Include 再使用 FirstOrDefault 返回实体。

[TestMethod]
public void TestIncludeGet()
{
    using (var db = new DbContext())
    {
        var detail = db.OrderDetails
                    .Include(s => s.Products)
                    .Include(s => s.Orders)
                    .FirstOrDefault(s => s.OrderID == 1 && s.ProductID == 1);

        Assert.AreEqual(detail.Orders.CustomerID, "VINET");
    }
}

  Include 也可以用于加载子实体集,以及子实体集中关联的其他实体属性,如下所示:

[TestMethod]
public void TestIncludeDetails()
{
    using (var db = new DbContext())
    {
        var orders = db.Orders
                    .Include(s => s.OrderDetails)
                    .ThenInclude(s => s.Products)
                    .Include(s => s.Customers)
                    .ToList();

        Console.WriteLine(orders[0].OrderDetails.Count);
    }
}