在ICollection上使用AsQueryable()是否真的实现了延迟执行?

10

我正在使用Entity Framework CodeFirst,其中我使用了ICollection作为父子关系的集合类型。

public class Person
{
   public string UserName { get;set}
   public ICollection<Blog> Blogs { get; set;}
}

public class Blog
{
   public int id { get; set; }
   public string Subject { get; set; }
   public string Body { get; set; }
}

好的,到目前为止一切都运行良好,但我的担忧是,每当我想要获取一个人的博客时,我得到的结果是

var thePerson = _context.Persons.Where(x => x.UserName = 'xxx').SingleOrDefault();
var theBlogs = thePerson.Blogs.OrderBy(id).Take(5);

现在,我理解当该行代码被执行时,该人的所有博客都会被加载到内存中,然后从内存中进行排序和选择。对于具有大量博客的人来说,这并不理想。我希望将Blog Child声明为IQueryable,以便在将其拉入内存之前,在SQL数据库中进行排序和选择。

我知道我可以在我的上下文中将Blogs声明为IQueryable,这样我就可以直接查询:

var theBlogs = _context.Blogs.Where(.....)

但由于设计选择,这对我来说不可行,我希望尽可能避免循环引用,以避免序列化问题。因此,在我的子级中没有对父实体做任何引用。

我发现,我可以在博客上调用AsQueryable()方法:

var theBlogs = thePerson.Blogs.AsQueryable().OrderBy(id).Take(5);

对我来说,这看起来像是一种魔法,似乎太好了。所以我的问题是,AsQueryable是否真正将ICollection转换为IQueryable,并使所有查询过程在SQL Server中处理(延迟加载),还是只是进行强制类型转换,但Blogs仍然像以前一样被加载到内存中,但接口从ICollection更改为IQueryable?


谢谢 - Enigmativity,那么我的怀疑是正确的。这很可悲。 - Emran Hussain
2个回答

7

实际上,似乎不可能将导航属性写成 IQueryable<T>参考此处

你可以添加一个导航属性到 Blog

public class Blog
{
   public int id { get; set; }
   public string Subject { get; set; }
   public string Body { get; set; }
   public virtual Person Owner { get; set; }
}

从这里开始,您可以查询以下内容,以便它不会将所有内容加载到内存中:

var thePerson = _context.Persons.Where(x => x.UserName = 'xxx').SingleOrDefault();
var results = _context.Blogs.Where(z => z.Person.Name = thePerson.Name).OrderBy(id).Take(5)

我建议您尝试使用LINQPad工具,以了解LINQ如何转换成SQL,以及实际从数据库中请求的内容。


又有一个问题,如果我的上一个问题的答案是YES,那么我就不需要再为ICollection使用AsQueryable()了,对吗? - Emran Hussain
1
+1 推荐一款可以使 LINQ 查询开发更加轻松的工具。 - Roy Goode
@EmranH. 请查看更新的答案。根据您的评论,我意识到我可能有点误读了您的问题... 对于LINQPad,我真的很推荐它,它非常有用,并返回将针对您的数据库运行的SQL语句。当然,您可以对数据库进行分析(例如使用SQL Server Profiler),但它会返回相同的结果并且需要编译您的代码(而LINQPad则允许即时编写一些代码片段)。 - ken2k
@EmranH。我认为(1)不是问题。当使用模型优先方法时,EF默认添加两个导航属性,实际上会添加循环引用。如果EF以这种方式执行,则应该没问题。对于(2),您可以注释导航属性,使其被序列化器忽略([XmlIgnoreAttribute][SoapIgnoreAttribute]...)。 - ken2k
嗨,@Ken2K。谢谢你的见解。我将此问题标记为已回答。-敬礼。 - Emran Hussain
显示剩余3条评论

3
更好的方法在Ladislav的回答中有所描述。对于您的情况:
var theBlogs = _context.Entry(thePerson)
                       .Collection(x => x.Blogs)
                       .Query()
                       .OrderBy(x => x.id)
                       .Take(5);

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