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

时间:2016-02-26 20:09:24

标签: c# asp.net entity-framework asp.net-mvc-4 extension-methods

我有使用entityframework

的代码示例

这个正在运作:

    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);
    }

但它不需要。所以我的问题是: 为什么将toIriable转换为toList包含数据到对象dbContext我知道在entityframework中我们正在使用扩展,但据我记得扩展返回值而不是将一个赋值给对象。

这是我的部分视图崩溃:

@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(" ")
}

错误讯息:

  

传递到字典中的模型项是类型的   &#39; BlogWeb.Models.Post&#39;,但这个字典需要一个模型项   输入&#39; System.Collections.Generic.List`1 [BlogWeb.Models.Tag]&#39;。

3 个答案:

答案 0 :(得分:2)

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

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

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

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

答案 1 :(得分:1)

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

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

答案 2 :(得分:1)

请记住,在使用集合ToList时,ToArrayAsEnumerable(我认为所有这些都是)都强制EF对数据库执行并将对象具体化回记忆。换句话说,如果你不迭代收集或调用其中一个,那么没有任何事情发生。

  

这个正在运作:

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();的行将检索所有帖子以及每个帖子的标签。当您再调用return View(dbContext.Posts.ToList());时,已经从数据库中检索了标签,除非您在一小段时间内添加了新帖子,否则不会有任何问题。

  

而这个不是:

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(),只检索帖子。所以现在你有帖子但没有包含标签。

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不是命令对象,所以你不要这样使用它。