Eliminación masiva en LINQ to Entities

82

¿Hay alguna forma de eliminar de forma masiva un grupo de objetos que coincidan con una consulta determinada en LINQ o LINQ-to-Entities? Las únicas referencias que puedo encontrar están desactualizadas, y parece una tontería iterar y eliminar manualmente todos los objetos que deseo eliminar.

Benjamín Pollack
fuente

Respuestas:

30

La pregunta es antigua (de antes de que existiera EF5). Para cualquiera que esté usando EF5, EntityFramework.Extended hace esto en un instante.

Shaul Behr
fuente
53

Hace un tiempo escribí una serie de blogs de 4 partes (Partes 1 , 2 , 3 y 4 ) que cubren la realización de actualizaciones masivas (con un comando) en Entity Framework.

Si bien el enfoque de esa serie fue la actualización, definitivamente podría usar los principios involucrados para eliminar.

Entonces debería poder escribir algo como esto:

var query = from c in ctx.Customers
            where c.SalesPerson.Email == "..."
            select c;

query.Delete();

Todo lo que necesita hacer es implementar el método de extensión Delete (). Consulte la serie de publicaciones para obtener sugerencias sobre cómo ...

Espero que esto ayude

Alex James
fuente
17
¡Sería bueno tener una muestra de código para esto aquí si alguien la tiene!
jocull
1
Se pueden encontrar un montón de métodos de extensión (incluida una eliminación por lotes) aquí: github.com/loresoft/EntityFramework.Extended
Soliah
1
Tenga cuidado con github.com/loresoft/EntityFramework.Extended tiene una dependencia en EF5, si usa Nuget Package Manager Console para instalarlo, lo hará sin pedirle que instale EF5
Paul Zahra
1
Realmente no ayuda - me gustaría ver un ejemplo funcional de un comando Eliminar y no "adivinar" cómo implementar el mío en el código de producción y posiblemente arruinar algo. -1
nuzzolilo
27
Hm ... entonces la respuesta a la pregunta es redirigir a 4 publicaciones de blog en lugar de presentar la respuesta específica a la audiencia.
DeepSpace101
41
    using (var context = new DatabaseEntities())
    {
        // delete existing records
        context.ExecuteStoreCommand("DELETE FROM YOURTABLE WHERE CustomerID = {0}", customerId);
    }
Vlad Bezden
fuente
3
+1 - Es bueno ver un ejemplo de código de cómo ejecutar código SQL usando EF
Carlos P
2
Me doy cuenta de que esta es probablemente la única forma de hacer esto, además de crear un procedimiento almacenado, pero esto se siente como una trampa =). Ahora que estoy usando esto, estoy tentado de usarlo en varios otros lugares para la peculiaridad de cicumvent EF jajaja, como complejas uniones izquierdas y grupos bys ..... :)
Losbear
+! ... cuando usa un DB, encontrará que la herramienta que desea es un destornillador .. EF es solo otro martillo.
gbjbaanb
2
Ligero inconveniente: ahora tiene un comando de base de datos que está desconectado del entorno de desarrollo. No está fuertemente tipado, por lo que un cambio en la base de datos para las columnas en este SQL no se resaltará en Visual Studio
Ian
7

Las respuestas que veo aquí son Linq a Sql

DeleteAllOnSubmit es parte de System.Data.Linq e ITable, que es Linq to Sql

Esto no se puede hacer con Entity Framework.

Habiendo dicho todo eso, todavía no tengo una solución, pero volveré a publicar cuando la tenga

Phil Strong
fuente
5

Para aquellos que usan EF6 y desean ejecutar una consulta SQL de fila para su eliminación:

using (var context = new DatabaseEntities())
{
    // delete existing records
    context.Database.ExecuteSqlCommand("DELETE FROM YOURTABLE WHERE CustomerID = @id", idParameter);
}
Uriil
fuente
1
Esto funcionó para mí en EF 5 pero tuve que usar @ p0 para el parámetro. Lo bueno es que proporciona una verificación de parámetros segura de tipos en el sql generado: así que en EF5, esto funcionaría: context.Database.ExecuteSqlCommand ("ELIMINAR DE YOURTABLE WHERE CustomerID = @ p0", idParameter); \ @ p1 para el próximo parámetro, etc ...
Nathan Prather
3

RemoveRange se introdujo en EF6, puede eliminar una lista de objetos. Muy facil.

var origins= (from po in db.PermitOrigins where po.PermitID == thisPermit.PermitID select po).ToList();
db.PermitOrigins.RemoveRange(origins);
db.SaveChanges();
Jarrette
fuente
1
Si bien este fragmento de código puede resolver la pregunta, incluir una explicación realmente ayuda a mejorar la calidad de su publicación. Recuerde que está respondiendo la pregunta para los lectores en el futuro, y es posible que esas personas no conozcan los motivos de su sugerencia de código.
DimaSan
2

