Comment puis-je supprimer plusieurs lignes dans le cadre de L'entité (sans foreach)

on va quand même faire un foreach, Non?

261
demandé sur Jon Galloway 2010-03-26 01:24:26

19 réponses

si vous ne voulez pas exécuter SQL appelant directement DeleteObject dans une boucle est le mieux que vous pouvez faire aujourd'hui.

cependant, vous pouvez exécuter SQL et encore le faire tout à fait usage général via une méthode d'extension, en utilisant l'approche que je décris ici .

bien que cette réponse soit pour 3.5. Pour la version 4.0, j'utiliserais probablement la nouvelle API ExecuteStoreCommand sous le capot, au lieu de passer à la StoreConnection.

46
répondu Alex James 2017-05-23 12:26:34

Entitefram Framework 6 a rendu cela un peu plus facile avec .RemoveRange() .

exemple:

db.People.RemoveRange(db.People.Where(x => x.State == "CA"));
db.SaveChanges();
554
répondu Kyle 2016-07-18 22:00:22

c'est aussi bon qu'il obtient, droit? Je peux l'abstraire avec une extension méthode ou aide, mais quelque part nous allons encore faire foreach, droit?

Eh bien, oui, sauf que vous pouvez en faire une double doublure:

context.Widgets.Where(w => w.WidgetId == widgetId)
               .ToList().ForEach(context.Widgets.DeleteObject);
context.SaveChanges();
76
répondu Klaus Byskov Pedersen 2012-06-18 22:40:50
using (var context = new DatabaseEntities())
{
    context.ExecuteStoreCommand("DELETE FROM YOURTABLE WHERE CustomerID = {0}", customerId);
}
64
répondu Vlad Bezden 2011-07-07 18:10:10

je sais qu'il est assez tard, mais si quelqu'un a besoin d'une solution simple, la chose cool est que vous pouvez également ajouter la clause où avec elle:

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

Note: vient d'être testé avec MSSQL2008.

mise à Jour: La solution ci-dessus ne fonctionnera pas lorsque EF génère une déclaration sql avec paramètres , donc voici la mise à jour pour 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);
        }

il exige un peu de Réflexion, mais fonctionne bien.

37
répondu Thanh Nguyen 2013-08-30 15:12:05

pour toute personne utilisant EF5, la bibliothèque d'extension suivante peut être utilisée: https://github.com/loresoft/EntityFramework.Extended

context.Widgets.Delete(w => w.WidgetId == widgetId);
28
répondu Marcelo Mason 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();
} 

Utilisation:

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

Or:

// delete all from entity
DeleteWhere<MyEntity>();
10
répondu jzm 2016-09-14 13:19:36

semble toujours fou de devoir retirer quoi que ce soit du serveur juste pour l'effacer, mais au moins récupérer juste les IDs est beaucoup plus mince que de tirer vers le bas les entities complètes:

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
répondu Edward Brey 2017-02-04 03:51:53

pour EF 4.1,

var objectContext = (myEntities as IObjectContextAdapter).ObjectContext;
objectContext.ExecuteStoreCommand("delete from [myTable];");
5
répondu Amit Pawar 2013-03-07 10:50:31

le moyen le plus rapide de supprimer est d'utiliser une procédure stockée. Je préfère les procédures stockées dans un projet de base de données au SQL dynamique parce que les renommages seront manipulés correctement et auront des erreurs de compilation. Le SQL dynamique peut se référer à des tables qui ont été supprimées/renommées et qui causent des erreurs de temps d'exécution.

dans cet exemple, j'ai deux tables List et ListItems. J'ai besoin d'un moyen rapide pour supprimer tous les ListItems d'une liste donnée.

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

maintenant le partie intéressante de la suppression des éléments et de la mise à jour du cadre de L'entité à l'aide d'une extension.

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

le code principal peut maintenant l'utiliser est comme

[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
répondu Xavier John 2015-02-06 21:35:42

Si vous souhaitez supprimer toutes les lignes d'une table, vous pouvez exécuter la commande sql

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

table tronquée (Transact-SQL) supprime toutes les lignes d'une table sans journaliser les suppressions de ligne individuelles. Le tableau tronqué est similaire à L'énoncé de suppression sans clause où; cependant, le tableau tronqué est plus rapide et utilise moins de ressources du système et du journal des transactions.

3
répondu mirtiger 2015-02-18 05:43:06

vous pouvez utiliser les bibliothèques extensions pour faire cela comme Entitefram Framework.Étendue ou Z. Entitefram Framework.Majoré.EF6, disponible pour EF 5, 6 ou Core. Ces bibliothèques ont de bonnes performances lorsque vous devez supprimer ou mettre à jour et QU'elles utilisent LINQ. Exemple de suppression ( source plus ):

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

, ou source étendue )

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

ceux-ci utilisent des énoncés SQL natifs, donc la performance est grande.

2
répondu UUHHIVS 2016-11-21 00:12:18

vous pouvez exécuter des requêtes sql directement comme suit:

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

pour sélectionner nous pouvons utiliser

using (var context = new MyContext()) 
{ 
    var blogs = context.MyTable.SqlQuery("SELECT * FROM dbo.MyTable").ToList(); 
}
1
répondu Abhishek Sharma 2014-05-26 09:35:05

vous pouvez également utiliser la méthode DeleteAllOnSubmit () en lui passant vos résultats dans une liste générique plutôt que dans var. Ainsi votre foreach se réduit à une ligne de code:

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

context.Widgets.DeleteAllOnSubmit(widgetList);

context.SubmitChanges();

il utilise probablement encore une boucle en interne cependant.

1
répondu Hugo Nava Kopp 2014-09-24 10:41:22

UUHHIVS 's est un moyen très élégant et rapide pour supprimer par lots, mais il doit être utilisé avec soin:

  • génération automatique de transaction: ses requêtes seront couvertes par une transaction
  • indépendance du contexte de la base de données: son exécution n'a rien à voir avec context.SaveChanges()

ces émissions peuvent être contournées en prenant le contrôle de la transaction. Le code suivant illustre comment supprimer et insérer en vrac de manière transactionnelle:

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
répondu Alexei 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
répondu Erçin Dedeoğlu 2014-10-25 00:36:11

voir la réponse "bit de code préféré" qui fonctionne

Voici comment je l'ai utilisé:

     // 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
répondu Brian Quinn 2014-02-21 09:54:16

: in EF6 => .RemoveRange()

exemple:

db.Table.RemoveRange(db.Table.Where(x => Field == "Something"));
-1
répondu maXXis 2016-01-10 20:58:17
 int id = 5;
 db.tablename.RemoveRange(db.tablename.Where(c => c.firstid == id));
-1
répondu Varinder Singh Baidwan 2018-06-18 11:09:02