Zbiorcze usuwanie w LINQ to Entities

82

Czy istnieje sposób, aby zbiorczo usunąć kilka obiektów pasujących do danego zapytania w LINQ lub LINQ-to-Entities? Jedyne odniesienia, które mogę znaleźć, są nieaktualne i wydaje mi się głupie powtarzanie i ręczne usuwanie wszystkich obiektów, które chcę usunąć.

Benjamin Pollack
źródło

Odpowiedzi:

53

Jakiś czas temu napisałem czteroczęściową serię blogów (części 1 , 2 , 3 i 4 ) obejmującą wykonywanie zbiorczych aktualizacji (za pomocą jednego polecenia) w Entity Framework.

Chociaż głównym celem tej serii była aktualizacja, zdecydowanie można było użyć zasad, aby usunąć.

Więc powinieneś móc napisać coś takiego:

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

query.Delete();

Wszystko, co musisz zrobić, to zaimplementować metodę rozszerzenia Delete (). Zobacz serię postów, aby uzyskać wskazówki, jak ...

Mam nadzieję że to pomoże

Alex James
źródło
17
Byłoby miło mieć tutaj przykładowy kod, jeśli ktoś go ma!
wesoły
1
Kilka metod rozszerzających (w tym usuwanie wsadowe) można znaleźć tutaj: github.com/loresoft/EntityFramework.Extended
Soliah
1
Uważaj na github.com/loresoft/EntityFramework.Extended ma zależność od EF5, jeśli używasz konsoli menedżera pakietów Nuget, aby go zainstalować, bez pytania o zainstalowanie EF5
Paul Zahra
1
To naprawdę nie pomaga - chciałbym zobaczyć działający przykład polecenia Usuń, a nie „zgadywać”, jak zaimplementować swój własny w kodzie produkcyjnym i ewentualnie coś schrzanić. -1
nuzzolilo
27
Hm ... więc odpowiedzią na pytanie jest przekierowanie do 4 postów na blogu zamiast przedstawiania konkretnej odpowiedzi publiczności.
DeepSpace101
41
    using (var context = new DatabaseEntities())
    {
        // delete existing records
        context.ExecuteStoreCommand("DELETE FROM YOURTABLE WHERE CustomerID = {0}", customerId);
    }
Vlad Bezden
źródło
3
+1 - Miło zobaczyć przykładowy kod pokazujący, jak wykonać kod SQL za pomocą EF
Carlos P
2
Zdaję sobie sprawę, że jest to prawdopodobnie jedyny sposób, aby to zrobić, poza utworzeniem procedury składowanej, ale wydaje się to być oszustwem =). Teraz, kiedy tego używam, kusi mnie, aby użyć go w kilku innych miejscach, aby cicumventować dziwaczność EF lol - takie jak złożone lewe łączenia i grupy bys ..... :)
Losbear
+! ... używając DB, przekonasz się, że pożądane narzędzie to śrubokręt. EF to po prostu kolejny młotek.
gbjbaanb
2
Niewielki minus: masz teraz polecenie bazy danych, które jest odłączone od środowiska programistycznego. Nie jest silnie wpisany, więc zmiana w bazie danych dla kolumn w tym SQL nie zostanie wyróżniona w programie Visual Studio
Ian
7

Odpowiedzi, które widzę tutaj, to Linq do Sql

DeleteAllOnSubmit jest częścią System.Data.Linq i ITable, czyli Linq to Sql

Nie można tego zrobić za pomocą Entity Framework.

Powiedziawszy to wszystko, nie mam jeszcze rozwiązania, ale wrócę, gdy to zrobię

Phil Strong
źródło
5

Dla tych, którzy używają EF6 i chcą wykonać zapytanie SQL wiersza w celu usunięcia:

using (var context = new DatabaseEntities())
{
    // delete existing records
    context.Database.ExecuteSqlCommand("DELETE FROM YOURTABLE WHERE CustomerID = @id", idParameter);
}
Uriil
źródło
1
To działało dla mnie w EF 5, ale musiałem użyć @ p0 jako parametru. Fajne jest to, że zapewnia sprawdzanie bezpiecznych parametrów typu w wygenerowanym sql: więc w EF5 zadziałałoby to: context.Database.ExecuteSqlCommand ("USUŃ Z TWOJEGO TABELI, GDZIE ID_Klienta = @ p0", parametr idP); \ @ p1 dla następnego parametru itp ...
Nathan Prather
3

