Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Skip and Take: An efficient approach to OFFSET LIMIT in EF 4.1?

The following code:

using (var db = new Entities())
{
    db.Blogs.First().Posts.Skip(10).Take(5).ToList();
}

Will generate the following SQL:

-- statement #1
SELECT TOP ( 1 ) [c].[Id] AS [Id],
             [c].[Title]          AS [Title],
             [c].[Subtitle]       AS [Subtitle],
             [c].[AllowsComments] AS [AllowsComments],
             [c].[CreatedAt]      AS [CreatedAt]
FROM [dbo].[Blogs] AS [c]

-- statement #2
SELECT [Extent1].[Id] AS [Id],
   [Extent1].[Title]    AS [Title],
   [Extent1].[Text]     AS [Text],
   [Extent1].[PostedAt] AS [PostedAt],
   [Extent1].[BlogId]   AS [BlogId],
   [Extent1].[UserId]   AS [UserId]
FROM [dbo].[Posts] AS [Extent1]
WHERE [Extent1].[BlogId] = 1 /* @EntityKeyValue1 */

(from http://ayende.com/blog/4351/nhibernate-vs-entity-framework-4-0)

NB The Skip and Take have not been translated to SQL resulting in ALL posts from the blog being loaded from the database, instead of just the 5 we require.

This seems dangerously, horribly inefficient. Unbelievably so, what gives?

like image 911
Tom Avatar asked Dec 08 '11 01:12

Tom


People also ask

What is limit and offset in query?

The limit option allows you to limit the number of rows returned from a query, while offset allows you to omit a specified number of rows before the beginning of the result set. Using both limit and offset skips both rows as well as limit the rows returned.

Is Dapper faster than Entity Framework Core?

Dapper is literally much faster than Entity Framework Core considering the fact that there are no bells and whistles in Dapper. It is a straight forward Micro ORM that has minimal features as well. It is always up to the developer to choose between these 2 Awesome Data Access Technologies.


1 Answers

The reason it's happening is the call to First, which is causing the Blog object to be materialized. Any further traversal requires more queries.

Try db.Blogs.Take(1).SelectMany(b => b.Posts).Skip(10).Take(5).ToList(); instead to do it in one query. You probably want to add some sort of ordering of blogs before the .Take(1), to ensure a deterministic result.

Edit You actually have to use OrderBy before Skip (otherwise LINQ to Entities will throw an exception), which makes it something like:

db.Blogs.OrderBy(b => b.Id).Take(1) // Filter to a single blog (while remaining IQueryable)
    .SelectMany(b => b.Posts) // Select the blog's posts
    .OrderBy(p => p.PublishedDate).Skip(10).Take(5).ToList(); // Filter to the correct page of posts
like image 121
Bennor McCarthy Avatar answered Sep 26 '22 22:09

Bennor McCarthy