Linq分页 - 如何解决性能问题?

时间:2010-09-01 03:22:52

标签: c# linq performance entity-framework pagination

修改:实体框架似乎是问题,在问题Entity Framework & Linq performance problem中进一步讨论。

我支持一个由长期离开的人编写的PagedList(使用linq / generics)类 - 它有2行性能非常差 - 在一个只有2千行的数据集上运行需要一分钟。 / p>

两条违规行是:

TotalItemCount = source.Count();

我可能可以通过将计数作为参数传递来解决这个问题。但另一条很慢的行是此代码段中的AddRange

IQueryable<T> a = source.Skip<T>((index) * pageSize).Take<T>(pageSize);
AddRange(a.AsEnumerable());

我不明白为什么AddRange如此缓慢或我能做些什么来改善它?

整个班级来源列表是

public class PagedList<T> : List<T>, IPagedList<T>
{
    public PagedList(IEnumerable<T> source, int index, int pageSize)
     : this(source, index, pageSize, null)
    {
    }

    public PagedList(IEnumerable<T> source, int index, int pageSize, int? totalCount)
    {
        Initialize(source.AsQueryable(), index, pageSize, totalCount);
    }

    public PagedList(IQueryable<T> source, int index, int pageSize)
     : this(source, index, pageSize, null)
    {
    }

    public PagedList(IQueryable<T> source, int index, int pageSize, int? totalCount)
    {
        Initialize(source, index, pageSize, totalCount);
    }

    #region IPagedList Members

    public int PageCount { get; private set; }
    public int TotalItemCount { get; private set; }
    public int PageIndex { get; private set; }
    public int PageNumber { get { return PageIndex + 1; } }
    public int PageSize { get; private set; }
    public bool HasPreviousPage { get; private set; }
    public bool HasNextPage { get; private set; }
    public bool IsFirstPage { get; private set; }
    public bool IsLastPage { get; private set; }

    #endregion

    protected void Initialize(IQueryable<T> source, int index, 
            int pageSize, int? totalCount)
    {
        //### argument checking
        if (index < 0)
        {
            throw new ArgumentOutOfRangeException("PageIndex cannot be below 0.");
        }
        if (pageSize < 1)
        {
            throw new ArgumentOutOfRangeException("PageSize cannot be less than 1.");
        }

        //### set source to blank list if source is null to prevent exceptions
        if (source == null)
        {
            source = new List<T>().AsQueryable();
        }

        //### set properties
        if (!totalCount.HasValue)
        {
            TotalItemCount = source.Count();
        }
        PageSize = pageSize;
        PageIndex = index;
        if (TotalItemCount > 0)
        {
            PageCount = (int)Math.Ceiling(TotalItemCount / (double)PageSize);
        }
        else
        {
            PageCount = 0;
        }
        HasPreviousPage = (PageIndex > 0);
        HasNextPage = (PageIndex < (PageCount - 1));
        IsFirstPage = (PageIndex <= 0);
        IsLastPage = (PageIndex >= (PageCount - 1));

        //### add items to internal list
        if (TotalItemCount > 0)
        {
            IQueryable<T> a = source.Skip<T>((index) * pageSize).Take<T>(pageSize);
            AddRange(a.AsEnumerable());
        }
    }
}

2 个答案:

答案 0 :(得分:3)

可能不是AddRange很慢,它可能是对源的查询。对AsEnumerable()的调用将立即返回,但在实际枚举序列之前,实际上不会执行查询直到AddRange调用内部。

您可以通过更改此行来证明这一点:

AddRange(a.AsEnumerable());

为:

T[] aa = a.ToArray();
AddRange(aa);

您可能会看到ToArray()调用占用了大部分时间,因为这是实际执行查询的时间。现在为什么那么慢是任何人的猜测。如果您正在使用LINQ to SQL或Entity Framework,则可以尝试分析数据库以查看瓶颈所在。但它可能不是PagedList类。

答案 1 :(得分:0)

为什么不让a成为IEnumerable?然后,您根本不必致电AsEnumerable()

虽然想到了它,但是在调用AsEnumerable之前查询实际上并不执行,所以它可能在这里没有任何区别。