Cadre d'entité. Supprimer toutes les lignes du tableau

280

Comment puis-je supprimer rapidement toutes les lignes du tableau à l'aide d'Entity Framework?

J'utilise actuellement:

var rows = from o in dataDb.Table
           select o;
foreach (var row in rows)
{
    dataDb.Table.Remove(row);
}
dataDb.SaveChanges();

Cependant, son exécution est longue.

Y a-t-il des alternatives?

Zhenia
la source
22
En lisant les réponses, je me demande pourquoi aucun de ces TRUNCATEadeptes ne s'inquiète des contraintes liées aux clés étrangères.
Gert Arnold
2
Je suis un peu étonné de voir comment les réponses ici tiennent pour acquis que tout le monde utilise Microsoft SQL Server, même si la prise en charge d'autres bases de données dans Entity Framework remonte à autant que je puisse trouver des informations sur cette question et la précède certainement de plusieurs années. . Astuce: si une réponse cite des noms de table dans des instructions SQL entre crochets (comme:) [TableName], elle n'est pas portable.
Mark Amery

Réponses:

293

Pour ceux qui googlent cela et se retrouvent ici comme moi, c'est comme ça que vous le faites actuellement dans EF5 et EF6:

context.Database.ExecuteSqlCommand("TRUNCATE TABLE [TableName]");

En supposant que le contexte est un System.Data.Entity.DbContext