RemoveRange został wprowadzony w EF6, może usunąć listę obiektów. Super łatwe.

var origins= (from po in db.PermitOrigins where po.PermitID == thisPermit.PermitID select po).ToList();
db.PermitOrigins.RemoveRange(origins);
db.SaveChanges();
Jarrette
źródło
1
Chociaż ten fragment kodu może rozwiązać problem, dołączenie wyjaśnienia naprawdę pomaga poprawić jakość Twojego posta. Pamiętaj, że odpowiadasz na pytanie do czytelników w przyszłości, a osoby te mogą nie znać powodów, dla których zaproponowałeś kod.
DimaSan
2

Znam metodę DeleteAllOnSubmit dowolnego kontekstu danych, która usunie wszystkie rekordy w zapytaniu. Musi istnieć jakaś optymalizacja, ponieważ wiele obiektów jest usuwanych. Nie jestem jednak pewien.

Hemant
źródło
3
W rzeczywistości nie jest wykonywana żadna optymalizacja. Wygenerowany kod SQL wylicza wszystkie obiekty pasujące do zapytania, a następnie ręcznie je iteruje, aby je usunąć. To prawda, iteracja odbywa się w bazie danych, a nie w kodzie, ale nadal niepotrzebnie tworzysz zestaw wyników tylko po to, aby usunąć jego zawartość - wciąż o wiele gorsze niż zwykłe „DELETE FROM table WHERE foo = bar”, które brak zestawu wyników i obejmuje tabelę tylko raz.
Benjamin Pollack
2

Nie jestem pewien, jak wydajne byłoby to, ale możesz spróbować czegoś takiego:

// 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
źródło
1
To wciąż kończy się iteracją po wszystkich elementach pasujących do zapytania; robi to po prostu w bazie danych, a nie w kodzie. Bardziej wydajne, ale wciąż dalekie od idealnego rozwiązania.
Benjamin Pollack
3
Jedynym innym sposobem, w jaki mógłbym to zrobić, byłoby wykonanie myDataContext.ExecuteCommand ("DELETE ...") ;. Daleko od ideału, ale to zadziała.
Scott Anderson
1

Mógłbyś napisać przechowywany proces, który usuwa i wywołuje go z LINQ. Usuwanie oparte na zestawie jest prawdopodobnie ogólnie szybsze, ale jeśli wpływa na zbyt wiele rekordów, możesz spowodować problemy z blokowaniem i możesz potrzebować hybrydy przechodzenia przez zestawy rekordów (może 2000 naraz, rozmiar zależy od projektu bazy danych, ale 2000 to jeśli okaże się, że usuwanie oparte na zestawie zajmuje tak dużo czasu, że wpływa na inne użycie tabeli), aby wykonać usunięcie.

HLGEM
źródło
1

Usuwanie danych za pośrednictwem Entity Framework polega na użyciu metody DeleteObject. Możesz wywołać tę metodę w EntityCollection dla klasy jednostki, którą chcesz usunąć, lub w pochodnym ObjectContext. Oto prosty przykład:

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();
Jestem w
źródło
Nie jest to jednak „usuwanie zbiorcze”.
nuzzolilo,
1

W tym przykładzie otrzymuję rekordy do usunięcia i dołączam je pojedynczo do zestawu wyników, a następnie żądam ich usunięcia. Wtedy mam 1 zapisz zmiany.

    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
źródło
1
 context.Entity.Where(p => p.col== id)
               .ToList().ForEach(p => db.Entity.DeleteObject(p));

jest to najszybsza metoda usuwania rekordu z bazy danych przy użyciu EF

Anurag Deokar
źródło
0

Zrobiłbym coś takiego:

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

Nie sądzę, aby można to zrobić bez pętli, ponieważ Entity Framework działa z Entity i przez większość czasu oznacza to kolekcję obiektów.

G Jeny Ramirez
źródło
Możesz też podzielić się tym, co zrobiłeś. Dzięki.
G Jeny Ramirez
@G Jeny Ramirez Dodano moje rozwiązanie.
Demodave
2
@GJennyRamirez również w twoim przykładzie zapisujesz zmiany wiele razy, które myślę, że możesz wyciągnąć to z pętli foreach i wykonać raz
Demodave