【问题标题】:c# EF6 asynchronous saving/editingc# EF6 异步保存/编辑
【发布时间】:2022-11-17 09:56:03
【问题描述】:

我正在尝试保存大约 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 但我遇到了同步问题。

谢谢你。

【问题讨论】:

  • 您可以检查 this 关于实体状态以避免 Find 调用。
  • 你应该看看 BulkInsert entityframework-extensions.net/bulk-insert
  • @Hasse 这似乎是一个付费图书馆。还有其他选择吗?
  • @StavrosZotalis 我已经试过了,但是我遇到了一个错误。 “失败,因为同一类型的另一个实体已经具有相同的主键值。”。我记得同样的错误是我不使用 EntityState 的原因。
  • @redz0323 测试这个 nuget 包 nuget.org/packages/EntityFramework6.BulkInsert

标签: c# entity-framework-6


【解决方案1】:

我建议进行一些分析,以查看哪些部分实际需要时间。我还建议发布时间和您要添加的数据的大小,以便能够估计时间的合理性。 “不可原谅的时间”将取决于上下文和耐心。请参阅Fastest way of inserting,其中最上面的帖子在大约 4 分钟内插入了 500k 个项目。

一个可能的问题可能是 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 可能不会以任何方式使您的代码更快。它旨在隐藏延迟,而不是提高性能。我还会考虑将您的项目分成块,并添加一些方式来向用户报告进度。

【讨论】:

  • 谢谢您的回答,是的,您提到的 await 实际上导致了异常。
【解决方案2】:

对于插入

您可以使用AddRange 方法将批量数据插入数据库。将它们保存在数据库中不会花费太多时间。

如果您使用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;
}

【讨论】:

  • 感谢您指出,这回答了保存部分,抱歉我不够具体,我的意思是保存和编辑。由于大多数记录正在编辑中。在我的 else 声明中,您对我如何优化它有任何见解吗?谢谢
  • 谢谢,如果可能的话,你能提供一个sample sn-p吗?我找不到关于如何在 EF6 上实现它的示例。谢谢你。
  • @redz0323 我也更新了示例
  • 注一:普通 EF6 不支持 AddRange 和 UpdateRange 方法。它们是 EF6 核心的一部分。此外,我认为该解决方案将遇到相同的错误“失败,因为同一类型的另一个实体已经具有相同的主键值”。因为据我所知,DepartmentalizedItem 具有指向共享相同键的不同未附加对象的导航属性。笔记2:Entity Framework 6 默认启用延迟加载。
  • 他需要使用AsNoTracking() 来避免这个问题。感谢您的注意。
【解决方案3】:

感谢您提供所有答案、cmets 和建议。所有这些都已考虑在内,我终于找到了一种更优化的批量保存/编辑 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);
        }
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-11-01
    • 2017-02-23
    • 2014-07-14
    • 2019-04-03
    • 2018-06-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多