私が達成しようとしていることのほんの少しの概要。アプリケーション内にリモートデータベース(サードパーティ)のローカルコピーを保持します。情報をダウンロードするには、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();
}
これは正常に機能しますが、改善できるように感じます。
私がこれをどのように改善できるかについての提案は素晴らしいでしょう。私はまだc#にかなり慣れていないので、物事を行うための最良の方法を探しています。
バックエンドデータベースとしてMSSQL 2014で.net 4.5.2とEntity Framework 6.1.3を使用しています
Attach(newAccount, originalAccount)
を使用して既存のエンティティを更新できるはずです。一括挿入には、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には何も組み込まれていません。ただし、これに対処するライブラリとソリューションがあります。例参照 ここ 式ツリーを使用したソリューションの場合。
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
メソッドが追加されています。
リストと辞書
悪いエンティティが存在する場合、毎回リストをチェックインします。代わりに、パフォーマンスを向上させるために辞書を作成する必要があります。
var existingAccounts = _accountRepository.GetAllList().ToDictionary(x => x.AccountID);
Account existingAccount;
if(existingAccounts.TryGetValue(account.AccountId, out existingAccount))
{
// ...code....
}
Add vs. AddRange
複数のレコードを追加するときは、AddとAddRangeのパフォーマンスに注意する必要があります。
したがって、エンティティが10,000個の場合、Addメソッドは、コンテキストにエンティティを単純に追加するのに875倍の時間がかかりました。
修正するには:
あなたの場合、InsertRangeメソッドをリポジトリに作成する必要があります。
EF拡張
あなたが正しいです。このライブラリは、すべてのデータを同じ値で更新します。それはあなたが探しているものではありません。
免責事項:私はプロジェクトの所有者です Entity Framework Extensions
パフォーマンスを劇的に改善したい場合、このライブラリは企業に完全に適合します。
以下を簡単に実行できます。
例:
public void ProcessApiData(List<Account> apiData)
{
// Insert or Update using the primary key (AccountID)
CurrentUnitOfWork.BulkMerge(apiData);
}