我正在查询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;
}
这是我的代码,它会产生“内存不足”错误。如何将新批处理功能合并到我的代码中?
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();
}
答案 0 :(得分: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
计算到您需要的页数(- 1
)。 Skip
和Take
是表达式,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/