Juste un aperçu de ce que j'essaie d'accomplir. Nous conservons une copie locale d'une base de données distante (tierce partie) dans notre application. Pour télécharger les informations, nous utilisons une API. Nous téléchargeons actuellement les informations selon un calendrier qui insère ensuite de nouveaux enregistrements dans la base de données locale ou met à jour les enregistrements existants. voici comment cela fonctionne actuellement
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();
}
Cela fonctionne bien, mais il semble que cela pourrait être amélioré.
Toutes les suggestions sur la façon dont je peux améliorer cela seraient brillantes. Je suis encore relativement nouveau sur c # donc je cherche toujours la meilleure façon de faire les choses.
J'utilise .net 4.5.2 et Entity Framework 6.1.3 avec MSSQL 2014 comme base de données principale
Attach(newAccount, originalAccount)
pour mettre à jour une entité existante.Pour les insertions groupées, j'utilise AddRange(listOfNewEntitities)
. Si vous avez beaucoup d'entités à insérer, il est conseillé de les regrouper. Vous pouvez également supprimer et recréer le DbContext
sur chaque lot afin qu'il n'utilise pas trop de mémoire.
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();
Pour les mises à jour groupées, rien n'est intégré à LINQ to SQL. Il existe cependant des bibliothèques et des solutions pour résoudre ce problème. Voir par exemple Ici pour une solution utilisant des arbres d'expression.
Pour EFCore, vous pouvez utiliser cette bibliothèque:
https://github.com/borisdj/EFCore.BulkExtensions
Et pour EF 6 celui-ci:
https://github.com/TomaszMierzejowski/EntityFramework.BulkExtensions
Les deux étendent DbContext
avec des opérations en bloc et ont le même appel de syntaxe:
context.BulkInsert(entitiesList);
context.BulkUpdate(entitiesList);
context.BulkDelete(entitiesList);
La version EFCore a en plus la méthode BulkInsertOrUpdate
.
Liste vs dictionnaire
Vous vérifiez une liste à chaque fois si l'entité existe qui est mauvaise. Vous devez plutôt créer un dictionnaire pour améliorer les performances.
var existingAccounts = _accountRepository.GetAllList().ToDictionary(x => x.AccountID);
Account existingAccount;
if(existingAccounts.TryGetValue(account.AccountId, out existingAccount))
{
// ...code....
}
Ajouter vs AddRange
Vous devez être conscient des performances Add vs. AddRange lorsque vous ajoutez plusieurs enregistrements.
Ainsi, pour 10 000 entités, la méthode Add a pris 875 fois plus de temps pour ajouter simplement des entités dans le contexte.
Réparer:
Dans votre cas, vous devrez créer une méthode InsertRange dans votre référentiel.
EF étendu
Vous avez raison. Cette bibliothèque met à jour toutes les données avec la même valeur. Ce n'est pas ce que vous recherchez.
Avertissement : Je suis le propriétaire du projet Entity Framework Extensions
Cette bibliothèque peut parfaitement convenir à votre entreprise si vous souhaitez améliorer considérablement vos performances.
Vous pouvez facilement effectuer:
Exemple:
public void ProcessApiData(List<Account> apiData)
{
// Insert or Update using the primary key (AccountID)
CurrentUnitOfWork.BulkMerge(apiData);
}