Ron Sijm
la source
20
Pour info, afin d'utiliser TRUNCATE, l'utilisateur doit avoir l'autorisation ALTER sur la table. ( stackoverflow.com/questions/4735038/… )
Alex
7
@Alex vient de perdre une tonne de temps sur l'erreur "Impossible de trouver l'objet MyTable car il n'existe pas ou vous n'avez pas les autorisations." pour cette raison exacte - les autorisations ALTER sont rarement accordées aux applications EF, et le message d'erreur vous envoie vraiment dans une chasse aux oies sauvages.
Chris Moschini
8
J'ai eu des problèmes car ma table faisait partie d'une relation de clé étrangère, même si c'était la table feuille dans cette relation. J'ai fini par utiliser context.Database.ExecuteSqlCommand ("DELETE FROM [Centres d'intérêt"); à la place
Dan Csharpster
2
Notez que bien que les [échappements ici soient spécifiques à SQL Server, la TRUNCATEcommande ne l'est pas - elle fait partie de ANSI SQL et fonctionnera donc dans la plupart des dialectes SQL (mais pas SQLite).
Mark Amery
207

Avertissement: ce qui suit ne convient que pour les petites tables (pensez <1000 lignes)

Voici une solution qui utilise le framework d'entité (pas SQL) pour supprimer les lignes, elle n'est donc pas spécifique à SQL Engine (R / DBM).

Cela suppose que vous effectuez cette opération pour des tests ou une situation similaire. Soit

  • La quantité de données est petite ou
  • La performance n'a pas d'importance

Appelez simplement:

VotingContext.Votes.RemoveRange(VotingContext.Votes);

En supposant ce contexte:

public class VotingContext : DbContext
{
    public DbSet<Vote> Votes{get;set;}
    public DbSet<Poll> Polls{get;set;}
    public DbSet<Voter> Voters{get;set;}
    public DbSet<Candidacy> Candidates{get;set;}
}

Pour un code plus ordonné, vous pouvez déclarer la méthode d'extension suivante:

public static class EntityExtensions
{
    public static void Clear<T>(this DbSet<T> dbSet) where T : class
    {
        dbSet.RemoveRange(dbSet);
    }
}

Ensuite, ce qui précède devient:

VotingContext.Votes.Clear();
VotingContext.Voters.Clear();
VotingContext.Candidacy.Clear();
VotingContext.Polls.Clear();
await VotingTestContext.SaveChangesAsync();

J'ai récemment utilisé cette approche pour nettoyer ma base de données de test pour chaque exécution de testcase (c'est évidemment plus rapide que de recréer la base de données à chaque fois, bien que je n'ai pas vérifié la forme des commandes de suppression qui ont été générées).


Pourquoi cela peut-il être lent?

  1. EF obtiendra TOUTES les lignes (VotingContext.Votes)
  2. puis utiliseront leurs identifiants (je ne sais pas exactement comment, peu importe), pour les supprimer.

Donc, si vous travaillez avec une quantité importante de données, vous tuerez le processus du serveur SQL (il consommera toute la mémoire) et la même chose pour le processus IIS car EF mettra en cache toutes les données de la même manière que le serveur SQL. N'utilisez pas celui-ci si votre table contient une quantité importante de données.

Ahmed Alejo
la source
1
Excellente réponse, j'ai accéléré mon code de suppression de toutes les lignes d'un facteur 10! Notez que j'ai dû renommer la méthode d'extension statique Clear () en quelque chose comme ClearDbSet () car j'avais déjà une autre méthode d'extension statique Clear () définie ailleurs dans mon projet.
dodgy_coder
1
Le changement de nom de @dodgy_coder n'est pas nécessaire pour la raison que vous avez donnée, car la méthode d'extension est pour DbSet, IDbSet s et non IEnumerable, IList, ICollection, ICache ou toute autre interface qui "Clear" serait nécessaire. la préférence pour la méthode d'extension est le type sur lequel ils sont définis. mais si cela vous semble plus clair et ne semble pas redondant, c'est parfait !. Je suis content que cela aide à la performance! À votre santé!
Ahmed Alejo
je suis content que vous ayez déclaré pour les petites tables uniquement. j'espère que les gens comprennent l'importance de votre explication. parce que cette voie est du sucre syntaxique, la bonne voie est celle que Ron Sijm a suggérée. car vous ne chargez pas les données avant de les supprimer. bravo cependant pour avoir montré et expliqué cette façon de procéder.
yedevtxt
3
Cela ne réinitialise pas la clé d'identité. Donc, si vous effacez 10 enregistrements, le prochain sera toujours 11.
MDave
89

L'utilisation de la TRUNCATE TABLEcommande SQL sera la plus rapide car elle opère sur la table et non sur des lignes individuelles.

dataDb.ExecuteStoreCommand("TRUNCATE TABLE [Table]");

En supposant que dataDbc'est un DbContext(pas un ObjectContext), vous pouvez le boucler et utiliser la méthode comme ceci:

var objCtx = ((System.Data.Entity.Infrastructure.IObjectContextAdapter)dataDb).ObjectContext;
objCtx.ExecuteStoreCommand("TRUNCATE TABLE [Table]");
Rudi Visser
la source
Si vous obtenez une erreur d'autorisation lorsque vous essayez cela, changez simplement TRUNCATE TABLEenDELETE FROM
codeMonkey
1
@codeMonkey prend juste note que ce sont des opérations différentes, mais auront (presque) le même effet net 👍
Rudi Visser
3
Mais DELETE ne réinitialise pas la graine IDENTITY. Cela pourrait être problématique dans certaines situations.
Steve
43
var all = from c in dataDb.Table select c;
dataDb.Table.RemoveRange(all);
dataDb.SaveChanges();
user3328890
la source
11
Cela ne doit pas être utilisé car vous exécutez une sélection complète et une suppression après au lieu d'une simple suppression. Du point de vue de la performance temporelle, c'est un grand NON!
HellBaby
2
@HellBaby À moins que ce soit un rarement appelé et donc les performances sont assez hors de propos.
Alex
6
Même si c'est rarement appelé, c'est mauvais. Une table avec seulement 3000 entrées peut prendre plus de 30 secondes en raison de la lenteur du suivi des modifications EF.
léger
1
Cela ne convient que pour les petites tables (<1000 lignes)
Amir Touitou
Parfait pour ma base de données en mémoire dans mes tests unitaires :)
SimonGates
38
using (var context = new DataDb())
{
     var ctx = ((System.Data.Entity.Infrastructure.IObjectContextAdapter)context).ObjectContext;
     ctx.ExecuteStoreCommand("DELETE FROM [TableName] WHERE Name= {0}", Name);
}

ou

using (var context = new DataDb())
{
     context.Database.ExecuteSqlCommand("TRUNCATE TABLE [TableName]");
}
Manish Mishra
la source
1
mais quand je l'écris. "query.Delete ();" - "Supprimer" ne reconnaît pas
Zhenia
1
ajouter une référence de System.Data.Entity et EntityFrameWork dans votre projet en cours
Manish Mishra
Quelle méthode d'extension est Supprimer?
Ahmed Alejo
Pour autant que je sais qu'il n'y a pas une méthode d'extension Deletesur IQueryable- Je devine que Manish utilisait quelque chose comme EntityFramework.Extended: github.com/loresoft/EntityFramework.Extended
null
J'ai édité ma réponse, plus tôt elle était trompeuse. @null, vous avez raison, il .Deletes'agissait d'une extension personnalisée et dans le feu de la publication de la première réponse, j'ai totalement oublié de mentionner la définition de cette coutume .Delete. :)
Manish Mishra
23

