Jak usunąć wiele wierszy w ramach encji (bez foreach)

Usuwam kilka elementów z tabeli za pomocą struktury encji. Nie ma obiektu klucza obcego / rodzica, więc nie mogę sobie z tym poradzić za pomocą OnDeleteCascade.

Teraz to robię:

var widgets = context.Widgets
    .Where(w => w.WidgetId == widgetId);

foreach (Widget widget in widgets)
{
    context.Widgets.DeleteObject(widget);
}
context.SaveChanges();
Działa, ale foreach mnie wkurza. Używam EF4, ale nie chcę wykonywać SQL. Chcę się tylko upewnić, że nic mi nie umknie. Mogę to streścić metodą rozszerzenia lub helperem, ale gdzieś jeszcze będziemy robić foreach, prawda?
Author: Jon Galloway, 2010-03-26

19 answers

Jeśli nie chcesz wykonywać SQL bezpośrednio wywołanie DeleteObject w pętli jest najlepszym, co możesz zrobić dzisiaj.

Jednak możesz wykonać SQL i nadal uczynić go całkowicie ogólnym za pomocą metody rozszerzenia, używając podejścia, które opisuję tutaj .

Chociaż ta odpowiedź była za 3,5. Dla 4.0 prawdopodobnie użyłbym nowego ExecuteStoreCommand API pod maską, zamiast spadać do StoreConnection.

 46
Author: Alex James,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:26:34

EntityFramework 6 sprawił, że stało się to nieco łatwiejsze dzięki .RemoveRange().

Przykład:

db.People.RemoveRange(db.People.Where(x => x.State == "CA"));
db.SaveChanges();
 554
Author: Kyle,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-18 22:00:22
To jest tak dobre, jak tylko się da, prawda? Mogę go streścić z rozszerzeniem metody lub pomocnika, ale gdzieś jeszcze będziemy robić foreach, prawda?

NO tak, tyle że można zrobić z tego dwuliniowy:

context.Widgets.Where(w => w.WidgetId == widgetId)
               .ToList().ForEach(context.Widgets.DeleteObject);
context.SaveChanges();
 76
Author: Klaus Byskov Pedersen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-18 22:40:50
using (var context = new DatabaseEntities())
{
    context.ExecuteStoreCommand("DELETE FROM YOURTABLE WHERE CustomerID = {0}", customerId);
}
 64
Author: Vlad Bezden,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-07 18:10:10

Wiem, że jest dość późno, ale w przypadku, gdy ktoś potrzebuje prostego rozwiązania, fajne jest to, że możesz dodać do niego klauzulę where:

        public static void DeleteWhere<T>(this DbContext db, Expression<Func<T, bool>> filter) where T : class
        {
            string selectSql = db.Set<T>().Where(filter).ToString();
            string fromWhere = selectSql.Substring(selectSql.IndexOf("FROM"));
            string deleteSql = "DELETE [Extent1] " + fromWhere;
            db.Database.ExecuteSqlCommand(deleteSql);
        }

Uwaga: właśnie testowałem z MSSQL2008.

Aktualizacja: Powyższe rozwiązanie nie zadziała, gdy EF wygeneruje polecenie sql z parametrami , więc oto aktualizacja dla EF5 :

        public static void DeleteWhere<T>(this DbContext db, Expression<Func<T, bool>> filter) where T : class
        {
            var query = db.Set<T>().Where(filter);

            string selectSql = query.ToString();
            string deleteSql = "DELETE [Extent1] " + selectSql.Substring(selectSql.IndexOf("FROM"));

            var internalQuery = query.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance).Where(field => field.Name == "_internalQuery").Select(field => field.GetValue(query)).First();
            var objectQuery = internalQuery.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance).Where(field => field.Name == "_objectQuery").Select(field => field.GetValue(internalQuery)).First() as ObjectQuery;
            var parameters = objectQuery.Parameters.Select(p => new SqlParameter(p.Name, p.Value)).ToArray();

            db.Database.ExecuteSqlCommand(deleteSql, parameters);
        }
Wymaga odrobiny refleksji, ale działa dobrze.
 37
Author: Thanh Nguyen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-30 15:12:05

Dla każdego, kto używa EF5, można użyć następującej biblioteki rozszerzeń: https://github.com/loresoft/EntityFramework.Extended

context.Widgets.Delete(w => w.WidgetId == widgetId);
 28
Author: Marcelo Mason,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-14 14:40:38

EF 6.1

public void DeleteWhere<TEntity>(Expression<Func<TEntity, bool>> predicate = null) 
where TEntity : class
{
    var dbSet = context.Set<TEntity>();
    if (predicate != null)
        dbSet.RemoveRange(dbSet.Where(predicate));
    else
        dbSet.RemoveRange(dbSet);

    context.SaveChanges();
} 

