c# EF6异步保存/编辑



我正在尝试保存大约20,000条记录,并且需要不可原谅的时间才能完成。实现这一目标的最佳方式是什么?

以下是我目前的记录:

public async Task SaveOrUpdateItemAsync(List<DepartmentalizedItem> departmentalizedItems)
{
using(WarehouseContext dbContext = new WarehouseContext())
{
using(SemaphoreSlim throttler = new SemaphoreSlim(20))
{
var tasks = departmentalizedItems.Select(async item =>
{
await throttler.WaitAsync();
if (item.PK_DepartmentalizedItemId == 0)
dbContext.DepartmentalizedItems.Add(item);
else
{
var deptItem = await dbContext.DepartmentalizedItems.FindAsync(item.PK_DepartmentalizedItemId);
dbContext.Entry(deptItem).CurrentValues.SetValues(item);
}
throttler.Release();
});
await Task.WhenAll(tasks);
}
await dbContext.SaveChangesAsync();
}
}

我也试过Parallel。ForEach,但我有同步问题。

谢谢。

For Insert

可以使用AddRange方法将大容量数据插入到db中。

保存在数据库中不需要那么多时间。如果您使用的是Add,它会根据需要逐渐调整内部数组的大小(加倍),从默认的起始大小10 (IIRC)。

AddRange检查添加项的大小,只增加一次内部数组的大小。

为更新

可以使用UpdateRange在一次调用中更新多个记录。这将节省你继续查询的时间。

public class abc
{
public int propa {get;set;}
public string propb {get;set;} 
} 
public bool AddUpdate(List<abc> abc)
{
List<abc> added=new List<abc>();
List<abc> updated=new List<abc>();
foreach(var item in abc)
{
if(item.propa==0) // I considered that if propa has no value then it's new record to be added else updated
{
added.Add(item);
}
else
{
updated.Add(item)
}
}
if(added?.Any()??false)
{
_dbContext.abc.AddRange(added);
}
if(updated?.Any()??false)
{
_dbContext.abc.UpdateRange(added);
}
_dbContext.SaveChanges();
return true;
}

我建议进行一些分析,以查看哪些部分实际花费了时间。我还建议发布时间和您添加的数据的大小,以便能够估计时间的合理程度。"不可原谅的时间";这取决于上下文和耐心。参见最快的插入方式,在顶部帖子插入500k项在~4分钟。

一个可能的问题可能是FindAsync,如果它需要为每个项目运行查询,我预计它会相当慢。您可以通过一次查询所有项来避免这种情况,例如:

var ids = departmentalizedItems.Select(i => i.PK_DepartmentalizedItemId).ToList();
var itemsById = dbContext.DepartmentalizedItems.Where(i => ids.Contains(i)).ToDictionary(i  => i.PK_DepartmentalizedItemId, i => i);

我还会去掉信号量,并将其更改为常规的for循环。dbContext不是线程安全的,所以无论你做什么都不会让它并行运行,你只会让你的代码更难理解,没有真正的好处。

还要注意Async可能不会以任何方式使您的代码更快。它的目的是隐藏延迟,而不是提高性能。我还会考虑把你的项目分成几个块,并添加一些向用户报告进度的方法。

感谢大家的回答、评论和建议。所有这些都被考虑在内,我终于找到了一种更优的方式来批量保存/编辑20,000条记录。根据Hasse的建议,我看了一眼BulkInsert,但这是一个付费库,因此我试图找到任何替代方案,幸运的是我找到了N.EntityFramework.Extensions.

我的问题解决了:

public async Task BulkSaveAndUpdateAsync(List<DepartmentalizedItem> departmentalizedItems, CancellationToken cancellationToken)
{
using (WarehouseContext dbContext = new WarehouseContext())
{
var toAddItems = departmentalizedItems.Where(i => i.PK_DepartmentalizedItemId == 0);
var toUpdateItems = departmentalizedItems.Where(i => i.PK_DepartmentalizedItemId > 0);
await dbContext.BulkInsertAsync(toAddItems, cancellationToken);
await dbContext.BulkUpdateAsync(toUpdateItems, cancellationToken);
}
}

最新更新