Vous pouvez le faire sans Foreach

dataDB.Table.RemoveRange(dataDB.Table);
dataDB.SaveChanges();

Cela supprimera toutes les lignes

Omid Farvid
la source
Tronquera-t-il les éléments de propriété de navigation?
John Deer
19

Cela évite d'utiliser n'importe quel sql

using (var context = new MyDbContext())
{
    var itemsToDelete = context.Set<MyTable>();
    context.MyTables.RemoveRange(itemsToDelete);
    context.SaveChanges();
}
Rob Sedgwick
la source
9

Je suis tombé sur cette question quand j'ai dû faire face à un cas particulier: la mise à jour complète du contenu dans une table "feuille" (pas de FK pointant dessus). Cela impliquait de supprimer toutes les lignes et de mettre de nouvelles informations sur les lignes et cela devrait être fait de manière transactionnelle (je ne veux pas me retrouver avec une table vide, si les insertions échouent pour une raison quelconque).

J'ai essayé le public static void Clear<T>(this DbSet<T> dbSet) approche, mais de nouvelles lignes ne sont pas insérées. Un autre inconvénient est que l'ensemble du processus est lent, car les lignes sont supprimées une par une.

Je suis donc passé à l' TRUNCATEapproche, car il est beaucoup plus rapide et il est également ROLLBACKable . Il réinitialise également l'identité.

Exemple utilisant un modèle de référentiel:

public class Repository<T> : IRepository<T> where T : class, new()
{
    private readonly IEfDbContext _context;

    public void BulkInsert(IEnumerable<T> entities)
    {
        _context.BulkInsert(entities);
    }

    public void Truncate()
    {
        _context.Database.ExecuteSqlCommand($"TRUNCATE TABLE {typeof(T).Name}");
    }
 }

 // usage 
 DataAccess.TheRepository.Truncate();
 var toAddBulk = new List<EnvironmentXImportingSystem>();

 // fill toAddBulk from source system
 // ...

 DataAccess.TheRepository.BulkInsert(toAddBulk);
 DataAccess.SaveChanges();

Bien sûr, comme déjà mentionné, cette solution ne peut pas être utilisée par des tables référencées par des clés étrangères (TRUNCATE échoue).

Alexei
la source
Deux commentaires: 1. Le nom de la table doit être entouré de [...]. Une de mes classes / tables s'appelle "Transaction", qui est un mot clé SQL. 2. Si l'objectif est d'effacer toutes les tables d'une base de données pour les tests unitaires, les problèmes liés aux contraintes de clé étrangère peuvent facilement être résolus en ordonnant aux tables de les traiter de manière à ce que les tables enfants soient tronquées avant les tables parentes.
Christoph
@Christoph - 1. Oui, c'est vrai. Cela m'a manqué, car je nomme toujours les tableaux pour éviter les mots clés car cela pourrait entraîner des problèmes. 2. Si je me souviens bien, les tables référencées par les FK ne peuvent pas être tronquées (SQL Server lance Cannot truncate table because it is being referenced by a FOREIGN KEY constraint), même si elles sont vides, donc les FK doivent être supprimés et recréés pour pouvoir être utilisés de TRUNCATEtoute façon.
Alexei
5

si

      using(var db = new MyDbContext())
            {
               await db.Database.ExecuteSqlCommandAsync(@"TRUNCATE TABLE MyTable"););
            }

les causes

Impossible de tronquer la table 'MyTable' car elle est référencée par une contrainte FOREIGN KEY.

J'utilise ceci:

      using(var db = new MyDbContext())
               {
                   await db.Database.ExecuteSqlCommandAsync(@"DELETE FROM MyTable WHERE ID != -1");
               }
Zakos
la source
1
Si vous avez des contraintes de clé étrangère: (1) SQL pourrait être simplifié en "DELETE FROM MyTable". (2) Cela ne réinitialisera pas le compteur Id si vous l'avez réglé sur auto-incrémentation (tronquer le fait).
RGH
5
var data = (from n in db.users select n);
db.users.RemoveRange(data);
db.SaveChanges();
DBB
la source
4

Si vous souhaitez effacer l'intégralité de votre base de données.