Użycie:

// Delete where condition is met.
DeleteWhere<MyEntity>(d => d.Name == "Something");

Or:

// delete all from entity
DeleteWhere<MyEntity>();
 10
Author: jzm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-14 13:19:36

Nadal wydaje się Szalone, że trzeba coś wyciągnąć z serwera tylko po to, aby to usunąć, ale przynajmniej odzyskanie tylko identyfikatorów jest o wiele szczuplejsze niż ściąganie pełnych encji:

var ids = from w in context.Widgets where w.WidgetId == widgetId select w.Id;
context.Widgets.RemoveRange(from id in ids.AsEnumerable() select new Widget { Id = id });
 8
Author: Edward Brey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-04 03:51:53

Dla EF 4.1,

var objectContext = (myEntities as IObjectContextAdapter).ObjectContext;
objectContext.ExecuteStoreCommand("delete from [myTable];");
 5
Author: Amit Pawar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-07 10:50:31

Najszybszym sposobem usunięcia jest użycie procedury składowanej. Wolę procedury przechowywane w projekcie bazy danych niż dynamiczny SQL, ponieważ zmiany nazw będą obsługiwane poprawnie i będą miały błędy kompilatora. Dynamiczny SQL może odnosić się do tabel, które zostały usunięte/przemianowane, powodując błędy w czasie wykonywania.

W tym przykładzie mam dwie tabele List i ListItems. Potrzebuję szybkiego sposobu na usunięcie wszystkich list z danej listy.

CREATE TABLE [act].[Lists]
(
    [Id] INT NOT NULL PRIMARY KEY IDENTITY, 
    [Name] NVARCHAR(50) NOT NULL
)
GO
CREATE UNIQUE INDEX [IU_Name] ON [act].[Lists] ([Name])
GO
CREATE TABLE [act].[ListItems]
(
    [Id] INT NOT NULL IDENTITY, 
    [ListId] INT NOT NULL, 
    [Item] NVARCHAR(100) NOT NULL, 
    CONSTRAINT PK_ListItems_Id PRIMARY KEY NONCLUSTERED (Id),
    CONSTRAINT [FK_ListItems_Lists] FOREIGN KEY ([ListId]) REFERENCES [act].[Lists]([Id]) ON DELETE CASCADE
)
go
CREATE UNIQUE CLUSTERED INDEX IX_ListItems_Item 
ON [act].[ListItems] ([ListId], [Item]); 
GO

CREATE PROCEDURE [act].[DeleteAllItemsInList]
    @listId int
AS
    DELETE FROM act.ListItems where ListId = @listId
RETURN 0

Teraz ciekawa część usuwania elementów i aktualizacji Entity framework z wykorzystaniem rozszerzenia.

public static class ListExtension
{
    public static void DeleteAllListItems(this List list, ActDbContext db)
    {
        if (list.Id > 0)
        {
            var listIdParameter = new SqlParameter("ListId", list.Id);
            db.Database.ExecuteSqlCommand("[act].[DeleteAllItemsInList] @ListId", listIdParameter);
        }
        foreach (var listItem in list.ListItems.ToList())
        {
            db.Entry(listItem).State = EntityState.Detached;
        }
    }
}

Główny kod może teraz używać go jako

[TestMethod]
public void DeleteAllItemsInListAfterSavingToDatabase()
{
    using (var db = new ActDbContext())
    {
        var listName = "TestList";
        // Clean up
        var listInDb = db.Lists.Where(r => r.Name == listName).FirstOrDefault();
        if (listInDb != null)
        {
            db.Lists.Remove(listInDb);
            db.SaveChanges();
        }

        // Test
        var list = new List() { Name = listName };
        list.ListItems.Add(new ListItem() { Item = "Item 1" });
        list.ListItems.Add(new ListItem() { Item = "Item 2" });
        db.Lists.Add(list);
        db.SaveChanges();
        listInDb = db.Lists.Find(list.Id);
        Assert.AreEqual(2, list.ListItems.Count);
        list.DeleteAllListItems(db);
        db.SaveChanges();
        listInDb = db.Lists.Find(list.Id);
        Assert.AreEqual(0, list.ListItems.Count);
    }
}
 4
Author: Xavier John,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-06 21:35:42

Jeśli chcesz usunąć wszystkie wiersze tabeli, możesz wykonać polecenie sql

using (var context = new DataDb())
{
     context.Database.ExecuteSqlCommand("TRUNCATE TABLE [TableName]");
}

TRUNCATE TABLE (Transact-SQL) usuwa wszystkie wiersze z tabeli bez rejestrowania usuwania pojedynczych wierszy. TRUNCATE TABLE jest podobny do instrukcji DELETE z klauzulą no WHERE; jednak TRUNCATE TABLE jest szybszy i zużywa mniej zasobów w logach systemowych i transakcyjnych.

 3
