2

我正在查询 200k 记录并用完所有服务器的内存(不足为奇)。我是 LINQ 的新手,所以我发现以下代码可以帮助我,但我不知道如何使用它:

public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> collection, int batchSize)
{
    List<T> nextbatch = new List<T>(batchSize);
    foreach (T item in collection)
    {
        nextbatch.Add(item);
        if (nextbatch.Count == batchSize)
        {
            yield return nextbatch;
            nextbatch = new List<T>(batchSize);
        }
    }
    if (nextbatch.Count > 0)
        yield return nextbatch;
}

资源: //goo.gl/aQZIj

这是我的代码,它创建了“内存不足”错误。如何将新的批处理功能合并到我的代码中?

var crmMetrics = _crmDbContext.tpm_metricsSet.Where(a => a.ModifiedOn >= lastRunDate);

foreach (var crmMetric in crmMetrics)
{
    metric = new Metric();                                
    metric.ProductKey = crmMetric.tpm_Product.Id;
    dbContext.Metrics.Add(metric);
    dbContext.SaveChanges();
}
4

2 回答 2

2

它是一个扩展方法,所以如果它是静态类的一部分并且在您的代码中有对该类命名空间的引用,您可以这样做:

var crmMetricsBatches = _crmDbContext.tpm_metricsSet
                        .Where(a => a.ModifiedOn >= lastRunDate)
                        .AsEnumerable() // !!
                        .Batch(20);

除非它没有帮助。到 时.AsEnumerable(),您仍然获取内存中的所有数据,但现在以 20 个块为单位。这是因为您不能直接使用该方法IQueryable:实体框架将尝试将其转换为 SQL,但当然不知道如何做到这一点。

正如 TGH 所说,Skip更多Take的是为此:

var crmMetricsPage = _crmDbContext.tpm_metricsSet
                        .Where(a => a.ModifiedOn >= lastRunDate)
                        .OrderBy(a => a.??) // some property you choose
                        .Skip(pageNo * pageSize)
                        .Take(pageSize);

从哪里pageNo计算到您需要0的页数 ( )。和是表达式,EF 知道如何将它们转换为 SQL。EF 需要知道从哪里开始跳过。- 1SkipTakeOrderBy

在这个称为分页的过程中,您总是pageSize一次获得记录。查询的数量更多,但节省了资源。一个条件是您可以pageSize提前确定a。我不知道这是否符合你的逻辑。

如果您不能使用分页,您应该尝试缩小过滤器 ( Where(a => a.ModifiedOn >= lastRunDate),例如尝试以一天或一周为单位批量获取数据。

于 2013-06-22T21:49:28.400 回答
1

我会使用 Linq 的 Skip and Take 来获取批次

看一下这个:

http://www.c-sharpcorner.com/UploadFile/3d39b4/take-and-skip-operator-in-linq-to-sql/

于 2013-06-22T20:56:07.887 回答