En raison des contraintes de clé étrangère, il importe de savoir dans quelle séquence les tables sont tronquées. C'est un moyen de renforcer brutalement cette séquence.

    public static void ClearDatabase<T>() where T : DbContext, new()
    {
        using (var context = new T())
        {
            var tableNames = context.Database.SqlQuery<string>("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_NAME NOT LIKE '%Migration%'").ToList();
            foreach (var tableName in tableNames)
            {
                foreach (var t in tableNames)
                {
                    try
                    {

                        if (context.Database.ExecuteSqlCommand(string.Format("TRUNCATE TABLE [{0}]", tableName)) == 1)
                            break;

                    }
                    catch (Exception ex)
                    {

                    }
                }
            }

            context.SaveChanges();
        }
    }

usage:

ClearDatabase<ApplicationDbContext>();

n'oubliez pas de réactiver votre DbContext après cela.

Kristian Nissen
la source
2

Les travaux suivants sur la base de données SQLite (en utilisant Entity Framework)

Il semble que le moyen le plus rapide d'effacer toutes les tables de base de données utilise "context.Database.ExecuteSqlCommand (" certains SQL ")", comme certains commentaires ci-dessus l'ont également souligné. Ici, je vais montrer comment réinitialiser le nombre d'index des tables aussi.

            context.Database.ExecuteSqlCommand("delete from TableA");
            context.Database.ExecuteSqlCommand("delete from sqlite_sequence where name='TableA'");//resets the autoindex

            context.Database.ExecuteSqlCommand("delete from TableB");
            context.Database.ExecuteSqlCommand("delete from sqlite_sequence where name='TableB'");//resets the autoindex 

            context.Database.ExecuteSqlCommand("delete from TableC");
            context.Database.ExecuteSqlCommand("delete from sqlite_sequence where name='TableC'");//resets the autoindex 

Un point important est que si vous utilisez des clés étrangères dans vos tables, vous devez d'abord supprimer la table enfant avant la table parent, de sorte que la séquence (hiérarchie) des tables pendant la suppression est importante, sinon une exception SQLite peut se produire.

Remarque: var context = new YourContext ()

Matt Allen
la source
1

Cela fonctionne correctement dans EF 5:

YourEntityModel myEntities = new YourEntityModel();

var objCtx = ((IObjectContextAdapter)myEntities).ObjectContext;
objCtx.ExecuteStoreCommand("TRUNCATE TABLE [TableName]");
Hekmat
la source
1

Dans EFCore (la version que j'utilise est 3.1), vous pouvez utiliser ce qui suit pour supprimer toutes les lignes -

context.Database.ExecuteSqlRaw("TRUNCATE TABLE [TableName]");
bsod_
la source
0

Supprimez tous les enregistrements. Ne réinitialisez pas l'index primaire comme "tronquer".

/// <summary>
/// SET - DELETE all record by table - no truncate - return deleted records
/// </summary>
public static int setListDelAllMYTABLE()
{
    // INIT
    int retObj = 0;
    using (MYDBEntities ctx = new MYDBEntities())
    {
        // GET - all record
        var tempAllRecord = ctx.MYTABLE.ToList();
        // RESET
        ctx.MYTABLE.RemoveRange(tempAllRecord);
        // SET - final save
        retObj += ctx.SaveChanges();
    }
    // RET
    return retObj;
}
Roberto Mutti
la source
pourquoi voudriez-vous retirer tous les enregistrements pour les supprimer? extrêmement inefficace
jument
Parce que la performance n'était pas ma priorité. Il est basé sur la modularité, donc si vous voulez ajouter une condition where ou vérifier les données avant de les supprimer, vous pouvez. EF6 est l'outil le plus lent sur les E / S SQL, alors pourquoi utiliser EF6 si la performance était la priorité devrait être la question ..
Roberto Mutti
0

Dans mon code, je n'avais pas vraiment accès à l'objet Database, vous pouvez donc le faire sur le DbSet où vous pouvez également utiliser n'importe quel type de SQL. Cela se terminera en quelque sorte comme ceci:

var p = await _db.Persons.FromSql("truncate table Persons;select top 0 * from Persons").ToListAsync();
Thomas Koelle
la source
0

Si MVC, vous pouvez faire:

public async Task<IActionResult> DeleteAll()
{
    var list = await _context.YourClass.ToListAsync();
    _context.YourClass.RemoveRange(list);
    await _context.SaveChangesAsync();
    return RedirectToAction(nameof(Index));
}
Diego Venâncio
la source
0

Assurez-vous que lorsque vous essayez de supprimer le parent, tous les enfants seront mis en cascade lors de la suppression. Ou les enfants ont une clé étrangère nullable.

E. Radokhlebov
la source
0
var list = db.Discounts.ToList().Select(x => x as Discount);
foreach (var item in list)
{
    db.Discounts.Remove(item);
}
db.SaveChanges();
nima chapi
la source