Author: mirtiger,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-18 05:43:06

Możesz używać bibliotek rozszerzeń, takich jak EntityFramework.Rozszerzony lub Z.Plus.EF6, są dostępne dla EF 5, 6 lub rdzenia. Biblioteki te mają świetną wydajność, gdy trzeba usunąć lub zaktualizować i używają LINQ. Przykład usuwania (source plus):

ctx.Users.Where(x => x.LastLoginDate < DateTime.Now.AddYears(-2)) .Delete();

Or ( source extended )

context.Users.Where(u => u.FirstName == "firstname") .Delete();

Używają natywnych poleceń SQL, więc wydajność jest świetna.

 2
Author: UUHHIVS,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-21 00:12:18

Możesz wykonywać zapytania sql bezpośrednio w następujący sposób:

    private int DeleteData()
{
    using (var ctx = new MyEntities(this.ConnectionString))
    {
        if (ctx != null)
        {

            //Delete command
            return ctx.ExecuteStoreCommand("DELETE FROM ALARM WHERE AlarmID > 100");

        }
    }
    return 0;
}

Dla select możemy użyć

using (var context = new MyContext()) 
{ 
    var blogs = context.MyTable.SqlQuery("SELECT * FROM dbo.MyTable").ToList(); 
}
 1
Author: Abhishek Sharma,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-26 09:35:05

Możesz również użyć metody DeleteAllOnSubmit () przekazując jej wyniki w ogólnej liście zamiast w var. W ten sposób twój foreach zmniejsza się do jednej linii kodu:

List<Widgets> widgetList = context.Widgets
              .Where(w => w.WidgetId == widgetId).ToList<Widgets>();

context.Widgets.DeleteAllOnSubmit(widgetList);

context.SubmitChanges();

Prawdopodobnie nadal używa pętli wewnętrznie.

 1
Author: Hugo Nava Kopp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-24 10:41:22

UUHHIVS's jest bardzo eleganckim i szybkim sposobem usuwania partii, ale należy go używać ostrożnie: {]}

  • automatyczne generowanie transakcji: jej zapytania będą objęte transakcją
  • niezależność kontekstu bazy danych: jej wykonanie nie ma nic wspólnego z context.SaveChanges()

Te problemy można obejść przejmując kontrolę nad transakcją. Poniższy kod ilustruje, jak masowo usuwać i masowo wstawiać w sposób transakcyjny:

var repo = DataAccess.EntityRepository;
var existingData = repo.All.Where(x => x.ParentId == parentId);  

TransactionScope scope = null;
try
{
    // this starts the outer transaction 
    using (scope = new TransactionScope(TransactionScopeOption.Required))
    {
        // this starts and commits an inner transaction
        existingData.Delete();

        // var toInsert = ... 

        // this relies on EntityFramework.BulkInsert library
        repo.BulkInsert(toInsert);

        // any other context changes can be performed

        // this starts and commit an inner transaction
        DataAccess.SaveChanges();

        // this commit the outer transaction
        scope.Complete();
    }
}
catch (Exception exc)
{
    // this also rollbacks any pending transactions
    scope?.Dispose();
}
 1
Author: Alexei,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-12 10:31:21

EF 6.=>

var assignmentAddedContent = dbHazirBot.tbl_AssignmentAddedContent.Where(a =>
a.HazirBot_CategoryAssignmentID == categoryAssignment.HazirBot_CategoryAssignmentID);
dbHazirBot.tbl_AssignmentAddedContent.RemoveRange(assignmentAddedContent);
dbHazirBot.SaveChanges();
 0
Author: Erçin Dedeoğlu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-25 00:36:11

Zobacz odpowiedź 'ulubiony bit kodu', który działa

Oto jak go użyłem:

     // Delete all rows from the WebLog table via the EF database context object
    // using a where clause that returns an IEnumerable typed list WebLog class 
    public IEnumerable<WebLog> DeleteAllWebLogEntries()
    {
        IEnumerable<WebLog> myEntities = context.WebLog.Where(e => e.WebLog_ID > 0);
        context.WebLog.RemoveRange(myEntities);
        context.SaveChanges();

        return myEntities;
    }
 -1
Author: Brian Quinn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-21 09:54:16

Najlepsze: in EF6 => .RemoveRange()

Przykład:

db.Table.RemoveRange(db.Table.Where(x => Field == "Something"));
 -1
Author: maXXis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-10 20:58:17
 int id = 5;
 db.tablename.RemoveRange(db.tablename.Where(c => c.firstid == id));
 -1
Author: Varinder Singh Baidwan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-18 11:09:02