web-dev-qa-db-fra.com

Comment supprimer 1 000 lignes avec EF6?

J'utilise Entity Framework 6.

J'ai un tableau avec des informations de test appelé Tests. Je supprime des lignes de ce tableau en obtenant d'abord une liste des tests, en effectuant une suppression pour chacun, puis une validation.

   var testList = _testService.GetTests(1, userId).ToList();
   testList.ForEach(_obj => _uow.Tests.Delete(_obj));
   _uow.Commit();

J'ai un autre tableau avec des informations sur les questions appelé Questions. Je voudrais faire de même mais il y a plus de 1000 lignes dans ce tableau. Si je les répertorie tous et que je fais ensuite 1 000 suppressions, cela ne sera pas très efficace.

Cette suppression de questions ne se produit pas très souvent. Quelqu'un at-il une suggestion sur la façon dont je pourrais faire cela. Dois-je faire 1 000 suppressions. Est-il normal de faire ce genre de chose en utilisant EF?

39
Samantha J T Star

Autant que je sache, EF 6 a introduit l'option .RemoveRange () sur votre DbContext. Donc, en bref, vous pouvez faire quelque chose comme ceci:

var db = new MyDbContext();
var itemsToDelete = db.MyTable.Where(x=>!x.active);
db.MyTable.RemoveRange(itemsToDelete);
db.SaveChanges();

Ainsi, au lieu d'avoir à faire n'importe quel type de foreach, vous pouvez utiliser cette nouvelle méthode d'extension. Avec votre contexte Unit Of Work, vous pourriez avoir une surcharge de votre méthode Delete qui prend un IEnumerable (? *) Au lieu d'un seul objet Test comme votre méthode actuelle. Cette nouvelle surcharge devrait appeler la fonction RemoveRange() sur le DbContext.

? * - Cela dépend de ce que GetTests() renvoie, mais je pense que IEnumerable<> Couvre à la fois un IList<> Et un IQueryable<>

Modifier

Quelques commentaires. Tout d'abord, je n'appellerais pas .ToList() avant d'émettre le RemoveRange car vous ne voulez pas réellement récupérer les éléments à votre service. Cela devrait aider à réduire certains temps de performance. Deuxièmement, vous avez raison, en quelque sorte, de toujours émettre 1000 instructions de suppression. Cependant, les gains de performances proviennent de ne pas appeler le ChangeTracker dans EF pour chaque élément individuel que vous supprimez du DbSet. De magazine MSDN :

AddRange et RemoveRange Comme mentionné précédemment, AddRange et RemoveRange sont des contributions du membre de la communauté Zorrilla. Chaque méthode prend comme paramètre un énumérable d'un type d'entité unique. Dans le premier exemple de code de la section de partage de DbTransactions, j'ai utilisé AddRange lorsque je suis passé dans un tableau d'instances de Casino:

context.Casinos.AddRange (nouveau [] {casino1, casino2}); Ces méthodes s'exécutent beaucoup plus rapidement que l'ajout ou la suppression d'un seul objet à la fois car, par défaut, Entity Framework appelle DetectChanges dans chaque méthode Add et Remove. Avec les méthodes Range, vous pouvez gérer plusieurs objets tandis que DetectChanges n'est appelé qu'une seule fois, ce qui améliore considérablement les performances. J'ai testé cela en utilisant cinq, 50, 500, 5 000 et même 50 000 objets et, au moins dans mon scénario, il n'y a pas de limite à la taille de la matrice - et c'est incroyablement rapide! Gardez à l'esprit que cette amélioration n'est utile que pour que le contexte agisse sur les objets et n'a aucune incidence sur SaveChanges. L'appel à SaveChanges exécute toujours une seule commande de base de données à la fois. Ainsi, bien que vous puissiez ajouter rapidement 50 000 objets dans un contexte, vous obtiendrez toujours 50 000 commandes d'insertion exécutées individuellement lorsque vous appellerez SaveChanges. Ce n'est probablement pas quelque chose que vous voulez faire dans un vrai système.

