Comment puis-je supprimer rapidement toutes les lignes d'un 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, l'exécution prend beaucoup de temps.
Y a-t-il des alternatives?
Pour ceux qui googuent cela et se sont retrouvés ici comme moi, voici comment 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
Avertissement: Ce qui suit ne convient que pour les petites tables (pensez à moins de 1000 lignes)
Voici une solution qui utilise la structure d’entité (pas SQL) pour supprimer les lignes, elle n’est donc pas spécifique au moteur SQL (R/DBM).
Cela suppose que vous le faites pour des tests ou une situation similaire. Soit
Appelez simplement:
VotingContext.Votes.RemoveRange(VotingContext.Votes);
En supposant que 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 le 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);
}
}
Alors 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 test (c'est évidemment plus rapide que de recréer la base de données à partir de zéro à chaque fois, bien que je n'ai pas vérifié le formulaire des commandes de suppression générées).
Pourquoi cela peut-il être lent?
Donc, si vous travaillez avec une quantité importante de données, le processus du serveur SQL sera tué (il consommera toute la mémoire). Même chose pour le processus IIS, puisque EF mettra en cache toutes les données de la même manière que Serveur SQL. N'utilisez pas celui-ci si votre table contient une quantité importante de données.
L'utilisation de la commande TRUNCATE TABLE
de SQL est la plus rapide car elle opère sur la table et non sur des lignes individuelles.
_dataDb.ExecuteStoreCommand("TRUNCATE TABLE [Table]");
_
En supposant que dataDb
est un DbContext
(pas un ObjectContext
), vous pouvez l'envelopper et utiliser la méthode comme ceci:
_var objCtx = ((System.Data.Entity.Infrastructure.IObjectContextAdapter)dataDb).ObjectContext;
objCtx.ExecuteStoreCommand("TRUNCATE TABLE [Table]");
_
var all = from c in dataDb.Table select c;
dataDb.Table.RemoveRange(all);
dataDb.SaveChanges();
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]");
}
Cela évite d'utiliser n'importe quel sql
using (var context = new MyDbContext())
{
var itemsToDelete = context.Set<MyTable>();
context.MyTables.RemoveRange(itemsToDelete);
context.SaveChanges();
}
Vous pouvez le faire sans Foreach
dataDB.Table.RemoveRange(dataDB.Table);
dataDB.SaveChanges();
Cela supprimera toutes les lignes
Je suis tombé sur cette question lorsque j'ai dû traiter un cas particulier: la mise à jour complète du contenu dans un tableau "feuille" (aucun FK ne le pointe). Cela impliquait de supprimer toutes les lignes et de mettre de nouvelles informations sur les lignes, et cela devrait se faire 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é l'approche public static void Clear<T>(this DbSet<T> dbSet)
, mais les 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.
Donc, je suis passé à l’approche TRUNCATE
, car c’est beaucoup plus rapide et c’est aussi 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 entendu, comme déjà mentionné, cette solution ne peut pas être utilisée par les tables référencées par des clés étrangères (TRUNCATE échoue).
si
using(var db = new MyDbContext())
{
await db.Database.ExecuteSqlCommandAsync(@"TRUNCATE TABLE MyTable"););
}
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");
}
Si vous souhaitez effacer toute votre base de données.
En raison des contraintes de clé étrangère, l'ordre dans lequel les tables sont tronquées importe. C'est un moyen de forcer 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établir votre DbContext après cela.
var data = (from n in db.users select n);
db.users.RemoveRange(data);
db.SaveChanges();
Cela fonctionne correctement dans EF 5:
YourEntityModel myEntities = new YourEntityModel();
var objCtx = ((IObjectContextAdapter)myEntities).ObjectContext;
objCtx.ExecuteStoreCommand("TRUNCATE TABLE [TableName]");
Supprimer 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;
}