虚拟关键字,包括扩展方法,延迟加载,急切加载 - 加载相关对象如何实际工作

Pus*_*ots 6 asp.net-mvc entity-framework lazy-loading eager-loading

在MVC中加载相关对象可能非常混乱.

如果您真的想知道在编写实体模型类和控制器时您正在做什么,那么您需要了解并学习很多术语.

我在很长一段时间内遇到的几个问题是:virtual关键字如何工作以及何时应该使用它?Include扩展方法如何工作以及何时使用它?

这就是我所说的;

virtual 关键词:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace LazyLoading.Models
{
    public class Brand
    {
        public int BrandId { get; set; }
        public string Name { get; set; }
        public int ManufacturerId { get; set; }
        public virtual Manufacturer Manufacturer { get; set; }
    }
}
Run Code Online (Sandbox Code Playgroud)

Include扩展方法:

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using LazyLoading.Models;

namespace LazyLoading.Controllers
{
    public class LazyLoadingStoreController : Controller
    {
        private UsersContext db = new UsersContext();

        //
        // GET: /LazyLoadingStore/

        public ActionResult Index()
        {
            var brands = db.Brands.Include(b => b.Manufacturer);
            return View(brands.ToList());
        }

        ... Other action methods ...
Run Code Online (Sandbox Code Playgroud)

请注意,Index()Action Studio会自动生成action方法.是的,Visual Studio自动添加了.Include(b => b.Manufacturer).那太好了.

Lad*_*nka 12

注意:写下这个答案花了太长时间,当另外两个人出现时,就把它丢弃了......

虚拟关键字与两个属性一起使用DbContext.Configuration:

  • ProxyCreationEnabled - 当EF创建对象时,允许EF crating动态代理
  • LazyLoadingEnabled - 允许动态代理在首次使用navigation属性时加载相关实体

延迟加载通过动态代理透明地实现.动态代理是从您的实体派生的类,由EF在运行时创建和编译.它会覆盖您的虚拟导航属性,并在相关实体已加载或未加载的情况下实施逻辑检查.如果不是,则触发上下文加载(向数据库发出新查询).延迟加载只能在实体所附加的上下文范围内执行 - 如果处置上下文,则无法使用它.

动态代理创建由首先提到的属性控制.将实体实例创建为代理后,您无法"删除"代理.此外,如果您创建没有代理的实体(例如通过调用构造函数),您不能在以后添加它(但您可以使用DbSet<T>.Create而不是构造函数来获取代理实例).

第二个属性可以在实体实例的实时时间内更改,因此您可以通过将实体更改为false来避免对数据库进行不必要的查询(有时非常有用).

Include代表急切的装载.Eager loading将相关实体与主实体一起加载,并作为主实体查询的一部分执行(它将SQL连接添加到查询中并构建一个大结果集).

热切加载的好处是通过一次往返数据库来预先获取所有数据.特别是如果你知道你将需要所有这些,它可以是一种方法.如果您使用太多包含以及一些限制(您不能向已加载的实体添加排序或过滤 - 它总是加载所有相关对象),那么急切加载的缺点是非常大的结果集.

延迟加载的好处是只在您真正需要时才加载数据.如果您真的不需要它们,那么如果您不了解它们会很有帮助.缺点是EF在某些情况下在您不期望它们时生成的其他查询(任何首次访问该属性都会触发延迟加载 - 即使Count在导航集合中也会触发加载所有数据以便能够在您的应用程序中进行计数从数据库中查询计数 - 这称为额外的延迟加载,并且EF本身不支持它.另一个大问题是N + 1问题.如果您加载多个品牌,并且您将通过循环浏览所有已加载的品牌而不使用预先加载来访问其制造商产品,则您将向数据库生成N + 1个查询(其中N是品牌数量) - 一个用于加载所有品牌,一个用于加载每个品牌的制造商.

还有另一个选项称为显式加载.它就像延迟加载但它没有透明地执行.您必须使用上下文类自己执行它:

context.Entry(brand).Reference(b => b.Manufacturer).Load();
Run Code Online (Sandbox Code Playgroud)

在这种情况下它不是很有用,但如果你在Manufacturer类上有Brands导航属性会很有用,因为你可以这样做:

var dataQuery = context.Entry(manufacturer).Collection(m => m.Brands).Query();
Run Code Online (Sandbox Code Playgroud)

现在你有了一个IQueryable<Brand>实例,你可以添加任何条件,排序甚至是额外的热切加载,并对数据库执行它.


Pus*_*ots 7

我创建了一个测试MVC4互联网应用程序.

这是我发现的:

首先,创建实体模型类 - 注意属性的virtual关键字Manufacturer:

public class Manufacturer
{
    public int ManufacturerId { get; set; }
    public string Name { get; set; }
    public ICollection<Brand> Brands { get; set; }
}

public class Brand
{
    public int BrandId { get; set; }
    public string Name { get; set; }
    public int ManufacturerId { get; set; }
    public virtual Manufacturer Manufacturer { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

接下来,创建您的控制器 - 我使用CRUD操作方法和视图创建(使用创建新控制器对话框自动生成).请注意IncludeVisual Studio自动生成的扩展方法,这要归功于Brand模型类中的关系.

public class LazyLoadingStoreController : Controller
{
    private UsersContext db = new UsersContext();

    //
    // GET: /LazyLoadingStore/

    public ActionResult Index()
    {
        var brands = db.Brands.Include(b => b.Manufacturer);
        return View(brands.ToList());
    }
Run Code Online (Sandbox Code Playgroud)

我们暂时删除该Include部分,以便我们的操作方法如下所示:

public ActionResult Index()
{
    var brands = db.Brands;
    return View(brands.ToList());
}
Run Code Online (Sandbox Code Playgroud)

这是Index在添加几个Brand对象后视图在页面检查器中的外观- 注意Visual Studio自动添加了下拉列表Manufacturer以及它如何自动搭建NameManufacturer- 甜蜜!: 在此输入图像描述 在此输入图像描述

Create操作方法:

//
// GET: /LazyLoadingStore/Create

public ActionResult Create()
{
    ViewBag.ManufacturerId = new SelectList(db.Manufacturers, "ManufacturerId", "Name");
    return View();
}
Run Code Online (Sandbox Code Playgroud)

真棒.一切都是为我们自动生成的!

现在,如果我们virtualManufacturer属性中删除关键字会发生什么?

public class Brand
{
    public int BrandId { get; set; }
    public string Name { get; set; }
    public int ManufacturerId { get; set; }
    public Manufacturer Manufacturer { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

这将是会发生什么 - 我们的制造商数据消失了:

在此输入图像描述

好的,有道理.如果我添加了Include扩展方法(virtual仍然从Manufacturer属性中删除)该怎么办?

public ActionResult Index()
{
    var brands = db.Brands.Include(b => b.Manufacturer);
    return View(brands.ToList());
}
Run Code Online (Sandbox Code Playgroud)

这是添加回Include扩展方法的结果- Manufacturer数据又回来了!:

在此输入图像描述

这就是所有这些东西的运作方式.

接下来将解释在两种情况下(延迟加载和Eager加载)后台生成的T-SQL类型.我将留给别人.:)

注意:Visual Studio会自动生成Include(b => b.Manufacturer)您是否添加virtual关键字.

注2:哦,是的.差点忘了.以下是一些良好的Microsoft资源的链接.

第二个链接讨论了另一个链接缺少的性能考虑因素,如果这是让你前进的话.