在 LINQ to Entities 中批量删除

Posted

技术标签:

【中文标题】在 LINQ to Entities 中批量删除【英文标题】:Bulk-deleting in LINQ to Entities 【发布时间】:2010-10-26 12:49:34 【问题描述】:

有没有办法批量删除与 LINQ 或 LINQ-to-Entities 中的给定查询匹配的一组对象?我能找到的唯一引用已经过时,迭代并手动删除我希望删除的所有对象似乎很愚蠢。

【问题讨论】:

【参考方案1】:

不久前,我写了一个由 4 部分组成的博客系列(部分 1、2、3 和 4)涵盖在实体框架中进行批量更新(使用一个命令)。

虽然该系列的重点是更新,但您绝对可以使用所涉及的原则进行删除。

所以你应该可以这样写:

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

query.Delete();

您需要做的就是实现 Delete() 扩展方法。有关如何...的提示,请参阅帖子系列。

希望对你有帮助

【讨论】:

如果有人有这个代码示例,我会很高兴! 一堆扩展方法(包括批量删除)可以在这里找到:github.com/loresoft/EntityFramework.Extended 注意github.com/loresoft/EntityFramework.Extended 依赖于EF5,如果你使用Nuget Package manager Console 安装它,它不会询问你安装EF5 它并没有真正的帮助 - 我希望看到一个删除命令的工作示例,而不是“猜测”如何在生产代码中实现我自己的并可能搞砸一些事情。 -1 嗯...所以问题的答案是重定向到 4 篇博文,而不是向观众展示具体的答案。【参考方案2】:
    using (var context = new DatabaseEntities())
    
        // delete existing records
        context.ExecuteStoreCommand("DELETE FROM YOURTABLE WHERE CustomerID = 0", customerId);
    

【讨论】:

+1 - 很高兴看到如何使用 EF 执行 SQL 代码的代码示例 我意识到这可能是唯一的方法,没有创建存储过程,但这感觉像是在作弊 =)。现在我正在使用它,我很想在其他几个地方使用它来解决 EF 的怪癖大声笑 - 例如复杂的左连接和分组...... :) +!... 使用DB时,你会发现你想要的工具是螺丝刀.. EF只是另一个锤子。 轻微的缺点:您现在有一个与开发环境断开连接的数据库命令。它不是强类型的,因此此 SQL 中列的数据库更改不会在 Visual Studio 中突出显示【参考方案3】:

这个问题是一个老问题(在 EF5 存在之前)。对于使用 EF5 的任何人,EntityFramework.Extended 可以快速完成此操作。

【讨论】:

【参考方案4】:

我在这里看到的答案是 Linq to Sql

DeleteAllOnSubmit 是 System.Data.Linq 和 ITable 的一部分,它是 Linq to Sql

实体框架无法做到这一点。

说了这么多,我还没有解决方案,但当我有解决方案时会回复

【讨论】:

【参考方案5】:

对于那些使用EF6并希望执行行SQL查询以进行删除的人:

using (var context = new DatabaseEntities())

    // delete existing records
    context.Database.ExecuteSqlCommand("DELETE FROM YOURTABLE WHERE CustomerID = @id", idParameter);

【讨论】:

这在 EF 5 中对我有用,但我必须使用 @p0 作为参数。好的是它在生成的 sql 中提供类型安全参数检查:因此在 EF5 中,这将起作用: context.Database.ExecuteSqlCommand("DELETE FROM YOURTABLE WHERE CustomerID = @p0", idParameter); \@p1 用于下一个参数等... YOURTABLE 可以像这样获得:***.com/a/45671666/1016343 这样您就可以编写一个将表作为类型参数的函数。然后您将有一个通用的批量操作来删除表。【参考方案6】:

RemoveRange 是在 EF6 中引入的,它可以删除对象列表。超级简单。

var origins= (from po in db.PermitOrigins where po.PermitID == thisPermit.PermitID select po).ToList();
db.PermitOrigins.RemoveRange(origins);
db.SaveChanges();

【讨论】:

虽然这段代码 sn-p 可以解决问题,但including an explanation 确实有助于提高帖子的质量。请记住,您是在为将来的读者回答问题,而这些人可能不知道您提出代码建议的原因。【参考方案7】:

我知道任何数据上下文的DeleteAllOnSubmit 方法将删除查询中的所有记录。由于正在删除许多对象,因此必须进行一些优化。不过我不确定。

【讨论】:

实际上没有进行任何优化。生成的 SQL 会枚举与您的查询匹配的所有对象,然后手动迭代它们以删除它们。当然,迭代发生在数据库中,而不是在您的代码中,但是您仍然不必要地构建一个结果集,只是为了删除它的内容——仍然比一个简单的“从表 WHERE foo = bar 中删除”差得多,它构建没有结果集,只覆盖表格一次。【参考方案8】:

我不确定它的效率如何,但您可以尝试以下方法:

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

【讨论】:

