2016-09-23 15 views
7

Немного очертания того, что я пытаюсь выполнить. Мы храним локальную копию удаленной базы данных (сторонней) в нашем приложении. Чтобы загрузить информацию, мы используем api. В настоящее время мы загружаем информацию о расписании, которое затем вставляет новые записи в локальную базу данных или обновляет существующие записи. вот как он в настоящее время работаетОбновление Entity Framework/вставка нескольких объектов

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. Это просто похоже на множество запросов к базе данных, было бы так или иначе «Bulk» сделать это. Я посмотрел на этот пакет, который я видел на нескольких других сообщениях. https://github.com/loresoft/EntityFramework.Extended Но, похоже, основное внимание уделяется массовому обновлению одного свойства с тем же значением, или я могу сказать.

Любые предложения о том, как я могу улучшить это, были бы блестящими. Я все еще довольно новичок в C#, поэтому я все еще ищу лучший способ сделать что-то.

Я использую .net 4.5.2 и Entity Framework 6.1.3 с MSSQL 2014 в качестве серверной базы данных

ответ

5
  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 к SQL. Тем не менее существуют библиотеки и решения для решения этой проблемы. См. Here для решения с использованием деревьев выражений.

+1

AddRange не выполняет «Массовую вставку», а просто вызывает DetectChanges, что повышает производительность над добавлением. –

+0

Спасибо @Hintham.Я отмечаю это как принятый ответ, так как он очень помог мне достичь окончательного решения. – tjackadams

+0

Без проблем, рад, что это помогло – Hintham

-1

Список против словарь

Вы проверяете в списке каждый раз, если объект существует, который плохо. Вместо этого вы должны создать словарь, чтобы повысить производительность.

var existingAccounts = _accountRepository.GetAllList().ToDictionary(x => x.AccountID); 

Account existingAccount; 

if(existingAccounts.TryGetValue(account.AccountId, out existingAccount)) 
{ 
    // ...code.... 
} 

Добавить против AddRange

Вы должны быть осведомлены о Добавить в сравнении производительности AddRange при добавлении нескольких записей.

  • Добавить: Позвоните DetectChanges после каждой записи добавляется
  • AddRange: Вызов DetectChanges после всех записей добавляется

enter image description here

Так что на 10000 лиц, добавить метод приняли больше 875x время для добавления объектов в контексте просто.

Чтобы это исправить:

  1. создать список
  2. ADD объект в список
  3. USE AddRange со списком
  4. SaveChanges
  5. Done!

В вашем случае вам необходимо создать метод InsertRange в вашем репозитории.

EF Extended

Вы правы. Эта библиотека обновляет все данные с одинаковым значением. Это не то, что вы ищете.

Отказ от ответственности: Я владелец проекта Entity Framework Extensions

Эта библиотека может идеально подходит для вашего предприятия, если вы хотите улучшить свою производительность значительно.

Вы можете легко выполнить:

  • BulkSaveChanges
  • BulkInsert
  • BulkUpdate
  • BulkDelete
  • BulkMerge

Пример:

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

Для EFCore вы можете использовать эту библиотеку:
https://github.com/borisdj/EFCore.BulkExtensions

И для EF 6 это одно:
https://github.com/tiagoln/BulkExtensions

Оба простирающуюся DbContext с объемными операциями и имеют тот же синтаксис вызова:

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

EFCore версия есть дополнительно BulkInsertOrUpdate способ.