ICollection 上的 AsQueryable() 真的会延迟执行吗?

发布于 2025-01-03 22:25:16 字数 1177 浏览 1 评论 0原文

我正在使用 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);

,我明白,当该行执行时,该人的所有博客都会加载到内存中,然后从内存中完成排序和选择。这对于拥有大量博客的 Person 的记录来说并不理想。我想将博客子项设置为 IQueryable,以便在拉入内存之前在 SQL 数据库中完成排序和选择。

我知道我可以在我的上下文中将博客声明为 IQueryable,以便我可以直接查询,

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

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

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

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

这对我来说看起来像是一个魔法,而且似乎好得令人难以置信。所以我的问题。这个 AsQueryable 是否真的使 ICollection 实际上成为 IQueryable 并在 SQL Server 中进行所有查询过程(延迟加载),或者它只是像以前一样将博客加载到内存中的转换,但将接口从 ICollection 更改为 IQueryable ?

I am using Entity Framework CodeFirst where I have used Parent Child relations using ICollection as

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

Ok, so far everything is working ok, but my concern is, whenever I want to get the Blogs of a person, I get it as

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

Now, I understand that, when the line is executed, all Blogs for that person is loaded into the memory and then sorting and selecting is done from memory. That is not ideal for a record of Person who has large number of blogs. I want to make the Blog Child as IQueryable so that the Sorting and Selecting is done in SQL database before pulling to Memory.

I know I could declare the Blogs as IQueryable in my context so that I could directly query as

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

but that is not feasible for me due to design choice, I want to avoid any circular reference as much as possible due to serialization problem. So, I did not make any reference of the parent entity in my child.

I found that, i can call AsQueryable() method on the blogs as

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

That looks like a magic for me and seems too good to be true. So my question. Does this AsQueryable really make the ICollection as IQueryable in reality and makes all Query process in SQL Server (Lazy loading) OR it is just a casting where Blogs are loaded into memory as like before, but change the interface from ICollection to IQueryable ?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

后来的我们 2025-01-10 22:25:16

因此,实际上,将导航属性编写为 IQueryable 不可能

您可以做的就是向 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,以及实际从数据库请求什么。

So actually it appears that writing your navigation property as IQueryable<T> is not possible.

What you could do is adding a navigation property to 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; }
}

From that, you can query as follows so it won't load everything into memory:

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)

I suggest you to try LINQPad to see how LINQ is translated into SQL, and what is actually requested from the DB.

三五鸿雁 2025-01-10 22:25:16

Ladislav 的回答中描述了更好的方法。在你的情况下:

var theBlogs = _context.Entry(thePerson)
                       .Collection(x => x.Blogs)
                       .Query()
                       .OrderBy(x => x.id)
                       .Take(5);

A better approach is described in Ladislav's answer. In your case:

var theBlogs = _context.Entry(thePerson)
                       .Collection(x => x.Blogs)
                       .Query()
                       .OrderBy(x => x.id)
                       .Take(5);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文