我知道这在LINQ-to-SQL中是可能的,而且我已经看到了点点滴滴,使我相信在EF中是可能的。是否有可以执行以下操作的扩展程序:
var peopleQuery = Context.People.Where(p => p.Name == "Jim"); peopleQuery.DeleteBatch();
当DeleteBatch分开peopleQuery只是选秀权,并创建一个SQL语句删除所有相应的记录,然后执行查询,而不是直接将标记为删除所有这些实体,并让它做他们一个接一个的。我以为我在下面的代码中找到了类似的内容,但由于实例无法转换为ObjectSet而立即失败。有谁知道如何解决此问题以使其与EF Code First一起使用?还是知道在任何地方都可以做到这一点的例子?
DeleteBatch
public static IQueryable<T> DeleteBatch<T>(this IQueryable<T> instance) where T : class { ObjectSet<T> query = instance as ObjectSet<T>; ObjectContext context = query.Context; string sqlClause = GetClause<T>(instance); context.ExecuteStoreCommand("DELETE {0}", sqlClause); return instance; } public static string GetClause<T>(this IQueryable<T> clause) where T : class { string snippet = "FROM [dbo].["; string sql = ((ObjectQuery<T>)clause).ToTraceString(); string sqlFirstPart = sql.Substring(sql.IndexOf(snippet)); sqlFirstPart = sqlFirstPart.Replace("AS [Extent1]", ""); sqlFirstPart = sqlFirstPart.Replace("[Extent1].", ""); return sqlFirstPart; }
实体框架不支持批处理操作。我喜欢代码解决问题的方式,但即使它确实可以实现您想要的功能(但对于ObjectContext API),这也是一个错误的解决方案。
为什么是错误的解决方案?
它仅在某些情况下有效。在将实体映射到多个表(实体拆分,TPT继承)的任何高级映射解决方案中,它绝对不起作用。我几乎可以肯定,您会发现由于查询的复杂性而无法使用的其他情况。
它使上下文和数据库不一致。这是对数据库执行的任何SQL的问题,但是在这种情况下,该SQL被隐藏了,并且其他使用您的代码的程序员可能会错过它。如果您删除同时加载到上下文实例中的任何记录,则该实体将不会被标记为已删除并从上下文中删除(除非您将该代码添加到您的DeleteBatch方法中- 如果删除的记录实际上映射到该对象,这将特别复杂。多个实体(表拆分))。
最重要的问题是对EF生成的SQL查询的修改以及您对该查询所做的假设。您期望EF将在查询中使用的第一个表命名为Extent1。是的,它现在确实使用该名称,但这是内部EF实现。它可以在EF的任何次要更新中更改。围绕任何API的内部构造自定义逻辑被认为是不好的做法。
Extent1
结果,您已经必须在SQL级别上使用查询,因此您可以按照@mreyeros所示直接调用SQL查询,并避免此解决方案中的风险。您将不得不处理表和列的真实名称,但这是您可以控制的(您的映射可以定义它们)。
如果您不认为这些风险很重要,则可以对代码进行少量更改以使其在DbContext API中起作用:
public static class DbContextExtensions { public static void DeleteBatch<T>(this DbContext context, IQueryable<T> query) where T : class { string sqlClause = GetClause<T>(query); context.Database.ExecuteSqlCommand(String.Format("DELETE {0}", sqlClause)); } private static string GetClause<T>(IQueryable<T> clause) where T : class { string snippet = "FROM [dbo].["; string sql = clause.ToString(); string sqlFirstPart = sql.Substring(sql.IndexOf(snippet)); sqlFirstPart = sqlFirstPart.Replace("AS [Extent1]", ""); sqlFirstPart = sqlFirstPart.Replace("[Extent1].", ""); return sqlFirstPart; } }
现在,您将以这种方式调用批量删除:
context.DeleteBatch(context.People.Where(p => p.Name == "Jim"));