Entity Framework Code First IQueryable

8

我正在使用Entity Framework Code First,遇到了一个小问题。我定义了一个名为“Person”的类:

public class Person
{
    public Guid Id { get; set; }
    public virtual ICollection<History> History { get; set; }
}

而一个类“History”定义如下:

public class History
{
    public Guid Id { get; set; }
    public virtual Person Owner { get; set; }
    public DateTime OnDate { get; set; }
}

然而,当我调用以下代码时:
IEnumerable<History> results = person.History
                               .OrderBy(h => h.OnDate)
                               .Take(50)
                               .ToArray();

它似乎会提取这个人的所有历史记录,然后在内存中进行排序等操作。有什么建议我可能忽略了些什么吗?

提前感谢!


你期望看到什么行为?它是否未能将结果限制为50条记录? - Dan J
3
我认为期望的行为是将其在数据库中排序。 - Ben Robinson
1
我原本希望它将订单发送到服务器并进行限制,然后由SQL服务器进行排序和限制。但实际上,它似乎将所有历史记录都加载到内存中,包括该联系人的所有30,000多个元素,然后在内存中进行排序和限制。 - Terry
你为什么确定它正在内存中加载?你看到生成的 SQL 了吗? - Diego
2个回答

5

因为你正在查询由EF提供的IEnumerable(即:LINQ to Objects),而不是IQueryable(即:LINQ to Entities)。

相反,你应该使用

IEnumerable<History> results = context.History.Where(h => h.Person.Id = "sfssd").OrderBy(h => h.OnDate).Take(50)

这是EF Code First - 假定Person已从数据库中检索出来,因此它的导航属性History与上下文连接。 - BrokenGlass
@BrokenGlass 是的,它已经连接了。但它的类型是 ICollection<History> 而不是 IQueryable<History>。因此,当您访问 person.History 时,它将加载所有历史对象。 - Eranga
啊,你当然是对的,因为懒加载会启动并加载完整的集合。明白了,给你点赞。 - BrokenGlass
太糟糕了。我想这都有道理,只是希望能有一种以这种方式完成的方法。这给EF 5提供了一个想法...IQueryableCollection。 :-) - Terry
你可以编写一个 [NotMapped] 属性,在其内部执行 IQueryable 操作。 - BrainSlugs83

0

这个问题和被接受的答案都有点老了。像原始问题所指出的那样,这样的代码会从数据库中加载该人的整个历史记录 - 不好!

var results = person
    .History
    .OrderBy(h => h.OnDate)
    .Take(50)
    .ToArray();

使用 EF 6,有一个简单的解决方案。不需要重新排列查询,您可以通过使用 DbContext.Entry 方法DbEntryEntity.Collection 方法DbCollectionEntry.Query 方法 来使其按照 IQueryable 的方式工作。

var results = dbContext
    .Entry(person)
    .Collection(p => p.History)
    .Query()
    .OrderBy(h => h.OnDate)
    .Take(50)
    .ToArray();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接