29

我正在尝试使用实体框架批量更新记录。我试过 Entity Framework.ExtensionsUpdate方法。

Update方法能够批量更新具有相同更新值集的记录集。

例子:

           Id -  Quantity
Record 1 - A  -  10
Record 2 - B  -  20
Record 3 - C  -  30

我们可以通过简单的调用批量更新以上所有记录

Records.Update(new => Record { Quantity = 100 });

如何使用Entityframework.Extensions或以任何其他方法批量更新不同数量的每条记录,从而更快地完成批量更新?

4

8 回答 8

25

如果您不想使用 SQL 语句,可以使用 Attach 方法来更新实体,而无需先加载它:

using (myDbEntities db = new myDbEntities())
{
    try
    {
      //disable detection of changes to improve performance
      db.Configuration.AutoDetectChangesEnabled = false;

      //for all the entities to update...
      MyObjectEntity entityToUpdate = new MyObjectEntity() {Id=123, Quantity=100};
      db.MyObjectEntity.Attach(entityToUpdate);

      //then perform the update
      db.SaveChanges();
    }
    finally
    {
      //re-enable detection of changes
      db.Configuration.AutoDetectChangesEnabled = true;
    }
}
于 2017-05-29T17:24:12.760 回答
23

使用ExecuteSqlCommand

using (yourDbEntities db = new yourDbEntities())
{
    db.Database.ExecuteSqlCommand("UPDATE YourTABLE SET Quantity = {0} WHERE Id = {1}", quantity, id);
}

或者ExecuteStoreCommand

yourDbContext.ExecuteStoreCommand("UPDATE YourTABLE SET Quantity = {0} WHERE Id = {1}", quantity, id);
于 2017-05-26T06:21:45.383 回答
4

如果您只想修改几个属性,请使用这种方式:

foreach (var vSelectedDok in doks)
{
    //disable detection of changes to improve performance
    vDal.Configuration.AutoDetectChangesEnabled = false;
    
    vDal.Dokumente.Attach(vSelectedDok);

    vDal.Entry(vSelectedDok).Property(x=>x.Status).IsModified=true;
    vDal.Entry(vSelectedDok).Property(x => x.LastDateChanged).IsModified = true;
}
vDal.SaveChanges();
于 2019-05-07T08:55:31.493 回答
2

a) EFCore.BulkExtensions - BatchUpdateAsync

_dbContext.Set<MyObjectEntity>().BatchUpdateAsync( x => new MyObjectEntity{ Id=123, Quantity=100 });

https://github.com/borisdj/EFCore.BulkExtensions

“EntityFrameworkCore 扩展:批量操作(插入、更新、删除、读取、Upsert、同步)和批处理(删除、更新)。库是轻量级且非常高效的,具有所有主要使用的 CRUD 操作。 在推荐的前 20 个 EF Core 扩展中被选中由微软。”

b) 或 EF 扩展 - UpdateFromQuery

_dbContext.Set<MyObjectEntity>().UpdateFromQuery( x => new MyObjectEntity{ Id=123, Quantity=100 });

资源:

https://entityframework-extensions.net/update-from-query

https://stackoverflow.com/a/63460251/12425844

为什么 UpdateFromQuery 比 SaveChanges、BulkSaveChanges 和 BulkUpdate 快?

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

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

于 2020-08-19T01:06:17.147 回答
1

我找到了一种无需任何 3rd 方包 的简单方法
:通过添加一个通用扩展方法SetValue,您可以简单地编写:

例子:

void Main()
{
    
    var dc = this; // context
    var p = dc.Purchases.Where(x=>x.Description.ToLower()=="bike")
                        .SetValue(w => w.Description = "Bicycle");
    p.Dump();
    dc.SubmitChanges();
}

如您所见,任何符合Where条件的值都可以显式设置为新值,因此这里Bike将替换为Bicycle. 之后您可以查询该表以查看真正持久的更改。

当然Where,如果您想更改所有记录,您也可以省略该语句,例如:

dc.Records.SetValue(x => x.Quantity = 100);
dc.SubmitChanges();

实体框架 (EF) / LINQ 跟踪这些更改,当您调用 .SubmitChanges() 时 - 如果您使用的是 LinqPad,您可以在 SQL 选项卡中看到 - 它将创建 SQL 代码,如下所示:

-- Region Parameters
DECLARE @p0 Int = 3
DECLARE @p1 VarChar(1000) = 'Bicycle'
-- EndRegion
UPDATE [Purchase]
SET [Description] = @p1
WHERE [ID] = @p0

对于小的更改,这是可以的,但对于大型表,它变得低效,因为它使用 ID 列来识别和更改记录,而不是 .SetValue 定义的 Description 列。

理论上 EF 可以对此进行优化,但正如您所见,它并没有这样做。因此,如果您想要真正的批量操作,您需要改为运行 SQL 命令或创建您通过 EF 调用的存储过程(用于复杂查询)。


扩展方法SetValue

这种扩展方法可以解决问题(不需要其他 3rd 方包):

// see: https://visualstudiomagazine.com/articles/2019/07/01/updating-linq.aspx
public static class Extensions
{
    public static IEnumerable<T> SetValue<T>(this IEnumerable<T> items, 
                                                  Action<T> updateMethod)
    {
        foreach (T item in items)
        {
            updateMethod(item);
        }
        return items;
    }
}

注意: 上面的示例使用Nutshell示例数据库,您可以通过点击此链接轻松创建它,并且代码是为LinqPad 6编写的,但可以轻松调整(LinqPad 6 使用 .NET Core,但您可以使用 LinqPad 5 进行尝试)非常适合 .NET 框架)。

于 2020-08-20T15:01:08.083 回答
0

在 EF 6 中,我们在每个表中都有 AddRange 方法。文件表明这种方法比使用许多添加方法要快得多。因此,可以使用一条 sql 语句将所有可更新记录插入临时表并批量更新主表。

编辑:本文档建议 AddRange 仅优化更改检测。它不会更改将更改应用于数据库的方式。

于 2020-07-25T17:09:40.760 回答
0

EFCore 中将包含内置方法BulkUpdate()BulkDelete方法,这些方法将在 EFCore 7.0 中提供

context.Customers.Where(...).BulkDelete();
context.Customers.Where(...).BulkUpdate(c => new Customer { Age = c.Age + 1 });
context.Customers.Where(...).BulkUpdate(c => new { Age = c.Age + 1 });
于 2022-02-05T10:52:06.047 回答
-1

批量更新可以通过简单的 EF 分三步完成,而不是单独的扩展方法:-

  • 首先加载所有实体。
  • Foreach 每个实体并更改其字段值。
  • Foreach 保存后,上下文更改一次。

这将在单个批次中发送多个更新查询。

于 2017-05-29T16:52:35.543 回答