【问题标题】:Why casting IQueryable to .toList() include data to object DbContext?为什么将 IQueryable 转换为 .toList() 将数据包含到对象 DbContext?
【发布时间】:2016-06-10 04:56:04
【问题描述】:

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

这个正在工作:

    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 我知道在实体框架中我们正在使用扩展,但据我所知,扩展返回值不会将值分配给对象。

这是我崩溃的部分观点:

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

错误信息:

传入字典的模型项是类型 'BlogWeb.Models.Post',但是这个字典需要一个模型项 输入“System.Collections.Generic.List`1[BlogWeb.Models.Tag]”。

【问题讨论】:

  • 我不确定为什么第一个有效,但 IMO 最后一个是正确的方法。
  • 定义“工作”和“不工作”。
  • @JeffMercado 我在问题中包含了更多数据,另一方面,它只是没有将标签列表包含到对象中。它一直是空的
  • 您的代码看起来不对劲。以 dbContext 开头的行从未分配给变量,因此它们基本上什么都不做。强烈推荐 reshaper...
  • 问题不是关于“我应该如何编写代码”我已经知道的。问题是为什么在这个例子中我什至不需要变量。同样 resharper 对 mvc 也没用

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


【解决方案1】:

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

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

【讨论】:

  • 但是为什么第二个不起作用呢?我在最后铸造 ToList 并且仍然没有成功,您的回答包含将在最后添加查询,并且 ToList 作为回报应该足以执行最后的查询
  • 因为这条语句dbContext.Posts.Include(a =&gt; a.Tags); 永远不会被执行。
  • 哦!所以 ToList 就像 Execute 一样在这里!明白了,谢谢
  • 是的,重申上面的观点,在您的实际系统中使用第三个示例,但不是第一个或第二个。
【解决方案2】:

请记住,在使用集合ToListToArrayAsEnumerable(我认为这就是全部)时,所有这些都会强制 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 =&gt; 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 =&gt; 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&lt;t&gt; 上的结果。所有 EF 扩展命令都实现了某些东西,并且您打算使用该结果。 DbSets&lt;T&gt; 本身并不能跟踪对它们执行的操作(EF 实体跟踪除外,但这不是我要说的)。 DbSet 不是一个命令对象,所以你不应该这样使用它。

【讨论】:

    【解决方案3】:

    当您调用dbContext.Posts.Include(a =&gt; a.Tags); 时,您只是在创建一个查询。没有从数据库中带来任何元素。

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

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

    在您的第一个示例中,您不包括 Tags,但 EF 会跟踪先前加载的元素以避免数据库查询,并且加载的此元素包括 Tags。这就是结果包含 Tags 属性的原因。

    【讨论】:

      猜你喜欢
      • 2021-07-15
      • 1970-01-01
      • 2016-12-06
      • 2019-01-17
      • 2022-06-18
      • 1970-01-01
      • 2021-12-22
      • 2015-01-03
      • 1970-01-01
      相关资源
      最近更新 更多