尝试使用 EF Core 将一百万条记录保存到 SQL Server 表中。首先从暂存表中读取一百万条记录并将其存储到列表中,然后使用 EF Core 的
AddRangeAsync
函数保存列表中的数据。这是代码块
var importJobValidations = new List<ImportJobValidation>();
importJobValidations.AddRange(
(from stg in stagingUnits.AsEnumerable()
select new ImportJobValidation
{
Guid = Guid.NewGuid(),
StudyProductStagingID = stg.Id,
IsValidationSuccessful = !errors.Exists(e => e.StagingId == stg.Id),
Type = "UPLOAD",
IsActive = true,
CreatedBy = userId,
CreatedOn = DateTime.UtcNow
}));
await _supplyManagementHangFireContext.AddRangeAsync(importJobValidations);
await _supplyManagementHangFireContext.SaveChangesAsync();
stagingUnits
是一个 IQueryable。上面的代码块抛出一个Out of memory exception
。
在这种情况下修复内存不足异常的最佳方法是什么。
一个相对快速的“修复”是:将工作分成可管理的块(例如,每个块 10k 条记录) - 插入那么多,然后完全使用新的数据上下文重新开始。
Enumerable.Chunk
扩展方法可能会让这变得微不足道。
如果要添加那么多数据,您可能更愿意放弃一些抽象级别并查看
SqlBulkCopy
,也许使用 FastMember
或 DapperAOT 之类的工具将 IEnumerable<T>
转换为适合的 IDataReader
流媒体使用;例如(类似于这里):
using var reader = TypeAccessor.CreateDataReader(stagingUnits.AsEnumerable(),
/* TODO: property names to include */);
using var table = new SqlBulkCopy(connection)
{
DestinationTableName = "YourTableHere",
ColumnMappings =
{
// TODO: specify column-property map
}
};
table.EnableStreaming = true;
table.WriteToServer(reader);
return table.RowsCopied;
我可以建议对结果进行分页并批量保存记录:
var batchSize = 1000;
var batchCount = 1000000 / batchSize;
for (var i = 0; i < batchCount; i++)
{
var query = from stg in stagingUnits.AsEnumerable()
select new ImportJobValidation
{
Guid = Guid.NewGuid(),
StudyProductStagingID = stg.Id,
IsValidationSuccessful = !errors.Exists(e => e.StagingId == stg.Id),
Type = "UPLOAD",
IsActive = true,
CreatedBy = userId,
CreatedOn = DateTime.UtcNow
};
var batch = await query.Skip(i * batchSize).Take(batchSize).ToArrayAsync();
// AddRangeAsync is not recommended to be used.
_supplyManagementHangFireContext.AddRange(batch);
await _supplyManagementHangFireContext.SaveChangesAsync();
}
AddRangeAsync
不建议在标准操作中使用。 来自文档: