Marco de la entidad. Eliminar todas las filas en la tabla

280

¿Cómo puedo eliminar rápidamente todas las filas de la tabla con Entity Framework?

Actualmente estoy usando:

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

Sin embargo, lleva mucho tiempo ejecutarlo.

¿Hay alguna alternativa?

Zhenia
fuente
22
Al leer las respuestas, me pregunto por qué ninguno de estos TRUNCATEadeptos se preocupa por las restricciones de claves externas.
Gert Arnold
2
Estoy un poco sorprendido de cómo las respuestas aquí dan por sentado que todos están usando Microsoft SQL Server, a pesar de que el soporte para otras bases de datos en Entity Framework se remonta hasta donde puedo encontrar información y ciertamente es anterior a esta pregunta por varios años. . Consejo: si una respuesta cita nombres de tablas en declaraciones SQL con corchetes (como:) [TableName], no es portátil.
Mark Amery

Respuestas:

293

Para aquellos que están buscando en Google esto y terminaron aquí como yo, así es como lo haces actualmente en EF5 y EF6:

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

Suponiendo que el contexto es un System.Data.Entity.DbContext

Ron Sijm
fuente
20
Para su información, para usar TRUNCATE, el usuario debe tener permiso ALTER en la tabla. ( stackoverflow.com/questions/4735038/… )
Alex
77
@Alex acaba de perder una tonelada de tiempo en el error "No se puede encontrar el objeto MyTable porque no existe o no tiene permisos". por esa razón exacta: los permisos ALTER rara vez se otorgan a las aplicaciones EF, y el mensaje de error realmente te envía en una loca persecución.
Chris Moschini
8
Tuve problemas ya que mi tabla era parte de una relación de clave externa, a pesar de que era la tabla de hoja en esa relación. Terminé usando context.Database.ExecuteSqlCommand ("DELETE FROM [Interests]"); en cambio
Dan Csharpster
2
Tenga en cuenta que si bien los [escapes aquí son específicos de SQL Server, el TRUNCATEcomando no lo es: es parte de ANSI SQL y, por lo tanto, funcionará en la mayoría de los dialectos de SQL (aunque no SQLite).
Mark Amery
207

Advertencia: lo siguiente solo es adecuado para tablas pequeñas (piense <1000 filas)

Aquí hay una solución que usa el marco de entidad (no SQL) para eliminar las filas, por lo que no es específico del motor SQL (R / DBM).

Esto supone que está haciendo esto para probar o alguna situación similar. Ya sea

  • La cantidad de datos es pequeña o
  • El rendimiento no importa

Simplemente llame:

VotingContext.Votes.RemoveRange(VotingContext.Votes);

Suponiendo este contexto:

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;}
}

Para el código más ordenado, puede declarar el siguiente método de extensión:

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

Entonces lo anterior se convierte en:

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

Recientemente utilicé este enfoque para limpiar mi base de datos de prueba para cada ejecución de caso de prueba (obviamente es más rápido que recrear la base de datos desde cero cada vez, aunque no verifiqué la forma de los comandos de eliminación que se generaron).


¿Por qué puede ser lento?

  1. EF obtendrá TODAS las filas (VotingContext.Votes)
  2. y luego usarán sus ID (no estoy seguro exactamente cómo, no importa), para eliminarlos.

Entonces, si está trabajando con una gran cantidad de datos, matará el proceso del servidor SQL (consumirá toda la memoria) y lo mismo para el proceso IIS, ya que EF almacenará en caché todos los datos de la misma manera que el servidor SQL. No use este si su tabla contiene una gran cantidad de datos.

