简体   繁体   English

实体框架/Linq to SQL:跳过并接受

[英]Entity Framework/Linq to SQL: Skip & Take

Just curious as to how Skip & Take are supposed to work.只是好奇 Skip & Take 应该如何工作。 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.我得到了我想在客户端看到的结果,但是当我连接 AnjLab SQL Profiler 并查看正在执行的 SQL 时,它看起来好像正在查询并将整个行集返回给客户。

Is it really returning all the rows then sorting and narrowing down stuff with LINQ on the client side?它真的返回所有行,然后在客户端使用 LINQ 对内容进行排序和缩小范围吗?

I've tried doing it with both Entity Framework and Linq to SQL;我已经尝试使用 Entity Framework 和 Linq to SQL 来做这件事; both appear to have the same behavior.两者似乎具有相同的行为。

Not sure it makes any difference, but I'm using C# in VWD 2010.不确定它有什么不同,但我在 VWD 2010 中使用 C#。

Any insight?任何见解?

public IEnumerable<Store> ListStores(Func<Store, string> 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):结果 SQL(注意:我不包括 Count 查询):

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<Store> ListStores(Func<Store, string> 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:结果 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.传入一个 lambda 表达式进行排序。 Is there any way to accomplish the same thing in the LINQ to SQL orderby syntax?有没有办法在 LINQ to SQL orderby 语法中完成同样的事情? 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.我尝试使用 qry.OrderBy(sort).Skip(skipRows).Take(pageSize),但这最终给了我与我的第一个代码块相同的结果。 Leads me to believe my issues are somehow tied to OrderBy.让我相信我的问题在某种程度上与 OrderBy 相关。

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

PROBLEM SOLVED问题解决

Had to wrap the incoming lambda function in Expression:必须将传入的 lambda 函数包装在 Expression 中:

Expression<Func<Store,string>> sort

The following works and accomplishes the simplicity I was looking for:以下工作并实现了我一直在寻找的简单性:

public IEnumerable<Store> ListStores(Expression<Func<Store, string>> sort, bool desc, int page, int pageSize, out int totalRecords)
{
    List<Store> stores = new List<Store>();
    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:为我修复它的主要事情是将 Func 排序参数更改为:

Expression<Func<Store, string>> sort

As long as you don't do it like queryable.ToList().Skip(5).Take(10) , it won't return the whole recordset.只要你不像queryable.ToList().Skip(5).Take(10) ,它就不会返回整个记录集。

Take

Doing only Take(10).ToList() , does a SELECT TOP 10 * FROM .只做Take(10).ToList() ,做一个SELECT TOP 10 * FROM

Skip跳过

Skip works a bit different because there is no 'LIMIT' function in TSQL. Skip 的工作方式有点不同,因为 TSQL 中没有“LIMIT”函数。 However it creates an SQL query that is based on the work described in this ScottGu blog post .但是,它创建了一个 SQL 查询,该查询基于此ScottGu 博客文章中描述的工作。

If you see the whole recordset returned, it probably is because you are doing a ToList() somewhere too early.如果您看到整个记录集返回,这可能是因为您在某处过早地执行了ToList()

Entity Framework 6 solution here...实体框架 6 解决方案在这里...

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

eg例如

using System.Data.Entity;
....

int skip = 5;
int take = 10;

myQuery.Skip(() => skip).Take(() => take);

I created simple extension:我创建了简单的扩展:

public static IEnumerable<T> SelectPage<T, T2>(this IEnumerable<T> list, Func<T, T2> sortFunc, bool isDescending, int index, int length)
{
    List<T> 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();
}

If you are using SQL Server as DB如果您使用 SQL Server 作为数据库

Then you can convert然后你可以转换

context.Users.OrderBy(u => u.Id)
.Skip(() => 10)
.Take(() => 5)
.ToList

=> =>

SELECT 
[Extent1].[Id] AS [Id], 
[Extent1].[UserName] AS [UserName]
FROM [dbo].[AspNetUsers] AS [Extent1]
ORDER BY [Extent1].[Id] ASC
OFFSET 10 ROWS FETCH NEXT 5 ROWS ONLY

refrence: https://anthonychu.ca/post/entity-framework-parameterize-skip-take-queries-sql/参考: https : //anthonychu.ca/post/entity-framework-parameterize-skip-take-queries-sql/

Try this:试试这个:

public IEnumerable<Store> ListStores(Func<Store, string> 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...事实上,当您返回 IEnumerable 时,最后一个 .ToList() 并不是真正必要的...

There will be 2 database calls, one for the count and one when the ToList() is executed.将有 2 次数据库调用,一次用于计数,一次用于执行 ToList()。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM