如何删除entity framework中的多行(无foreach)

我正在使用entity framework从表中删除几个项目。 没有外键/父对象,所以我不能用OnDeleteCascade处理这个。

现在我正在这样做:

var widgets = context.Widgets .Where(w => w.WidgetId == widgetId); foreach (Widget widget in widgets) { context.Widgets.DeleteObject(widget); } context.SaveChanges(); 

它的工作,但foreach错误我。 我正在使用EF4,但我不想执行SQL。 我只是想确保我没有失去任何东西 – 这是一样好,对吧? 我可以用扩展方法或帮助器来抽象它,但是在某个地方我们仍然会做一个foreach,对吧?

如果你不想在一个循环中直接调用DeleteObject来执行SQL是你今天能做的最好的。

但是,您可以使用我在此处描述的方法,通过扩展方法执行SQL,并将其完全用作通用目的。

虽然答案是3.5。 对于4.0我可能会使用新的ExecuteStoreCommand API,而不是下降到StoreConnection。

EntityFramework 6使得.RemoveRange()更容易.RemoveRange()

例:

 db.People.RemoveRange(db.People.Where(x => x.State == "CA")); db.SaveChanges(); 

这是好的,对吧? 我可以用扩展方法或帮助器来抽象它,但是在某个地方我们仍然会做一个foreach,对吧?

那么,是的,除了你可以把它做成一个双线:

 context.Widgets.Where(w => w.WidgetId == widgetId) .ToList().ForEach(context.Widgets.DeleteObject); context.SaveChanges(); 
 using (var context = new DatabaseEntities()) { context.ExecuteStoreCommand("DELETE FROM YOURTABLE WHERE CustomerID = {0}", customerId); } 

我知道这是相当晚,但如果有人需要一个简单的解决scheme,很酷的事情是,你也可以添加它的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); } 

注意:只用MSSQL2008进行testing。

更新:当EF生成带有参数的 sql语句时,上面的解决scheme将不起作用,所以下面是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); } 

它需要一点反思,但效果很好。

对于使用EF5的人,可以使用以下扩展库: https : //github.com/loresoft/EntityFramework.Extended

 context.Widgets.Delete(w => w.WidgetId == widgetId); 

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

用法:

 // Delete where condition is met. DeleteWhere<MyEntity>(d => d.Name == "Something"); Or: // delete all from entity DeleteWhere<MyEntity>(); 

看起来似乎疯狂,不得不从服务器上拉回任何东西来删除它,但是至less取回ID比拉下整个实体要简单得多:

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

对于EF 4.1,

 var objectContext = (myEntities as IObjectContextAdapter).ObjectContext; objectContext.ExecuteStoreCommand("delete from [myTable];"); 

最快的删除方法是使用存储过程。 我更喜欢数据库项目中的存储过程通过dynamicSQL,因为重命名将被正确处理,并有编译器错误。 dynamicSQL可能引用已被删除/重命名导致运行时错误的表。

在这个例子中,我有两个表List和ListItems。 我需要一个快速的方法来删除给定列表的所有ListItems。

 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 

现在删除项目和使用扩展名更新entity framework的有趣部分。

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

主要的代码现在可以使用它

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

如果你想删除一个表的所有行,你可以执行sql命令

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

TRUNCATE TABLE(Transact-SQL)删除表中的所有行,而不logging单个行删除。 TRUNCATE TABLE类似于没有WHERE子句的DELETE语句; 但是,TRUNCATE TABLE速度更快,并使用更less的系统和事务日志资源。

您也可以使用DeleteAllOnSubmit()方法将结果传递给通用列表而不是var。 这样你的foreach简化为一行代码:

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

它可能仍然在内部使用循环。

您可以使用扩展库来执行EntityFramework.Extended或Z.EntityFramework.Plus.EF6,可用于EF 5,6或Core。 当你需要删除或更新这些库时,这些库具有很好的性能,而且它们使用LINQ。 删除示例( source plus ):

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

或( 源扩展 )

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

这些使用本机SQL语句,所以性能很好。

EF 6。=>

 var assignmentAddedContent = dbHazirBot.tbl_AssignmentAddedContent.Where(a => a.HazirBot_CategoryAssignmentID == categoryAssignment.HazirBot_CategoryAssignmentID); dbHazirBot.tbl_AssignmentAddedContent.RemoveRange(assignmentAddedContent); dbHazirBot.SaveChanges(); 

UUHHIVS是批量删除的一种非常优雅和快速的方式,但必须谨慎使用:

  • 自动生成事务:其查询将被包含在事务中
  • 数据库上下文无关性:它的执行与context.SaveChanges()无关。

这些问题可以通过控制交易来规避。 以下代码说明如何以事务方式批量删除和批量插入:

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

看到有用的答案“最喜欢的代码”

以下是我如何使用它:

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

你可以直接执行sql查询如下:

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

对于我们可以使用的select

 using (var context = new MyContext()) { var blogs = context.MyTable.SqlQuery("SELECT * FROM dbo.MyTable").ToList(); } 

最佳: in EF6 => .RemoveRange()

例:

 db.Table.RemoveRange(db.Table.Where(x => Field == "Something"));