Ahmed Alejo
fuente
1
Gran respuesta, ¡aceleré mi código de borrar todas las filas por un factor de 10! Tenga en cuenta que tuve que cambiar el nombre del método de extensión estática Clear () a algo como ClearDbSet () ya que ya tenía otro método de extensión estática Clear () definido en otra parte de mi proyecto.
dodgy_coder
1
El cambio de nombre de @dodgy_coder no es necesario por la razón que proporcionó, ya que el método de extensión es para DbSet, IDbSet sy no IEnumerable, IList, ICollection, ICache o cualquier otra interfaz que se requeriría "Clear". la preferencia por el método de extensión es el tipo en el que se definen. pero si eso te parece más claro y no suena redundante, ¡Genial !. ¡Me alegra que ayude al rendimiento! ¡Salud!
Ahmed Alejo
Me alegra que hayas indicado solo para mesas pequeñas. Espero que la gente entienda la importancia de tu explicación. como esta forma es azúcar sintáctica, la forma correcta es la que sugirió Ron Sijm. porque no carga los datos antes de eliminarlos. aplausos por mostrar y explicar esta forma de hacerlo.
yedevtxt
3
Esto no restablece la clave de identidad. Entonces, si borra 10 registros, el siguiente seguirá siendo 11.
MDave
89

Usar el TRUNCATE TABLEcomando de SQL será el más rápido ya que opera en la tabla y no en filas individuales.

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

Suponiendo que dataDbes un DbContext(no un ObjectContext), puede envolverlo y usar el método de esta manera:

var objCtx = ((System.Data.Entity.Infrastructure.IObjectContextAdapter)dataDb).ObjectContext;
objCtx.ExecuteStoreCommand("TRUNCATE TABLE [Table]");
Rudi Visser
fuente
Si se obtiene un error de permisos cuando intenta esto, el cambio simplemente TRUNCATE TABLEaDELETE FROM
codemonkey
1
@codeMonkey solo tenga en cuenta que estas son operaciones diferentes, pero tendrán (casi) el mismo efecto neto 👍
Rudi Visser
3
Pero DELETE no restablece la semilla IDENTITY. Esto podría ser problemático en ciertas situaciones.
Steve
43
var all = from c in dataDb.Table select c;
dataDb.Table.RemoveRange(all);
dataDb.SaveChanges();
usuario3328890
fuente
11
Esto no debe usarse porque ejecuta una selección completa y una eliminación posterior en lugar de solo una eliminación. Desde el punto de vista del rendimiento en tiempo, ¡este es un gran NO!
HellBaby
2
@HellBaby A menos que se llame raramente y, por lo tanto, el rendimiento sea bastante irrelevante.
Alex
66
Incluso si rara vez se llama esto es malo. Una tabla con solo 3000 entradas podría tomar más de 30 segundos debido a lo lento que es el seguimiento de cambios EF.
Ligero
1
Esto solo es adecuado para mesas pequeñas (<1000 filas)
Amir Touitou
Perfecto para mi base de datos en memoria en mis pruebas unitarias :)
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);
}

o

using (var context = new DataDb())
{
     context.Database.ExecuteSqlCommand("TRUNCATE TABLE [TableName]");
}
Manish Mishra
fuente
1
pero cuando lo escribo "query.Delete ();" - "Eliminar" no se reconoce
Zhenia
1
agregar referencia de System.Data.Entity y EntityFrameWork en su proyecto actual
Manish Mishra
¿Qué método de extensión es Eliminar?
Ahmed Alejo
Por lo que yo sé que no es un método de extensión Deletede IQueryable- supongo Manish estaba usando algo así como EntityFramework.Extended: github.com/loresoft/EntityFramework.Extended
nula
He editado mi respuesta, antes era engañosa. @ null, tienes razón, esta .Deleteera una extensión personalizada y en el momento de publicar la respuesta primero, se olvidó por completo de mencionar la definición de esta costumbre .Delete. :)
Manish Mishra
23

Puedes hacerlo sin Foreach

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

Esto eliminará todas las filas

Omid Farvid
fuente
¿Truncará los elementos de propiedad de navegación?
John Deer
19

Esto evita usar cualquier sql

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

Me encontré con esta pregunta cuando tuve que lidiar con un caso particular: la actualización completa del contenido en una tabla de "hojas" (sin que los FK lo señalaran). Esto implicó la eliminación de todas las filas y la colocación de información de filas nuevas y debe hacerse transaccionalmente (no quiero terminar con una tabla vacía, si las inserciones fallan por alguna razón).

He intentado el public static void Clear<T>(this DbSet<T> dbSet)enfoque, pero no se insertan nuevas filas. Otra desventaja es que todo el proceso es lento, ya que las filas se eliminan una por una.

