我正在查询20万条记录,并耗尽了服务器的所有内存(这并不奇怪)。我是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
这是我的代码,它创建了"内存不足"错误。如何将新的Batch函数合并到代码中?
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();
}
这是一个扩展方法,因此如果它是静态类的一部分,并且在代码中引用了该类的命名空间,则可以执行以下操作:
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
才能知道从哪里开始跳过。
在这个称为分页的过程中,您总是一次获得pageSize
记录。查询的数量更多,但资源是多余的。一个条件是您可以提前确定pageSize
。我不知道这是否符合你的逻辑。
如果不能使用分页,则应尝试缩小筛选器(Where(a => a.ModifiedOn >= lastRunDate
),例如,尝试在一天或一周内分批获取数据。
我会使用Linq的Skip and Take来获取批次
看看这个:
http://www.c-sharpcorner.com/UploadFile/3d39b4/take-and-skip-operator-in-linq-to-sql/