Подтвердить что ты не робот

Skip and Take: эффективный подход к ограничению OFFSET в EF 4.1?

Следующий код:

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

Будет создан следующий 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 */

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

NB. Skip и Take не были переведены на SQL, в результате чего все сообщения из блога загружаются из базы данных, а не только 5, которые нам требуются.

Это кажется опасным, ужасно неэффективным. Невероятно, что дает?

4b9b3361

Ответ 1

Причина, по которой это происходит, - вызов First, из-за чего объект Blog будет реализован. Для любого последующего обхода требуется больше запросов.

Попробуйте db.Blogs.Take(1).SelectMany(b => b.Posts).Skip(10).Take(5).ToList(); вместо этого сделать это в одном запросе. Вероятно, вы захотите добавить некоторый порядок упорядочения блогов до .Take(1), чтобы обеспечить детерминированный результат.

Edit Фактически вам нужно использовать OrderBy перед пропуском (иначе LINQ to Entities будет генерировать исключение), что делает что-то вроде:

db.Blogs.OrderBy(b => b.Id).Take(1) // Filter to a single blog (while remaining IQueryable)
    .SelectMany(b => b.Posts) // Select the blog posts
    .OrderBy(p => p.PublishedDate).Skip(10).Take(5).ToList(); // Filter to the correct page of posts

Ответ 2

Как он предлагает в своем посте, вы можете использовать EQL для выполнения этого запроса. Что-то вроде:

// Create a query that takes two parameters.
string queryString =
    @"SELECT VALUE product FROM 
      AdventureWorksEntities.Products AS product 
      order by product.ListPrice SKIP @skip LIMIT @limit";

ObjectQuery<Product> productQuery =
    new ObjectQuery<Product>(queryString, context);

// Add parameters to the collection.
productQuery.Parameters.Add(new ObjectParameter("skip", 3));
productQuery.Parameters.Add(new ObjectParameter("limit", 5));

// Iterate through the collection of Contact items.
foreach (Product result in productQuery)
    Console.WriteLine("ID: {0}; Name: {1}",
    result.ProductID, result.Name);

Код, взятый отсюда: http://msdn.microsoft.com/en-us/library/bb738702.aspx

Ответ 3

Вы можете попытаться получить свой первый блог и использовать идентификатор блога для фильтрации сообщений, подобных этому:

Blog blog = db.Blogs.First();
blog.posts = Posts.Where(r=>r.blogID=blog.id).Skip(10).Take(5).ToList();