我正在从我的表中删除数百行。使用 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 秒。
谁能解释一下,或者我做错了什么。
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 + "'");
好在EF Core7引入了ExecuteUpdate和ExecuteDelete来增加更新和删除操作,是目前使用
Entity Framework
进行批量更新和批量删除的最佳选择。
MyDbContext context = new MyDbContext();
var affectedRows = await context.SomeEntity
.Where(i => i.somecolumn == somestring).ExecuteDeleteAsync();