C#实体框架:批量扩展输入内存问题

C#实体框架:批量扩展输入内存问题,c#,.net,entity-framework,asp.net-core,entity-framework-extensions,C#,.net,Entity Framework,Asp.net Core,Entity Framework Extensions,我目前正在使用EF扩展。有一件事我不明白,“它应该有助于提高性能” 然而,将一百万条以上的记录放入列表变量本身就是一个内存问题。 因此,如果想要更新一百万条记录,而不将所有内容都保存在内存中,如何才能有效地做到这一点 我们是否应该对循环使用,并批量更新,比如10000?EFUPDATE是否有任何本机功能支持此功能 示例: var productUpdate = _dbContext.Set<Product>() .Where(x => x.ProductType ==

我目前正在使用EF扩展。有一件事我不明白,“它应该有助于提高性能”

然而,将一百万条以上的记录放入列表变量本身就是一个内存问题。 因此,如果想要更新一百万条记录,而不将所有内容都保存在内存中,如何才能有效地做到这一点

我们是否应该对循环使用
,并批量更新,比如10000?EFUPDATE是否有任何本机功能支持此功能

示例:

var productUpdate = _dbContext.Set<Product>()
    .Where(x => x.ProductType == 'Electronics');  // this creates IQueryable

await productUpdate.ForEachAsync(c => c.ProductBrand = 'ABC Company');

_dbContext.BulkUpdateAsync(productUpdate.ToList());
var productUpdate=\u dbContext.Set()
.其中(x=>x.ProductType=='Electronics');//这就创造了一个可供选择的环境
等待productUpdate.ForEachAsync(c=>c.ProductBrand='ABC Company');
_dbContext.BulkUpdateAsync(productUpdate.ToList());
资源:

var productUpdate = _dbContext.Set<Product>()
    .Where(x => x.ProductType == 'Electronics');  // this creates IQueryable

await productUpdate.ForEachAsync(c => c.ProductBrand = 'ABC Company');

_dbContext.BulkUpdateAsync(productUpdate.ToList());

这实际上是EF不适合的。EF的数据库交互从记录对象开始,并从那里开始。如果实体未进行更改跟踪(并因此加载),EF无法生成部分更新(即不覆盖所有内容),同样,EF也无法基于条件而不是密钥删除记录

对于条件更新/删除逻辑,例如

UPDATE People
SET FirstName = 'Bob'
WHERE FirstName = 'Robert'

使用EF方法执行此操作将需要加载所有这些实体,只需将它们发送回(更新或删除)数据库,这是对带宽和性能的浪费,正如您已经发现的那样

我在这里找到的最佳解决方案是绕过EF的LINQ友好方法,而是自己执行原始SQL。这仍然可以使用EF上下文来完成

using (var ctx = new MyContext())
{
    string updateCommand = "UPDATE People SET FirstName = 'Bob' WHERE FirstName = 'Robert'";
    int noOfRowsUpdated = ctx.Database.ExecuteSqlCommand(updateCommand);

    string deleteCommand = "DELETE FROM People WHERE FirstName = 'Robert'";
    int noOfRowsDeleted = ctx.Database.ExecuteSqlCommand(deleteCommand);
}
更多信息。当然,不要忘记在相关的地方防止SQL注入

运行原始SQL的特定语法可能因EF/EF Core的版本而异,但据我所知,所有版本都允许您执行原始SQL


我不能具体评论EF扩展或BulkUpdate的性能,我也不会从他们那里购买

根据他们的文档,他们似乎没有具有正确签名的方法来允许条件更新/删除逻辑

  • BulkUpdate
    似乎不允许您输入逻辑条件(更新命令中的WHERE)来优化此操作
  • BulkDelete
    仍然有一个
    BatchSize
    设置,这表明他们仍然在一次处理一条记录(好吧,我猜是每个批次),并且没有使用带条件的单个DELETE查询(WHERE子句)
基于您在问题中的预期代码,EF扩展并没有真正提供您所需要的。简单地在数据库上执行原始SQL,性能更高,成本更低,因为这绕过了EF加载其实体的需要

更新
我可能会被纠正,有一些条件更新逻辑的支持,如图所示。然而,我不清楚的是,这个示例仍然在内存中加载所有内容,如果您已经在内存中加载了所有内容,那么条件WHERE逻辑的目的是什么(为什么不在内存中使用LINQ呢?)

但是,即使在不加载实体的情况下也能正常工作,它仍然:

  • 更有限(与允许任何有效SQL的布尔条件的SQL相比,只允许相等检查)
  • 相对复杂(我不喜欢它们的语法,可能是主观的)
  • 而且更昂贵(仍然是付费图书馆)
与滚动自己的原始SQL查询相比。我仍然建议在这里滚动您自己的原始SQL,但这只是我的意见。

我找到了“合适的”EF扩展方法,可以使用类似查询的条件进行批量更新:

var productUpdate = _dbContext.Set<Product>()
    .Where(x => x.ProductType == 'Electronics')
    .UpdateFromQuery( x => new Product { ProductBrand = "ABC Company" });
var productUpdate=\u dbContext.Set()
.Where(x=>x.ProductType=='Electronics')
.UpdateFromQuery(x=>新产品{ProductBrand=“ABC公司”});
这将导致正确的SQL
更新。。。设置其中
,无需首先加载实体,如下所示:

为什么
UpdateFromQuery
SaveChanges
BulkSaveChanges
BulkUpdate
更快

UpdateFromQuery
直接在SQL中执行语句,例如
UPDATE[TableName]SET[SetColumnsAndValues],其中[Key]

其他操作通常需要一次或多次数据库往返,这会降低性能

您可以根据他们的
BulkUpdate
示例来检查这方面的工作语法

其他考虑
  • 不幸的是,没有提到这方面的批处理操作

  • 在进行像这样的大型更新之前,可能需要考虑停用此列上的索引,然后重新构建它们。如果你有很多这样的工具,这是非常有用的

  • 注意
    中的条件,如果EF不能将其转换为SQL,那么它将在客户端完成,这意味着“通常的”可怕的往返“加载-内存中的更改-更新”


100万条记录真是太多了。事实上,您可以尝试批量生产。如果对查询执行“
ToList()
”,则将其具体化,这意味着将所有内容加载到内存中。你想更准确地更新什么?这有帮助吗:?现在不存在支持延迟加载的数据类型吗?也许ICollection有?令人惊讶的是,批量扩展没有本机库函数来支持此batchinghi@Pac0我们正在尝试更新数据库中的单个字段,对于数百万条记录,如果它们符合特定的where标准,是的,批量更新扩展应能帮助解决OP案例。但是你的方法是有效的,也可以接受,这当然取决于OP的实际设置和性能要求。hi@Pac0他还提出了一些关于“使用EF方法进行此操作将要求你加载所有