通过IQueryable分批枚举

时间:2017-10-05 17:35:47

标签: c# entity-framework system.reactive

我正在使用Entity Framework Core来创建IQueryable。 我想找到一种方法一次一页地获得结果(一次说10个结果),但随后将其公开为IEnumerable(或类似于IObservable的东西)。我还想确保尽可能保持内存效率,换句话说,如果页面大小为10,那么一次只有10个实体驻留在内存中。最后,我希望数据库调用是异步的,这使得这更加困难。

这是一些伪代码,其中“ToPagingEnumerable”是我想要从另一个库创建或使用的方法:

IQueryable<T> query = dbContext.SomeTable;
IEnumerable<T> results = query.ToPagingEnumerable(pageSize: 10);
ConvertAndSaveResults(results);

这是一个快速失败的尝试,由于你无法将“yield”与“async”结合起来,因此无效:

public static IEnumerable<TSource> ToPagingEnumerable<TSource>(
    this IQueryable<TSource> source, 
    int size)
{
    var skip = 0;
    var cached = await source.Take(size).ToListAsync();
    while (cached.Any())
    {
        foreach (var item in cached)
        {
            yield return item;
        }
        skip += cached.Count;
        cached = await source.Skip(skip).Take(size).ToListAsync();
    }
}

我简要介绍了Reactive Streams(https://github.com/reactive-streams/reactive-streams-dotnet),这看起来与我想要实现的相似。

我认为另一个选择是使用Rx(Reactive)并创建一个Observable,它从IQueryable(比如10行)中抓取一页结果,将它们提供给订阅者,然后抓取另一个页面(比如另外10行),并将它们提供给订阅者。

我对这两个库中的任何一个都不太了解,知道如何使用它们来实现我的目标,或者是否有更简单或不同的方式。

3 个答案:

答案 0 :(得分:0)

您好,您可以使用此扩展方法

public static class QuerableExtensions
{
    public static IQueryable<TEntity> ToPage<TEntity>(this IQueryable<TEntity> query, PagingSettings pagingSettings) where TEntity : class
    {
        if (pagingSettings != null)
        {
            return query.Skip((pagingSettings.PageNumber - 1)*pagingSettings.PageSize).Take(pagingSettings.PageSize);
        }
        return query;
    }
    public static IQueryable<T> OrderByField<T>(this IQueryable<T> query, SortingSettings sortingSettings)
    {
        var exp = PropertyGetterExpression<T>(sortingSettings);

        var method = sortingSettings.SortOrder.Equals(SortOrder.Asc) ? "OrderBy" : "OrderByDescending";

        var types = new[] { query.ElementType, exp.Body.Type };

        var callExpression = Expression.Call(typeof(Queryable), method, types, query.Expression, exp);
        return query.Provider.CreateQuery<T>(callExpression);
    }


}

哪里

    public class PagingSettings
{
    public PagingSettings()
        : this(50, 1)
    { }

    protected PagingSettings(int pageSize, int pageNumber)
    {
        PageSize = pageSize;
        PageNumber = pageNumber;
    }

    public int PageNumber { get; set; }
    public int PageSize { get; set; }
}

要像这样使用它,你必须在进行分页之前订购你的设置

     public async Task<SimplePagedResult<TEntityDto>> GetAllPagedAsync<TEntityDto>(PagingSettins request) where TEntityDto : class
    {
        var projectTo = Set(); // Here is DBSet<TEnitity>


        var entityDtos = projectTo.OrderByField(new SortingSettings());

        if (request.PagingSettings != null)
            entityDtos = entityDtos.ToPage(request.PagingSettings);

        var resultItems = await entityDtos.ToListAsync();

        var result = MakeSimplePagedResult(request.PagingSettings, resultItems);
        return result;
    }

结果的等级是

    public class SimplePagedResult<T>
{
    public IEnumerable<T> Results { get; set; }
    public int CurrentPage { get; set; }
    public int PageSize { get; set; }
}

答案 1 :(得分:0)

为什么多次运行查询?

如何:

results['CreationDate'] = list(pd.to_datetime(pd.Series(strtotime)))

答案 2 :(得分:0)

这是一种无需使用扩展方法即可批量处理您的 IQueryable 的方法:

var batchSize = 5000;

var myqueryable = // GetQueryable();

var count = myqueryable.Count();
var processed = 0;

while (processed < count)
{
    var take = batchSize <= count - processed ? batchSize : count - processed;

    var batchToProcess = myqueryable.Skip(processed).Take(take);

    //Do your processing, insert, what have you...

    processed += take;
}

或者您可以简单地指定 PageSize 和 PageNumber,如下所示:

var myqueryable = // GetQueryable();

//var pageCount = (int)Math.Ceiling(myqueryable.Count() * 1D / PageSize);

int skip = PageNumber * PageSize - PageSize;

return myqueryable.Skip(skip).Take(PageSize);