为什么将 IQueryable 转换为 .toList() 将数据包含到对象 DbContext?

Why casting IQueryable to .toList() include data to object DbContext?

我有一个使用实体框架的代码示例

这个有效:

    public ActionResult Index()
    {
        BlogContext dbContext = new BlogContext();
        dbContext.Posts.Include(a => a.Tags).ToList();
        return View(dbContext.Posts.ToList());
    }

而这个不是:

    public ActionResult Index()
    {
        BlogContext dbContext = new BlogContext();
        dbContext.Posts.Include(a => a.Tags);
        return View(dbContext.Posts.ToList());
    }

所以我不明白,为什么只转换到List就足以包含来自模型(List<Tags>)的标签,如果是这样我会理解,这也很好(我正在使用它!问题与代码格式无关!):

    public ActionResult Index()
    {
        BlogContext dbContext = new BlogContext();
        var model=dbContext.Posts.Include(a => a.Tags).ToList();
        return View(model);
    }

但这并不需要。所以我的问题是: 为什么将 IQueryable 转换为 toList 会将数据包含到对象 dbContext 我知道在实体框架中我们正在使用扩展,但据我所知扩展 return值未将一个分配给对象。

这是我崩溃的局部视图:

@using BlogWeb.Models
@model Post


    <h1>@Model.Title</h1>
    <p class="lead">
        by <a href="#">1234</a>
    </p>
    <hr>
    <p>@Model.Created</p>
    <p class="lead">
        @Model.Body
    </p>
    @Html.Partial("_Tags", Model.Tags) -<< this is what is throwing error
    <hr>

这是部分标签视图

@using BlogWeb.Models
@model List<Tag>
@foreach (Tag tag in Model)
{
    @Html.Label('#' + tag.TagName)
    @Html.Raw(" ")
}

错误信息:

The model item passed into the dictionary is of type 'BlogWeb.Models.Post', but this dictionary requires a model item of type 'System.Collections.Generic.List`1[BlogWeb.Models.Tag]'.

当您将这些 Linq 方法链接在一起时,您就是在幕后构建一个查询。添加 Include(...) 告诉它在执行之前加入其他数据。当您调用 ToList() 时,将使用 Include(...).

执行 Linq 查询

在第二个示例中,您没有执行查询,因此当您调用 dbContext.Posts.ToList() 时,它没有缓存任何内容,而是在没有 Include(...).[= 的情况下执行的不同查询15=]

请记住,在使用 Collections ToListToArrayAsEnumerable(我认为这是所有这些)时,所有这些都会强制 EF 对数据库执行并具体化对象回到记忆中。换句话说,如果您不遍历集合或调用其中之一,则什么也不会发生。

This one is working:

public ActionResult Index()
{
    BlogContext dbContext = new BlogContext();
    dbContext.Posts.Include(a => a.Tags).ToList();
    return View(dbContext.Posts.ToList());
}

EF 在内部跟踪实体的状态。因此,代码 dbContext.Posts.Include(a => a.Tags).ToList(); 行将检索所有 post 以及每个 post 的标签。当您随后调用 return View(dbContext.Posts.ToList()); 时,标签已经从数据库中检索到,除非您在那几分之一秒内添加了一个新的 post,否则不会有任何问题。

and this one is not:

public ActionResult Index()
{
    BlogContext dbContext = new BlogContext();
    dbContext.Posts.Include(a => a.Tags);
    return View(dbContext.Posts.ToList());
}

dbContext.Posts.Include(a => a.Tags); 告诉 EF 包含标签但不对其执行任何其他操作,您实际上只是丢弃了您正在处理的命令。然后调用 dbContext.Posts.ToList(),它只检索 post。所以现在你有帖子但没有包含标签。

public ActionResult Index()
{
    BlogContext dbContext = new BlogContext();
    var model = dbContext.Posts.Include(a => a.Tags).ToList();
    return View(model);
}

这是使用 EF 的方式,因为现在您不会丢弃 DbSet<t> 上的结果。所有 EF 扩展命令都会具体化某些内容,您应该使用该结果。 DbSets<T> 本身并不是为了跟踪对它们执行的操作(EF 实体跟踪除外,但这不是我所说的)。 DbSet 不是一个命令对象,所以你不应该这样使用它。

当您调用 dbContext.Posts.Include(a => a.Tags); 时,您只是在创建一个查询。没有元素来自数据库。

当您调用 dbContext.Posts.Include(a => a.Tags).ToList(); 时,您正在创建并执行查询。元素加载到内存中(包括Tags 属性)。

在您的第二个示例中,当您执行 return View(dbContext.Posts.ToList()); 时,内存中没有任何元素,并且当元素来自数据库时,您并没有告诉要包含 Tags

在您的第一个示例中,您没有包括 Tags,但 EF 会跟踪先前加载的元素以避免数据库查询,并且加载的元素包括 Tags。这就是为什么结果包括 Tags 属性.