2

总结:我有哪些选项可以让实体的子集合的 LINQ 查询使用“LINQ-to-Entities”而不是“LINQ-to-Objects”,换句话说,导致使用生成的查询提供程序SQL而不是执行正常的枚举?这是使用 EF5、DbContext 和 POCO 实体。

另一种看待它的方式是:如何在不更改所有 LINQ 查询的情况下让实体的子集合(ICollection<> 类型)像旧式 EF1 实体的 EntityCollection<> 属性一样工作?

这是我的情况:

我有一个 Entity Framework 5 项目,我正在切换到使用 DbContext 和 POCO 实体而不是 ObjectContext 和默认生成的实体。

在我的代码中,我的查询看起来像这样:

var thirdEntities = from secondEntity in firstEntity.SecondEntities
    select secondEntity.ThirdEntity;

这过去只导致单个数据库命中,因为 firstEntity.SecondEntities 是 EntityCollection 类型。这导致使用 LINQ to Entities,它将整个 LINQ 语句转换为 SQL。

但是,当我切换到使用 DbContext 和 POCO 时,上面的语句现在会导致大量数据库命中。这是导致问题的 POCO 类属性:

public class FirstEntity {
  public FirstEntity() {
    this.SecondEntities = new HashSet<SecondEntity>();
  }

  public virtual ICollection<SecondEntity> SecondEntities {get; set;}
}

这是因为 firstEntity.SecondEntities 现在是 ICollection 类型(HashSet 的实际类型)。由于延迟加载已打开,因此会命中数据库以填充 firstEntity.SecondEntity,然后将该结果枚举 X 次以填充 secondEntity.ThirdEntity 的每个实例。

我找到的一个解决方案是将第一个查询更改为:

var thirdEntities = from secondEntity in dbContext
    .Entry(firstEntity)
    .Collection(e => e.SecondEntities)
    .Query()
    select secondEntity.ThirdEntity;

但是,我不想这样做。我宁愿以某种方式更改 POCO,这样我就不必更改所有 LINQ 查询。

4

1 回答 1

3

根据我的理解和测试,我运行的 LINQ 查询EntityCollection<T>是 LINQ-to-Entities 是不正确的。事实上,它是 LINQ-to-Objects 并且仅在内存中运行,无需创建 SQL 和接触数据库(除了 EF >= 4.0 中的延迟加载)。

举个例子:

using (var ctx = new MyEntities())
{
    var firstEntity = ctx.FirstEntities.First(f => f.ID == 1);
    var thirdEntities = (from secondEntity in firstEntity.SecondEntities
                         select secondEntity.ThirdEntity).ToList();
}

这里的第一个查询当然是 LINQ-to-Entities 并firstEntity从数据库加载。第二个查询不会创建一个单独的 SQL 查询,ThirdEntity正如我使用 SQL 探查器检查过的那样。在 EF 1 中,第二个查询实际上什么都不做,因为firstEntity.SecondEntities在第一个查询中没有急切地加载(所以集合是空的)并且 EF 1 不支持延迟加载。在 EF 4 中,将触发延迟加载查询以加载完整集合,然后为要加载的集合中的每个(唯一)元素添加一个额外的延迟加载查询ThirdEntity(我也对此进行了测试) - 与您观察到的行为完全相同使用 POCO。

结果对我来说并不奇怪,因为EntityCollection<T>没有实现IQueryable<T>. 签名是:

public sealed class EntityCollection<TEntity> : RelatedEnd, ICollection<TEntity>,
    IEnumerable<TEntity>, IEnumerable, IListSource

您对此类集合执行的所有 LINQ 查询都将使用扩展方法,IEnumerable<T>这意味着:它是 LINQ-to-Objects。

但是,与上一个代码片段类似,您可以通过应用以下代码来启用 LINQ-to-Entities/显式CreateSourceQuery加载EntityCollection<T>

var thirdEntities =
    (from secondEntity in firstEntity.SecondEntities.CreateSourceQuery()
     select secondEntity.ThirdEntity).ToList();

CreateSourceQuery将返回一个ObjectQuery<T>表示从数据库加载完整导航集合所需的 SQL。这个查询确实可以被细化以执行更多的过滤或选择,它将作为数据库中的 LINQ-to-Entities 执行。

如果您确定您已经观察到EntityCollection<T>与 POCO 不同的查询行为,那么如果您可以提供一个简单的示例模型来重现这一点,将会有所帮助。目前我不知道这怎么会发生。

于 2012-09-28T13:36:35.927 回答