Entity Framework RemoveRange 非常慢

问题描述 投票:0回答:2

我正在从我的表中删除数百行。使用 ADO.Net 代码,即

Delete from table where somecolumn = somestring
使用 Entity Framwork,即

不到一秒
MyDbContext context = new MyDbContext()    
context.SomeEntity.RemoveRange(context.SomeEntity.Where(i => i.somecolumn == somestring));
context.SaveChanges();

需要 8-10 秒。

谁能解释一下,或者我做错了什么。

c# entity-framework
2个回答
0
投票

EF 是围绕提供映射到对象模型的关系数据而设计的。它不太适合大批量操作。不过,您可以通过多种方式满足这样的一次性要求。

第一种方法是使用存根进行删除。为此,您需要确保 DbContext 实例对于任何可能被删除的跟踪实例都是“干净的”,因此理想情况下,DbContext 的范围是该方法。

using (var context = new SomeDbContext())
{
    var stubs = context.SomeEntities
        .Where(x => x.SomeColumn == someString)
        .Select(x => x.Id)
        .ToList()
        .Select(x => new SomeEntity { Id == x })
        .ToList();
}

现在您可以将其简化为:

var stubs = context.SomeEntities
    .Where(x => x.SomeColumn == someString)
    .Select(x => new SomeEntity { Id == x.Id })
    .ToList();

但是,您可能想要测试它以确保生成的 SQL 只是选择 ID(而不是整个实体)并且

context.SomeEntities.Local.Any()
仍然是错误的......第一个示例将确保查询加载 ID ,然后继续使用该 ID 构建存根实体。这使得我们的数据“选择”尽可能高效。

从这里你应该可以在未跟踪的存根上使用

RemoveRange

context.SomEntities.RemoveRamge(stubs);
context.SaveChanges();

重要的细节是 DbContext 不能跟踪任何这些实体,因为这会将这些存根临时附加到 DbContext。如果上下文已经在跟踪具有这些 ID 之一的实例,那么您将收到一个错误,指出一个或多个具有相同 ID 的实体已被跟踪。 (因此使用局部范围的 DbContext 来避免这种情况)

执行此删除的另一种方法是发出直接的 SQL 操作。如果您的 DbContext 的范围限定为请求或比此单个操作更长,那么这应该在after处理任何当前跟踪的实例后完成。

第 1 步。如果您有注入的 DbContext,则处理任何被跟踪的实例:

var trackedInstances = context.SomeEntities.Local
     .Where(x => x.SomeColumn == someString);
     .ToList();

if (trackedInstances.Any())
    context.SomeInstances.RemoveRange(trackedInstances);

这将在不访问数据库的情况下检查 DbContext 中是否有任何被跟踪的实例。我们将要删除这些实例,以避免在

SaveChanges
调用期间可能将其中任何一个标记为已修改并触发异常。

第 2 步。构建并运行参数化的原始 SQL 语句以清除数据库中所有剩余的行。

context.Database.ExecuteSqlCommand(@"DELETE FROM dbo.SomeEntities
        WHERE SomeColumn = @someString", new SqlParameter("someString", someString));
context.SaveChanges();

这里的重要细节是使用参数化查询。不要不要 使用字符串中嵌入的参数执行原始 SQL,因为这会为 SQL 注入攻击敞开大门。

即不要使用类似的东西:

context.Database.ExecuteSqlCommand($"DELETE FROM dbo.SomeEntities 
        WHERE SomeColumn = '{someString}'");
// or
context.Database.ExecuteSqlCommand("DELETE FROM dbo.SomeEntities 
        WHERE SomeColumn = '" + someString + "'");

0
投票

好在EF Core7引入了ExecuteUpdate和ExecuteDelete来增加更新和删除操作,是目前使用

Entity Framework
进行批量更新和批量删除的最佳选择。

  MyDbContext context = new MyDbContext(); 
    var affectedRows = await context.SomeEntity
                       .Where(i => i.somecolumn == somestring).ExecuteDeleteAsync();

https://medium.com/@mahsan-ghasemi-dev/bulk-update-and-bulk-delete-using-executeupdate-and-executedelete-in-ef-core7-8751e94e6953

© www.soinside.com 2019 - 2024. All rights reserved.