Sé del método DeleteAllOnSubmit de cualquier contexto de datos que eliminará todos los registros en la consulta. Debe haber alguna optimización subyacente ya que se están eliminando muchos objetos. Aunque no estoy seguro.

Hemant
fuente
3
En realidad, no se está realizando ninguna optimización. El SQL generado enumera todos los objetos que coinciden con su consulta, luego los itera manualmente para eliminarlos. Por supuesto, la iteración ocurre en la base de datos, en lugar de en su código, pero todavía está creando innecesariamente un conjunto de resultados simplemente para eliminar su contenido, aún mucho peor que un simple "DELETE FROM table WHERE foo = bar", que construye ningún conjunto de resultados y cubre la tabla solo una vez.
Benjamin Pollack
2

No estoy seguro de cuán eficiente sería, pero podrías probar algo como esto:

// deletes all "People" with the name "Joe"
var mypeople = from p in myDataContext.People
               where p.Name == "Joe";
               select p;
myDataContext.People.DeleteAllOnSubmit(mypeople);
myDataContext.SubmitChanges();
Scott Anderson
fuente
1
Eso todavía termina iterando sobre todos los elementos que coinciden con la consulta; simplemente lo hace en la base de datos, en lugar de en su código. Más eficiente, pero aún lejos de ser una solución ideal.
Benjamin Pollack
3
La única otra forma en que podría pensar en hacerlo, entonces, sería hacer myDataContext.ExecuteCommand ("DELETE ...") ;. Lejos de ser ideal, también, pero funcionaría.
Scott Anderson
1

PUEDE escribir un proceso almacenado que haga la eliminación y llamarlo desde LINQ. Es probable que una eliminación basada en conjuntos sea más rápida en general, pero si afecta a demasiados registros, podría causar problemas de bloqueo y es posible que necesite un híbrido de recorrer conjuntos de registros (tal vez 2000 a la vez, el tamaño depende del diseño de su base de datos, pero 2000 es un lugar de inicio si encuentra que el delte basado en conjuntos tarda tanto que está afectando a otro uso de la tabla) para realizar la eliminación.

HLGEM
fuente
1

La eliminación de datos a través de Entity Framework se basa en el uso del método DeleteObject. Puede llamar a este método en EntityCollection para la clase de entidad que desea eliminar o en el ObjectContext derivado. Aquí hay un ejemplo simple:

NorthwindEntities db = new NorthwindEntities();

IEnumerable<Order_Detail> ods = from o in db.Order_Details
                                where o.OrderID == 12345                                    
                                select o;

foreach (Order_Detail od in ods) 
    db.Order_Details.DeleteObject(od);

db.SaveChanges();
Amín
fuente
Sin embargo, eso no es "Eliminación masiva".
nuzzolilo
1

En este ejemplo, obtengo los registros para eliminar, y uno por uno los adjunto al conjunto de resultados y luego solicito que se eliminen. Entonces tengo 1 guardar cambios.

    using (BillingDB db = new BillingDB())
    {
      var recordsToDelete = (from i in db.sales_order_item
                  where i.sales_order_id == shoppingCartId
                  select i).ToList<sales_order_item>();

      if(recordsToDelete.Count > 0)
      {
        foreach (var deleteSalesOrderItem in recordsToDelete)
        {                  
            db.sales_order_item.Attach(deleteSalesOrderItem);
            db.sales_order_item.Remove(deleteSalesOrderItem);                  
        }
        db.SaveChanges();
      } 
    }
Demodave
fuente
1
 context.Entity.Where(p => p.col== id)
               .ToList().ForEach(p => db.Entity.DeleteObject(p));

este es el método más rápido para eliminar el registro de la base de datos usando EF

Anurag Deokar
fuente
0

Haría algo como:

var recordsToDelete = (from c in db.Candidates_T where c.MyField == null select c).ToList<Candidates_T>();
if(recordsToDelete.Count > 0)
{
    foreach(var record in recordsToDelete)
    {
        db.Candidate_T.DeleteObject(record);
        db.SaveChanges();
    }
}   

No creo que haya una manera de hacerlo sin un bucle, ya que Entity Framework funciona con Entidades y la mayoría de las veces, esto significa colección de objetos.

G Jeny Ramírez
fuente
También puedes compartir lo que hiciste. Gracias.
G Jeny Ramirez
@G Jeny Ramirez Agregué mi solución.
Demodave
2
@GJennyRamirez también en su ejemplo está guardando Cambios varias veces, lo que creo que puede extraer del bucle foreach y ejecutarlo una vez
Demodave