【问题标题】:Entity Framework update/insert multiple entities实体框架更新/插入多个实体
【发布时间】:2017-02-01 01:54:17
【问题描述】:

只是对我要完成的工作的一些概述。 我们在应用程序中保留远程数据库(第 3 方)的本地副本。要下载信息,我们使用 api。 我们目前按计划下载信息,然后将新记录插入本地数据库或更新现有记录。 这是它目前的工作方式

public void ProcessApiData(List<Account> apiData)
{
     // get the existing accounts from the local database
     List<Account> existingAccounts = _accountRepository.GetAllList();

     foreach(account in apiData)
     {
         // check if it already exists in the local database
         var existingAccount = existingAccounts.SingleOrDefault(a => a.AccountId == account.AccountId);

         // if its null then its a new record
         if(existingAccount == null)
         {
             _accountRepository.Insert(account);
             continue;
         }

         // else its a new record so it needs updating
         existingAccount.AccountName = account.AccountName;

         // ... continue updating the rest of the properties
     }

     CurrentUnitOfWork.SaveChanges();
}

这很好用,但感觉可以改进。

  1. 每个实体都有这些方法之一,它们都做同样的事情(只是更新不同的属性)或插入不同的实体。有没有办法让它更通用?
  2. 似乎有很多数据库调用,无论如何“批量”执行此操作。我看过这个包,我在其他一些帖子中提到过这个包https://github.com/loresoft/EntityFramework.Extended 但它似乎专注于批量更新具有相同值的单个属性,或者我可以说。

任何关于我如何改进这一点的建议都会很棒。我对 c# 还很陌生,所以我仍在寻找最好的做事方式。

我使用 .net 4.5.2 和 Entity Framework 6.1.3 以及 MSSQL 2014 作为后端数据库

【问题讨论】:

    标签: c# sql-server entity-framework


    【解决方案1】:

    对于 EFCore,您可以使用此库:
    https://github.com/borisdj/EFCore.BulkExtensions
    注意:我是这篇文章的作者。

    对于 EF 6,这个:
    https://github.com/TomaszMierzejowski/EntityFramework.BulkExtensions

    两者都使用批量操作扩展DbContext,并具有相同的语法调用:

    context.BulkInsert(entitiesList);
    context.BulkUpdate(entitiesList);
    context.BulkDelete(entitiesList);
    

    EFCore 版本还有BulkInsertOrUpdate 方法。

    【讨论】:

      【解决方案2】:
      1. 假设 apiData 中的类与您的实体相同,您应该可以使用 Attach(newAccount, originalAccount) 更新现有实体。
      2. 对于批量插入,我使用AddRange(listOfNewEntitities)。如果您有很多实体要插入,建议对它们进行批处理。此外,您可能希望在每个批次上处理和重新创建 DbContext,以便它不会使用太多内存。

        var accounts = new List<Account>();
        var context = new YourDbContext();
        context.Configuration.AutoDetectChangesEnabled = false;
        
        foreach (var account in apiData)
        {
            accounts.Add(account);
            if (accounts.Count % 1000 == 0) 
            // Play with this number to see what works best
            {
                context.Set<Account>().AddRange(accounts);
                accounts = new List<Account>();
                context.ChangeTracker.DetectChanges();
                context.SaveChanges();
                context?.Dispose();
                context = new YourDbContext();
            }
        }
        
        context.Set<Account>().AddRange(accounts);
        context.ChangeTracker.DetectChanges();
        context.SaveChanges();
        context?.Dispose();
        

      对于批量更新,LINQ to SQL 中没有内置任何内容。然而,有一些库和解决方案可以解决这个问题。参见例如Here 使用表达式树的解决方案。

      【讨论】:

      • AddRange 不执行“批量插入”,而只是调用 DetectChanges 一次,这比 add 提高了性能。
      • 谢谢@Hintham。我将此标记为已接受的答案,因为它对我找到最终解决方案有很大帮助。
      • 没问题,很高兴它有帮助
      【解决方案3】:

      列表与字典

      如果实体存在,您每次都检查一个列表,这是不好的。您应该改为创建字典以提高性能。

      var existingAccounts = _accountRepository.GetAllList().ToDictionary(x => x.AccountID);
      
      Account existingAccount;
      
      if(existingAccounts.TryGetValue(account.AccountId, out existingAccount))
      {
          // ...code....
      }
      

      添加与添加范围

      当您添加多条记录时,您应该注意 Add 与 AddRange 的性能。

      • 添加:添加每条记录后调用 DetectChanges
      • AddRange:添加所有记录后调用 DetectChanges

      因此,对于 10,000 个实体,Add 方法在上下文中简单地添加实体所花费的时间增加了 875 倍。

      修复它:

      1. 创建一个列表
      2. 将实体添加到列表中
      3. 在列表中使用 AddRange
      4. 保存更改
      5. 完成!

      在您的情况下,您需要为您的存储库创建一个 InsertRange 方法。

      EF 扩展

      你是对的。该库使用相同的值更新所有数据。这不是你要找的。​​p>

      免责声明:我是项目的所有者Entity Framework Extensions

      如果您想显着提高性能,此库可能非常适合您的企业。

      您可以轻松执行:

      • 批量保存更改
      • 批量插入
      • 批量更新
      • 批量删除
      • 批量合并

      例子:

      public void ProcessApiData(List<Account> apiData)
      {
          // Insert or Update using the primary key (AccountID)
          CurrentUnitOfWork.BulkMerge(apiData);
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-12-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-03-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多