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?


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


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.


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]");

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

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]");
}

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


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

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).


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

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

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.


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


1

Esto funciona correctamente en EF 5:

YourEntityModel myEntities = new YourEntityModel();

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

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]");

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

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

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

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.


0
var list = db.Discounts.ToList().Select(x => x as Discount);
foreach (var item in list)
{
    db.Discounts.Remove(item);
}
db.SaveChanges();
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.