D'un autre côté, il y a eu de longues discussions sur la mise en œuvre de la prise en charge des opérations en bloc sans nécessiter le suivi des objets par EF (bit.ly/16tMHw4), et des opérations par lots pour permettre l'envoi simultané de plusieurs commandes en un seul appel à la base de données (bit.ly/PegT17). Aucune de ces fonctionnalités n'est entrée dans la version initiale d'EF6, mais les deux sont importantes et prévues pour une future version.

Si vous voulez vraiment émettre une seule commande de base de données, soit une procédure stockée d'utilisation d'instructions SQL brutes serait la solution, car EntityFramework ne prend pas en charge les transactions en bloc. Cependant, l'utilisation des éléments RemoveRange et AddRange (surtout si, comme vous l'avez dit, sont peu fréquents) vous fera gagner beaucoup de temps par rapport à l'appel de Remove() dans une boucle foreach .

86
Tommy

Construit dans Entity Framework . Méthode RemoveRange () , récupère toujours les entrées en mémoire et émet [~ # ~ ] x [~ # ~] supprime le bouclage dans tous les cas.

Si vous ne voulez pas écrire Any SQL for Deletion en particulier lorsque la sélection des entités à supprimer est complexe

Entity Framework Plus Library offre suppression-mise à jour par lots méthodes n'émettant qu'une seule commande.

// Deleting
context.Users
  .Where(u => u.FirstName == "firstname")
  .Delete();

Une limitation actuelle de Entity Framework est que pour mettre à jour ou supprimer une entité, vous devez d'abord la récupérer en mémoire. Maintenant, dans la plupart des scénarios, c'est très bien. Il existe cependant certains senerios où les performances en souffriraient. En outre, pour les suppressions uniques, l'objet doit être récupéré avant de pouvoir être supprimé, ce qui nécessite deux appels à la base de données. La mise à jour et la suppression par lots élimine la nécessité de récupérer et de charger une entité avant de la modifier.

12
Anestis Kivranoglou

J'ai fait quelques tests en utilisant EF6 et Sql Server Profiler

en utilisant .RemoveRange ()

Il récupère d'abord tous les enregistrements à supprimer de la base de données

exec sp_executesql N'SELECT [Extent1]. [Id] AS [Id], [Extent1]. [IdOrder] AS [IdOrder], [Extent1]. [Name] AS [Name], [Extent1]. [Partita] AS [ Partita], FROM [dbo]. [MyTable] AS [Extent1] WHERE [Extent1]. [IdOrder] = @ p__linq__0 ', N' @ p__linq__0 varchar (8000) ', @ p__linq__0 =' 0cb41f32-7ccb-426a-a159- b85a4ff64c29 "

Ensuite, il lance la commande N delete sur la base de données

exec sp_executesql N'DELETE [dbo]. [MyTable] WHERE ([Id] = @ 0) ', N' @ 0 varchar (50) ', @ 0 =' ffea29aa-8ba5-4ac9-871b-3f5979180006 '

X 1000 fois

Cela arrive aussi en utilisant et IQueriable

tilisation de la bibliothèque étendue d'Entity Framework

Il ne déclenche qu'une seule commande dans la base de données

exec sp_executesql N'DELETE [dbo]. [MyTable] FROM [dbo]. [MyTable] AS j0 INNER JOIN (SELECT 1 AS [C1], [Extent1]. [Id] AS [Id] FROM [dbo]. [MyTable] ] AS [Extent1] WHERE [Extent1]. [IdOrder] = @ p__linq__0) AS j1 ON (j0. [Id] = j1. [Id]) ', N' @ p__linq__0 nvarchar (36) ', @ p__linq__0 = N' 0cb41f32-7ccb-426a-a159-b85a4ff64c29 '

3
Marco Staffoli