我想使用 linq 从表中批量删除记录。有一篇文章描述了如何做到这一点: 在 LINQ to Entities 中批量删除
var query = from c in ctx.Customers
where c.SalesPerson.Email == "..."
select c;
query.Delete();
但是我的 var 变量中不存在“删除”函数。
此外,我的上下文中不存在“SubmitChanges”函数。
我想使用 linq 从表中批量删除记录。有一篇文章描述了如何做到这一点: 在 LINQ to Entities 中批量删除
var query = from c in ctx.Customers
where c.SalesPerson.Email == "..."
select c;
query.Delete();
但是我的 var 变量中不存在“删除”函数。
此外,我的上下文中不存在“SubmitChanges”函数。
有一个有趣的 NuGet 包,可让您进行批量删除和更新:
实体框架中目前不支持批量删除。它实际上是 codeplex 上正在讨论的功能之一,现在 EF 是开源的。
EntityFramework.Extended
提供批量删除支持(你可以在 nuget 中找到它)但是我的经验是它有一些性能问题。
此代码向任何 DbContext 添加了一个简单的扩展方法,该方法将批量删除您提供的实体框架查询中引用的任何表中的所有数据。它的工作原理是简单地提取查询中涉及的所有表名,并尝试通过发出“DELETE FROM tablename”SQL 查询来删除数据,这在大多数类型的数据库中都很常见。
要使用,只需执行以下操作:
myContext.BulkDelete(x => x.Things);
这将删除链接到 Things 实体存储的表中的所有内容。
编码:
using System.Linq;
using System.Text.RegularExpressions;
namespace System.Data.Entity {
public static class DbContextExtensions {
/// <summary>
/// Efficiently deletes all data from any database tables used by the specified entity framework query.
/// </summary>
/// <typeparam name="TContext">The DbContext Type on which to perform the delete.</typeparam>
/// <typeparam name="TEntity">The Entity Type to which the query resolves.</typeparam>
/// <param name="ctx">The DbContext on which to perform the delete.</param>
/// <param name="deleteQuery">The query that references the tables you want to delete.</param>
public static void BulkDelete<TContext, TEntity>(this TContext ctx, Func<TContext, IQueryable<TEntity>> deleteQuery) where TContext : DbContext {
var findTables = new Regex(@"(?:FROM|JOIN)\s+(\[\w+\]\.\[\w+\])\s+AS");
var qry = deleteQuery(ctx).ToString();
// Get list of all tables mentioned in the query
var tables = findTables.Matches(qry).Cast<Match>().Select(m => m.Result("$1")).Distinct().ToList();
// Loop through all the tables, attempting to delete each one in turn
var max = 30;
var exception = (Exception)null;
while (tables.Any() && max-- > 0) {
// Get the next table
var table = tables.First();
try {
// Attempt the delete
ctx.Database.ExecuteSqlCommand(string.Format("DELETE FROM {0}", table));
// Success, so remove table from the list
tables.Remove(table);
} catch (Exception ex) {
// Error, probably due to dependent constraint, save exception for later if needed.
exception = ex;
// Push the table to the back of the queue
tables.Remove(table);
tables.Add(table);
}
}
// Error error has occurred, and cannot be resolved by deleting in a different
// order, then rethrow the exception and give up.
if (max <= 0 && exception != null) throw exception;
}
}
}
我这样做似乎工作正常。如果有任何理由说明这是一种不好的做法,请告知。
var customersToDelete = await ctx.Customers.Where(c => c.Email == "...").ToListAsync();
foreach (var customerToDelete in customersToDelete)
{
ctx.Customers.Remove(customerToDelete);
}
await ctx.SaveChangesAsync();
在调用 SaveChanges 后,EF 执行数千个 DELETE 查询时遇到了同样的问题。我不确定 EntityFramework.Extensions 商业库是否会帮助我,所以我决定自己实现批量 DELETE 并提出类似于BG100 的解决方案!
public async Task<List<TK>> BulkDeleteAsync(List<TK> ids)
{
if (ids.Count < 1) {
return new List<TK>();
}
// NOTE: DbContext here is a property of Repository Class
// SOURCE: https://stackoverflow.com/a/9775744
var tableName = DbContext.GetTableName<T>();
var sql = $@"
DELETE FROM {tableName}
OUTPUT Deleted.Id
// NOTE: Be aware that 'Id' column naming depends on your project conventions
WHERE Id IN({string.Join(", ", ids)});
";
return await @this.Database.SqlQuery<TK>(sql).ToListAsync();
}
如果您有类似通用存储库之类的东西,它应该适合您。至少您可以尝试将其安装到您的 EF 基础架构中。
我还对其进行了更多调整,并且能够对多个实体块执行查询。如果您的数据库中有任何查询大小限制,它将对您有所帮助。
const int ChunkSize = 1024;
public async Task<List<TK>> BulkDeleteAsync(List<TK> ids)
{
if (ids.Count < 1) {
return new List<TK>();
}
// SOURCE: https://stackoverflow.com/a/20953521/11344051
var chunks = ids.Chunks(ChunkSize).Select(c => c.ToList()).ToList();
var tableName = DbContext.GetTableName<T>();
var deletedIds = new List<TK>();
foreach (var chunk in chunks) {
var sql = $@"
DELETE FROM {tableName}
OUTPUT Deleted.Id
WHERE Id IN({string.Join(", ", chunk)});
";
deletedIds.AddRange(DbContext.Database.SqlQuery<TK>(sql).ToListAsync());
}
return deletedIds;
}