Entonces, cambié a TRUNCATEenfoque, ya que es mucho más rápido y también es ROLLBACKable . También restablece la identidad.

Ejemplo usando patrón de repositorio:

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();

Por supuesto, como ya se mencionó, esta solución no puede ser utilizada por tablas referenciadas por claves foráneas (falla TRUNCATE).

Alexei
fuente
Dos comentarios: 1. El nombre de la tabla debe estar entre [...]. Una de mis clases / tablas se llama "Transacción", que es una palabra clave SQL. 2. Si el objetivo es borrar todas las tablas en una base de datos para pruebas unitarias, los problemas con restricciones de clave externa se pueden solucionar fácilmente ordenando las tablas para procesarlas de una manera que las tablas secundarias se truncan antes que las tablas principales.
Christoph
@ Christoph - 1. Sí, eso es cierto. Me perdí eso, porque siempre nombro tablas para evitar palabras clave, ya que podría generar problemas. 2. Si no recuerdo mal, las tablas a las que hacen referencia los FK no se pueden truncar (los arroja SQL Server Cannot truncate table because it is being referenced by a FOREIGN KEY constraint), incluso si están vacías, por lo que los FK deben descartarse y recrearse para poder utilizarlos de TRUNCATEtodos modos.
Alexei
5

Si

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

causas

No se puede truncar la tabla 'MyTable' porque está siendo referenciada por una restricción FOREIGN KEY.

Yo uso esto :

      using(var db = new MyDbContext())
               {
                   await db.Database.ExecuteSqlCommandAsync(@"DELETE FROM MyTable WHERE ID != -1");
               }
Zakos
fuente
1
Si tiene restricciones de clave externa: (1) SQL podría simplificarse a "BORRAR DE MyTable". (2) Esto no restablecerá el contador de Id si lo ha configurado en autoincremento (truncar sí).
RGH
5
var data = (from n in db.users select n);
db.users.RemoveRange(data);
db.SaveChanges();
DBB
fuente
4

Si desea borrar toda su base de datos.

Debido a las restricciones de clave externa, importa qué secuencia se truncan las tablas. Esta es una forma de aplicar fuerza bruta a esta secuencia.

    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();
        }
    }

uso:

ClearDatabase<ApplicationDbContext>();

recuerde reinstalar su DbContext después de esto.

Kristian Nissen
fuente
2

Lo siguiente funciona en la base de datos SQLite (usando Entity Framework)

Parece que la forma más rápida de borrar todas las tablas de db es usando 'context.Database.ExecuteSqlCommand ("some SQL")', como también se resaltaron algunos comentarios anteriores. Aquí voy a mostrar cómo restablecer el recuento de 'índice' de tablas también.

            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 punto importante es que si usa claves foráneas en sus tablas, primero debe eliminar la tabla secundaria antes que la tabla primaria, por lo que la secuencia (jerarquía) de las tablas durante la eliminación es importante, de lo contrario puede ocurrir una excepción SQLite.

Nota: var context = new YourContext ()

Matt Allen
fuente
1

Esto funciona correctamente en EF 5:

YourEntityModel myEntities = new YourEntityModel();

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

En EFCore (la versión que estoy usando es la 3.1) puede usar lo siguiente para eliminar todas las filas:

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

Eliminar todos los registros. No restablezca el índice primario como "truncar".

/// <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
fuente
¿por qué sacarías todos los registros para eliminarlos? extremadamente ineficiente
yegua
Porque el rendimiento no era mi prioridad. Se basa en la modularidad, por lo que si desea agregar una condición where o verificar los datos antes de eliminarlos, puede hacerlo. EF6 es la herramienta más lenta sobre E / S SQL, entonces, ¿por qué usar EF6 si el rendimiento era la prioridad? Debería ser la pregunta ...
Roberto Mutti
0

En mi código, realmente no tenía un buen acceso al objeto de la base de datos, por lo que puede hacerlo en el DbSet donde también se le permite usar cualquier tipo de sql. De alguna manera terminará así:

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

Si MVC, puedes hacer:

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
fuente
0

Asegúrese de que cuando intente eliminar el elemento primario, todos los elementos secundarios se eliminarán en cascada. O los niños tienen clave foránea anulable.

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