【问题标题】:How do I add ROW_NUMBER to a LINQ query or Entity?如何将 ROW_NUMBER 添加到 LINQ 查询或实体?
【发布时间】:2010-11-12 23:54:11
【问题描述】:

我被这个简单的数据问题难住了。

我正在使用实体框架并拥有一个产品数据库。我的结果页面返回这些产品的分页列表。现在我的结果是按每个产品的销售数量排序的,所以我的代码如下所示:

return Products.OrderByDescending(u => u.Sales.Count());

这将返回我的实体的 IQueryable 数据集,按销售额排序。

我希望我的结果页面显示每个产品的排名(在数据集中)。我的结果应该是这样的:

Page #1
1. Bananas
2. Apples
3. Coffee

Page #2
4. Cookies
5. Ice Cream
6. Lettuce

我希望我只想使用 SQL ROW_NUMBER 变量在我的结果中添加一列...但我不知道如何将此列添加到我的结果数据表中。

我的结果页面确实包含一个 foreach 循环,但由于我使用的是分页集,我猜测使用该数字来伪造排名数字并不是最好的方法。

所以我的问题是,在这种情况下,如何在查询结果中添加 ROW_NUMBER 列?

【问题讨论】:

  • 听起来这可能是表示层的问题,可以通过计算来解决。 var rank = (pageIndex * pageSize) + rowIndex + 1;

标签: asp.net linq entity-framework


【解决方案1】:

使用indexed overload of Select

var start = page * rowsPerPage;
Products.OrderByDescending(u => u.Sales.Count())
    .Skip(start)
    .Take(rowsPerPage)
    .AsEnumerable()
    .Select((u, index) => new { Product = u, Index = index + start });

【讨论】:

  • 哈哈。我是手动实现的,但确实有效。
  • 您不必使用匿名类型。创建一个非匿名类型并在需要时使用它。这只是一个例子。
  • 当我尝试这个时,我得到 NotSupportedException: LINQ to Entities does not identify the method 'System.Linq.IQueryable`1 [...] method, and this method cannot be translate into a store expression .这向我表明 EF 提供程序不支持此索引重载。我弄错了吗?
  • 太糟糕了,Linq to Entities 提供程序不使用 sql "ROW_NUMBER()" internall 以防止您不得不使用 .AsEnuerable() 以及 Select 语句的索引重载,这看起来像在某些情况下,可能会导致从数据存储中检索到不必要的大结果集。
  • 在明确要求 sql 函数 (ROW_NUMBER) 时建议使用 AsEnumerable() 是完全没有答案的。
【解决方案2】:

EF 4.5 中实际使用 OrderBy 然后 Skip + Take 生成 ROW_NUMBER(可以使用 SQL Profiler 检查)。

我正在寻找一种方法来做你所要求的同样的事情,我能够通过简化 Craig 的回答来获得我需要的东西:

var start = page * rowsPerPage;
Products.OrderByDescending(u => u.Sales.Count())
    .Skip(start)
    .Take(rowsPerPage)
    .ToList();

顺便说一句,生成的 SQL 使用 ROW_NUMBER > start 和 TOP rowsPerPage。

【讨论】:

  • 好吧,查询本身会生成一个ROW_NUMBER(),但只是作为ORDER BY 的中间步骤。这不允许我选择它。
【解决方案3】:

这是一个冗长的答案。首先创建一个类来容纳数字/项目对,如下所示:

public class NumberedItem<T>
{
    public readonly int Number;
    public readonly T Item;

    public NumberedItem(int number, T item)
    {
        Item = item;
        Number = number;
    }
}

接下来是围绕一页项目(编号与否)的抽象:

class PageOf<T> : IEnumerable<T>
{
    private readonly int startsAt;
    private IEnumerable<T> items;

    public PageOf(int startsAt, IEnumerable<T> items)
    {
        this.startsAt = startsAt;
        this.items = items;
    }

    public IEnumerable<NumberedItem<T>> NumberedItems
    {
        get
        {
            int index = 0;
            foreach (var item in items)
                yield return new NumberedItem<T>(startsAt + index++, item);
            yield break;
        }
    }

    public IEnumerator<T> GetEnumerator()
    {
        foreach (var item in items)
            yield return item;
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
}

一旦你有了它,你就可以使用这个“分页”一个特定的可查询集合:

class PaginatedQueryable<T>
{
    private readonly int PageSize;
    private readonly IQueryable<T> Source;

    public PaginatedQueryable(int PageSize, IQueryable<T> Source)
    {
        this.PageSize = PageSize;
        this.Source = Source;
    }

    public PageOf<T> Page(int pageNum)
    {
        var start = (pageNum - 1) * PageSize;
        return new PageOf<T>(start + 1, Source.Skip(start).Take(PageSize));
    }
}

最后是一个很好的扩展方法来掩盖丑陋:

static class PaginationExtension
{
    public static PaginatedQueryable<T> InPagesOf<T>(this IQueryable<T> target, int PageSize)
    {
        return new PaginatedQueryable<T>(PageSize, target);
    }
}

这意味着你现在可以这样做了:

var products = Products.OrderByDescending(u => u.Sales.Count()).InPagesOf(20).Page(1);

foreach (var product in products.NumberedItems)
{
    Console.WriteLine("{0} {1}", product.Number, product.Item);
}

【讨论】:

    【解决方案4】:

    试试这个

    var x = Products.OrderByDecending(u => u.Sales.Count());
    var y = x.ToList();
    
    for(int i = 0; i < y.Count; i++) {
        int myNumber = i; // this is your order number
    }
    

    只要列表保持相同的顺序,除非销售数量发生变化,否则应该会发生这种情况。您可以得到准确的计数;

    也有这种做法。

    var page = 2;
    var count = 10;
    var startIndex = page * count;
    
    var x = Products.OrderByDecending(u => u.Sales.Count());
    var y = x.Skip(startIndex).Take(count);
    

    这给出了页面的起始索引,此外它还为您提供了一小部分销售额以显示在页面上。您只需从 startIndex 开始在您的网站上进行计数。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-08-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多