Entity Framework/Linq to SQL: Skip & Take

匿名 (未验证) 提交于 2019-12-03 07:50:05

问题:

Just curious as to how Skip & Take are supposed to work. I'm getting the results I want to see on the client side, but when I hook up the AnjLab SQL Profiler and look at the SQL that is being executed it looks as though it is querying for and returning the entire set of rows to the client.

Is it really returning all the rows then sorting and narrowing down stuff with LINQ on the client side?

I've tried doing it with both Entity Framework and Linq to SQL; both appear to have the same behavior.

Not sure it makes any difference, but I'm using C# in VWD 2010.

Any insight?

public IEnumerable ListStores(Func sort, bool desc, int page, int pageSize, out int totalRecords) {     var context = new TectonicEntities();     totalRecords = context.Stores.Count();     int skipRows = (page - 1) * pageSize;     if (desc)         return context.Stores.OrderByDescending(sort).Skip(skipRows).Take(pageSize).ToList();     return context.Stores.OrderBy(sort).Skip(skipRows).Take(pageSize).ToList(); } 

Resulting SQL (Note: I'm excluding the Count query):

SELECT  [Extent1].[ID] AS [ID],  [Extent1].[Name] AS [Name],  [Extent1].[LegalName] AS [LegalName],  [Extent1].[YearEstablished] AS [YearEstablished],  [Extent1].[DiskPath] AS [DiskPath],  [Extent1].[URL] AS [URL],  [Extent1].[SecureURL] AS [SecureURL],  [Extent1].[UseSSL] AS [UseSSL] FROM [dbo].[tec_Stores] AS [Extent1] 

After some further research, I found that the following works the way I would expect it to:

public IEnumerable ListStores(Func sort, bool desc, int page, int pageSize, out int totalRecords) {     var context = new TectonicEntities();     totalRecords = context.Stores.Count();     int skipRows = (page - 1) * pageSize;                var qry = from s in context.Stores orderby s.Name ascending select s;     return qry.Skip(skipRows).Take(pageSize);            } 

Resulting SQL:

SELECT TOP (3)  [Extent1].[ID] AS [ID],  [Extent1].[Name] AS [Name],  [Extent1].[LegalName] AS [LegalName],  [Extent1].[YearEstablished] AS [YearEstablished],  [Extent1].[DiskPath] AS [DiskPath],  [Extent1].[URL] AS [URL],  [Extent1].[SecureURL] AS [SecureURL],  [Extent1].[UseSSL] AS [UseSSL] FROM ( SELECT [Extent1].[ID] AS [ID], [Extent1].[Name] AS [Name], [Extent1].[LegalName] AS [LegalName], [Extent1].[YearEstablished] AS [YearEstablished], [Extent1].[DiskPath] AS [DiskPath], [Extent1].[URL] AS [URL], [Extent1].[SecureURL] AS [SecureURL], [Extent1].[UseSSL] AS [UseSSL], row_number() OVER (ORDER BY [Extent1].[Name] ASC) AS [row_number]     FROM [dbo].[tec_Stores] AS [Extent1] )  AS [Extent1] WHERE [Extent1].[row_number] > 3 ORDER BY [Extent1].[Name] ASC 

I really like the way the first option works; Passing in a lambda expression for sort. Is there any way to accomplish the same thing in the LINQ to SQL orderby syntax? I tried using qry.OrderBy(sort).Skip(skipRows).Take(pageSize), but that ended up giving me the same results as my first block of code. Leads me to believe my issues are somehow tied to OrderBy.

====================================

PROBLEM SOLVED

Had to wrap the incoming lambda function in Expression:

Expression> sort 

回答1:

The following works and accomplishes the simplicity I was looking for:

public IEnumerable ListStores(Expression> sort, bool desc, int page, int pageSize, out int totalRecords) {     List stores = new List();     using (var context = new TectonicEntities())     {         totalRecords = context.Stores.Count();         int skipRows = (page - 1) * pageSize;         if (desc)             stores = context.Stores.OrderByDescending(sort).Skip(skipRows).Take(pageSize).ToList();         else             stores = context.Stores.OrderBy(sort).Skip(skipRows).Take(pageSize).ToList();     }     return stores; } 

The main thing that fixed it for me was changing the Func sort parameter to:

Expression> sort 


回答2:

As long as you don't do it like queryable.ToList().Skip(5).Take(10), it won't return the whole recordset.

Take

Doing only Take(10).ToList(), does a SELECT TOP 10 * FROM.

Skip

Skip works a bit different because there is no 'LIMIT' function in TSQL. However it creates an SQL query that is based on the work described in this ScottGu blog post.

If you see the whole recordset returned, it probably is because you are doing a ToList() somewhere too early.



回答3:

Entity Framework 6 solution here...

http://anthonychu.ca/post/entity-framework-parameterize-skip-take-queries-sql/

e.g.

using System.Data.Entity; ....  int skip = 5; int take = 10;  myQuery.Skip(() => skip).Take(() => take); 


回答4:

Try this:

public IEnumerable ListStores(Func sort, bool desc, int page, int pageSize, out int totalRecords) {     var context = new TectonicEntities();     var results = context.Stores;      totalRecords = results.Count();     int skipRows = (page - 1) * pageSize;      if (desc)         results = results.OrderByDescending(sort);      return results.Skip(skipRows).Take(pageSize).ToList(); } 

in truth, that last .ToList() isn't really necessary as you are returning IEnumerable...

There will be 2 database calls, one for the count and one when the ToList() is executed.



回答5:

I created simple extension:

public static IEnumerable SelectPage(this IEnumerable list, Func sortFunc, bool isDescending, int index, int length) {     List result = null;     if (isDescending)         result = list.OrderByDescending(sortFunc).Skip(index).Take(length).ToList();     else         result = list.OrderBy(sortFunc).Skip(index).Take(length).ToList();     return result; } 

Simple use:

using (var context = new TransportContext()) {     var drivers = (from x in context.Drivers where x.TransportId == trasnportId select x).SelectPage(x => x.Id, false, index, length).ToList(); } 


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!