这仍然会遍历与查询匹配的所有元素;它只是在数据库上执行此操作,而不是在您的代码中执行此操作。更高效,但仍远非理想的解决方案。 那么,我能想到的唯一其他方法就是执行 myDataContext.ExecuteCommand("DELETE ...");。也远非理想,但它会起作用。 此方法的主要优点是可以在编译时捕获由于架构更改而导致的错误,此外您还可以获得 IntelliSense 和调试支持。但是,性能很差。获取所有记录,然后通过完整的数据比较来逐一删除,以验证正在删除的记录是否正确。此方法可能适用于单元测试或其他不经常调用的代码片段,其中代码可维护性比性能更重要。【参考方案9】:

你可以编写一个存储过程来执行删除并从 LINQ 调用它。基于集合的删除总体上可能更快,但如果它影响太多记录,您可能会导致锁定问题,并且您可能需要混合循环遍历记录集(一次可能 2000 个,大小取决于您的数据库设计,但 2000 是如果您发现基于集合的删除需要很长时间以影响表的其他使用)来执行删除。

【讨论】:

【参考方案10】:

通过实体框架删除数据依赖于使用 DeleteObject 方法。您可以在要删除的实体类的 EntityCollection 或派生的 ObjectContext 上调用此方法。这是一个简单的例子:

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

【讨论】:

这不是“批量删除”。【参考方案11】:

在本例中,我将要删除的记录一一附加到结果集中,然后请求将其删除。然后我有 1 个保存更改。

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

【讨论】:

【参考方案12】:
 context.Entity.Where(p => p.col== id)
               .ToList().ForEach(p => db.Entity.DeleteObject(p));

这是使用 EF 从数据库中删除记录的最快方法

【讨论】:

【参考方案13】:

我会这样做:

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

我认为没有循环可以做到这一点,因为实体框架与实体一起工作,而且大多数时候,这意味着对象的集合。

【讨论】:

你也可以分享你所做的。谢谢。 @G Jeny Ramirez 添加了我的解决方案。 @GJennyRamirez 在您的示例中,您也多次保存更改,我认为您可以将其拉出 foreach 循环并执行一次【参考方案14】:

当前 EF 中没有实现批量操作。

这正是实体框架团队设计的方式。反编译器清楚地显示了 EF 在内部做什么:

public void DeleteAllOnSubmit<TSubEntity>(IEnumerable<TSubEntity> entities) 
where TSubEntity : TEntity

    if (entities == null)
    
        throw Error.ArgumentNull("entities");
    
    CheckReadOnly();
    context.CheckNotInSubmitChanges();
    context.VerifyTrackingEnabled();
    foreach (TSubEntity item in entities.ToList())
    
        TEntity entity = (TEntity)(object)item;
        DeleteOnSubmit(entity);
    

如您所见,EF 在内部循环遍历表的所有元素 - 通过调用 .ToList() 在内存中实现。

如果您仍然想利用 EF 开箱即用的可能性来完成它,而不是提交 SQL 命令,您仍然可以使用一些辅助方法让您的生活更轻松。 语法

ctx.Table1.DeleteAllOnSubmit(ctx.Table1);
ctx.Table2.DeleteAllOnSubmit(ctx.Table2);
ctx.Table3.DeleteAllOnSubmit(ctx.Table3);
ctx.SubmitChanges();

在我看来不是很好。

这是我在LinqPad 中写的一个例子,它简化了一点:

void Main()

    var ctx = this;

    void DeleteTable<T>(System.Data.Linq.Table<T> tbl, bool submitChanges = false)
    where T : class
    
        tbl.DeleteAllOnSubmit(tbl);
        if (submitChanges) ctx.SubmitChanges();
    
    
    DeleteTable(ctx.Table1);
    DeleteTable(ctx.Table2);
    DeleteTable(ctx.Table3);
        
    ctx.SubmitChanges();

如果你在做测试并且需要删除很多表,那么这种语法更容易处理。换句话说,这是为您提供方便的一些语法糖。但请记住,EF 仍然会在内部和内存中循环遍历所有对象,如果数据量很大,这可能会非常低效。

【讨论】:

这是 LINQ-to-SQL,使用 Linqpad 时经常出错。 也就是说,您只提出了一种提供一些语法糖的小方法,而没有解决问题。与 IMO 无关。 @GertArnold - 正如我在反汇编代码中指出的那样,该批评者主要针对没有提供真正批量删除数据的功能的实体框架团队。但实际上我们正在这里寻找解决方案。如果我可以将表的名称确定为字符串以调用 SQL 删除语句,我编写的小函数会更有帮助。你有一个想法格特,如何获得?然后我可以更改函数,它会使用 SQL DELETE 进行批量操作。 这都不是重点。 EF 不提供批量删除,期间。有很多方法可以创建执行点删除的代码,只是更容易一点。都无关紧要。其他回答者也没有得到这个。 @GertArnold - 同意,我在回答中更清楚地说明了这一点。

以上是关于在 LINQ to Entities 中批量删除的主要内容,如果未能解决你的问题,请参考以下文章

Linq to Entities - 3 层架构

在 Linq-to-Entities 查询中格式化日期会导致异常

在 LINQ to Entities 查询中无法构造实体或复杂类型

在实体框架和 Linq to Entities 中使用规范模式和表达式

使用 LINQ-to-Entities 搜索时忽略空字符串

查询 LINQ to Entities 中联系字段的属性