【问题标题】:Deleting large volumes of data from database using .NetCore使用 .Net Core 从数据库中删除大量数据
【发布时间】:2021-11-08 15:31:48
【问题描述】:

我正在尝试使用 .netCore 2.1 从数据库中删除超过 5000 条记录。我有以下方法可以正常工作,但是需要的时间太长。

public async Task<bool> deleteAdhocDetails(int[] id)
{
    var status = false;

    for (var x = 0; x < id.Length; x++)
    {
        var existingReward = await _context.AdhocRewardInfo
            .Where(d => d.RowID == id[x])
            .FirstOrDefaultAsync<AdhocRewardInfo>();

        if ((existingReward != null) && (existingReward.HaloRewardCode != null))
        {
            try
            {
                //removing existingReward
                _context.AdhocRewardInfo.Remove(existingReward);
                await _context.SaveChangesAsync();
                status = true;
            }
            catch (Exception e)
            {
                throw e;
            }
        }
    }
    return status;
}            
            

我目前正在使用 EFCore.BulkExtensions 插入记录,它运行良好。我尝试使用 BulkDelete,但似乎没有什么不同。我也尝试使用 Z.EntityFramework.Extensions.EFCore 但也无法让它工作。我还应该提到我对此有点陌生。如果有人能指出我正确的方向,我将不胜感激。谢谢

在 adhoc.service.ts 中:

deleteAdhocRecipients(id: number[]): Promise<boolean> {
    return this.http.put<boolean>(this.baseUrl + 'deleteAdhocDetails', id)
      .toPromise();
    }

在 .ts 文件中:

this.general
  .load(this.aService.deleteAdhocCampaign(this.adhocForm.get('create.id').value))
  .then(
    y => {
      if (deleteArr.length > 0) {
        this.general.load(this.aService.deleteAdhocRecipients(deleteArr))
          .then(
            f => this.router.navigate(['/adhoc-campaign/lookup'])
          );
      } else {
        this.router.navigate(['/adhoc-campaign/lookup']);
      }
    }
  );

【问题讨论】:

  • 不是这样的。 EF Core 是一个 ORM - 它将对象映射到关系表。 ORM 不适用于批处理或报告操作。在这种情况下没有对象,只有行。 using EFCore.BulkExtensions for inserting records and it works nicely. 不是真的。 EF Core 没有这种能力。此类在后台所做的是将对象转换为行并使用 SqlBulkCopy 执行 BULK INSERT 会执行的相同操作。没有BULK DELETE,这些扩展(实际上来自同一作者)只是写出与您相同的 DELETE 语句
  • 顺便说一句,这段代码存在很多问题,包括对 EF Core 的严重误用。 SaveChanges 仅用于一次,用于持久化并将更改提交到数据库。您可以编写_context.AdhocRewardInfo.Where(d =&gt; ids.Contains(d.RowID) 来加载所有相关实体,在循环中对它们调用Remove,并且仅在循环调用SaveChanges 一次 之后。 EF Core 将生成并执行 DELETE 语句。这仍然比DELETE someTable where Id in (1,2,3,4..) 慢很多,但比执行一个接着一个 DELETE 快很多
  • 是否有理由需要在循环中保存?
  • 这些 ID 是从哪里来的?您可以编写带有生成 ID 的条件的 DELETE ... WHERE 查询,以避免使用单个 ID。如果列表来自外部文件,你可以使用 Dapper 轻松编写参数化DELETE,例如connection.Execute("DELETE from MyTable where ID in @ids",new {ids=id});。不幸的是,IN 最多可以处理大约 2000 个项目。另一种选择是将所有 ID 插入临时/临时表并写入 DELETE MyTable from MyTable inner join tempTable on MyTable.ID=TempTable.ID
  • @PanagiotisKanavos,谢谢你的建议和建议,我会试试看的。

标签: c# .net-core entity-framework-core


【解决方案1】:

我优化了你的 c# 代码

public async Task<bool> deleteAdhocDetails(int[] id){
  try{
  var deletedRecords= await _context.AdhocRewardInfo.Where(d => 
  id.Contains(d.RowID))

  foreach (var item in deletedRecords)
  {
    _context.AdhocRewardInfo.Remove(item );
  }
  await _context.SaveChangesAsync();
  return true;
  }catch (Exception e){
    return false;
   }
 }

【讨论】:

    【解决方案2】:

    您可以删除对象的选择,然后只执行一次 saveChanges

            public async Task<bool> deleteAdhocDetails((int id, object haloRewardCode)[] id)
            {
                try
                {
                    for (var x = 0; x < id.Length; x++)
                    {
                        var existingReward = new AdhocRewardInfo() { id = id[x].id, HaloRewardCode = id[x].haloRewardCode };
    
                        if ((existingReward != null) && (existingReward.HaloRewardCode != null))
                        {
                            //removing existingReward
                           _context.AdhocRewardInfo.Remove(existingReward);
                       }
                    }
                }
                catch (Exception e)
                {
                    throw;
                }
    
                await _context.SaveChangesAsync();
                return true;
            }
    

    【讨论】:

    • 这行不通。 existingReward != null 没有意义,因为您刚刚创建了对象,但 existingReward.HaloRewardCode != null 将始终失败,因为未设置该值。原始代码仅删除具有HaloRewardCode 值的对象
    • 您是否应用了全局查询过滤器?如果没有,请删除此检查并重试...为什么需要 HaloRewardCode 进行删除?
    • 我没有。 OP可以。再次检查查询。这不仅仅是删除与 ID 匹配的每个对象,而是删除具有特定非 null 属性的对象。
    • 你能把 id 和 HaloRewardCode 传递给这个方法吗?
    • 我改变了传递 id 和 haloRewardCode 的方法......我希望你能做到这一点......无论如何你需要在 cicle 之前进行选择......这会降低性能......像 karagoz 说 await _context.AdhocRewardInfo.Where(d => id.Contains(d.RowID))
    【解决方案3】:

    我建议使用linq2db.EntityFrameworkCore(免责声明:我是创作者之一)。

    Delete 会尽可能快:

    var deletedCount = await _context.AdhocRewardInfo
        .Where(d => id.Contains(d.RowID))
        .DeleteAsync();
    

    【讨论】:

    • 我尝试使用您的建议,但遇到“DataContext”是“DataContext”和“LinqToDB.DataContext”之间的模糊引用。
    • 可能你有自己的DataContext 类,所以指定完整路径。基于 ORM linq2db 的扩展和类名可能重复。
    【解决方案4】:

    我认为最快的方法是使用 sql 查询字符串

    public async Task<bool> deleteAdhocDetails(int[] id)
    {
    
    var ids= string.Join(",",id);
    var rows= await _context.Database.ExecuteSqlRawAsync(
    "DELETE FROM AdhocRewardInfo WHERE  Id IN ("+ ids +  ")");
    
    return rows>0;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多