LINQ内存不足错误

时间:2013-06-22 20:49:09

标签: performance linq entity-framework system.reactive

我正在查询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;
}

来源:http://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();
}

2 个答案:

答案 0 :(得分:2)

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

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

除非没有帮助。通过.AsEnumerable(),你仍然可以获取内存中的所有数据,但现在是20块。这是因为你不能直接使用该方法来对付IQueryable:实体框架会尝试将其转换为SQL但是当然不知道怎么做。

正如TGH所说,SkipTake更多是为此做的:

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

其中pageNo0计算到您需要的页数(- 1)。 SkipTake表达式,EF知道如何将这些转换为SQL。 EF需要OrderBy才能知道从哪里开始跳过。

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

如果您无法使用分页,则应尝试缩小过滤器(Where(a => a.ModifiedOn >= lastRunDate),例如尝试分批获取一天或一周的数据。

答案 1 :(得分:1)

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

检查出来:

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