Partager via


Mise à jour efficace

Traitement par lots

EF Core permet de réduire les allers-retours en regroupant automatiquement toutes les mises à jour dans un seul aller-retour. Tenez compte des éléments suivants :

var blog = await context.Blogs.SingleAsync(b => b.Url == "http://someblog.microsoft.com");
blog.Url = "http://someotherblog.microsoft.com";
context.Add(new Blog { Url = "http://newblog1.microsoft.com" });
context.Add(new Blog { Url = "http://newblog2.microsoft.com" });
await context.SaveChangesAsync();

Le code ci-dessus charge un blog à partir de la base de données, modifie son URL, puis ajoute deux nouveaux blogs ; pour l’appliquer, deux instructions SQL INSERT et une instruction UPDATE sont envoyées à la base de données. Au lieu de les envoyer un par un, à mesure que des instances de blog sont ajoutées, EF Core effectue le suivi de ces modifications en interne et les exécute en un seul aller-retour lorsque SaveChanges est appelé.

Le nombre d’instructions que EF regroupe en un seul aller-retour dépend du fournisseur de base de données utilisé. Par exemple, l’analyse des performances a montré que le traitement par lots était généralement moins efficace pour SQL Server lorsque moins de 4 instructions sont impliquées. De même, les avantages de l'exécution par lot se dégradent après environ 40 instructions pour SQL Server, donc EF Core exécutera par défaut uniquement jusqu'à 42 instructions dans un seul lot et exécutera des instructions supplémentaires dans des requêtes distinctes.

Les utilisateurs peuvent également ajuster ces seuils pour obtenir des performances potentiellement plus élevées, mais évaluer soigneusement avant de les modifier :

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder.UseSqlServer(
        @"Server=(localdb)\mssqllocaldb;Database=Blogging;Trusted_Connection=True",
        o => o
            .MinBatchSize(1)
            .MaxBatchSize(100));
}

Utiliser ExecuteUpdate et ExecuteDelete en cas de pertinence

Supposons que vous voulez donner à tous vos employés une augmentation. Une implémentation classique pour cela dans EF Core se présente comme suit :

foreach (var employee in context.Employees)
{
    employee.Salary += 1000;
}
await context.SaveChangesAsync();

Bien que ce code soit parfaitement valide, nous allons analyser ce qu’il fait du point de vue des performances :

  • Un boucle de base de données est effectuée, pour charger tous les employés pertinents. Notez que cela réunit toutes les données de ligne Employés dans le client, même si seul le salaire est nécessaire.
  • Le suivi des modifications d’EF Core crée des instantanés lors du chargement des entités, puis compare ces instantanés aux instances pour déterminer les propriétés modifiées.
  • En règle générale, un deuxième aller-retour de base de données est effectué pour enregistrer toutes les modifications (notez que certains fournisseurs de base de données divisent les modifications en plusieurs allers-retours). Bien que ce comportement de traitement par lots soit beaucoup mieux que d’effectuer un aller-retour pour chaque mise à jour, EF Core envoie toujours une instruction UPDATE par employé, et la base de données doit exécuter chaque instruction séparément.

À compter d’EF Core 7.0, vous pouvez utiliser les méthodes ExecuteUpdateAsync et ExecuteDeleteAsync pour faire la même chose beaucoup plus efficacement :

await context.Employees.ExecuteUpdateAsync(s => s.SetProperty(e => e.Salary, e => e.Salary + 1000));

Cela envoie l’instruction SQL suivante à la base de données :

UPDATE [Employees] SET [Salary] = [Salary] + 1000;

Cette UPDATE effectue toute l’opération en un seul aller-retour, sans charger ni envoyer de données réelles à la base de données, et sans utiliser la machine de suivi des modifications d’EF, ce qui impose une surcharge supplémentaire. Pour plus d’informations, consultez ExecuteUpdate et ExecuteDelete.

Si vous utilisez une version antérieure d’EF Core qui ne prend pas encore en charge ExecuteUpdate et ExecuteDelete, ou si vous souhaitez exécuter une instruction SQL complexe qui n’est pas prise en charge par ces méthodes, vous pouvez toujours utiliser une requête SQL pour effectuer l’opération :

context.Database.ExecuteSql($"UPDATE [Employees] SET [Salary] = [Salary] + 1000");

Pour en savoir plus sur les différences entre SaveChanges et ExecuteUpdate/ExecuteDelete, consultez la page vue d’ensemble sur l’enregistrement des données.