2011-12-08 20 views
13

Il seguente codice:Salta e scatta: un approccio efficiente a OFFSET LIMIT in EF 4.1?

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

genererà il seguente 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 */ 

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

NB Lo Skip e Take non sono stati tradotti a SQL con conseguente tutti i messaggi dal blog che viene caricato dal database, invece del solo 5 richiesto.

Questo sembra pericolosamente, orribilmente inefficiente. Incredibilmente così, cosa dà?

risposta

19

La ragione sta accadendo è la chiamata alla First, che sta causando l'oggetto Blog da materializzato. Ogni ulteriore attraversamento richiede più query.

Provare db.Blogs.Take(1).SelectMany(b => b.Posts).Skip(10).Take(5).ToList(); invece di farlo in una query. Probabilmente vuoi aggiungere una sorta di ordinamento di blog prima dello .Take(1), per garantire un risultato deterministico.

Modifica In realtà usare OrderBy prima di Skip (altrimenti LINQ to Entities sarà un'eccezione), che lo rende qualcosa di simile:

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

Come suggerisce nel suo post, è possibile utilizzare EQL per eseguire questa query. Qualcosa di simile:

// 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); 

codice preso da qui: http://msdn.microsoft.com/en-us/library/bb738702.aspx

+0

Grazie per la risposta, che è utile, ma cercavo una soluzione priva di EQL. Avrei dovuto dirlo nella domanda. Grazie comunque. – Tom

1

Si può cercare di ottenere il vostro primo blog e l'uso l'id del blog per filtrare post del genere:

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

Grazie mille. – Tom

Problemi correlati