Skip and Take: эффективный подход к ОГРАНИЧЕННОЙ ОГРАНИЧЕННОСТИ в EF 4.1? - PullRequest
15 голосов
/ 08 декабря 2011

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

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 Пропустить и принятьне были переведены на SQL, в результате чего ВСЕ посты из блога загружались из базы данных, а не только те 5, которые нам нужны.

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

Ответы [ 3 ]

20 голосов
/ 08 декабря 2011

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

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

Редактировать Вы действительно должны использовать OrderBy перед Skip (в противном случае 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's posts
    .OrderBy(p => p.PublishedDate).Skip(10).Take(5).ToList(); // Filter to the correct page of posts
2 голосов
/ 08 декабря 2011

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

Blog blog = db.Blogs.First();
blog.posts = Posts.Where(r=>r.blogID=blog.id).Skip(10).Take(5).ToList();
2 голосов
/ 08 декабря 2011

Как он предлагает в своем посте, вы могли бы вместо этого использовать 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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...