简体   繁体   English

如何批量更新实体框架中的记录?

[英]How to Bulk Update records in Entity Framework?

I am trying to bulk update records using Entity Framework.我正在尝试使用 Entity Framework 批量更新记录。 I have tried Entity Framework.Extensions Update method.我试过 Entity Framework.Extensions Update方法。

The Update method is able to bulk update for a set of records with same set of update values. Update方法能够批量更新具有相同更新值集的一组记录。

Example:例子:

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

We can bulk update all the above records by simple calling我们可以通过简单的调用批量更新以上所有记录

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

How can I bulk update each record with different quantity using Entityframework.Extensions or in any other approach, which completes the bulk update faster?如何使用Entityframework.Extensions或任何其他方法批量更新不同数量的每条记录,从而更快地完成批量更新?

If you don't want to use an SQL statement, you can use the Attach method in order to update an entity without having to load it first :如果不想使用 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;
    }
}

Use ExecuteSqlCommand :使用ExecuteSqlCommand

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

Or ExecuteStoreCommand :ExecuteStoreCommand

yourDbContext.ExecuteStoreCommand("UPDATE YourTABLE SET Quantity = {0} WHERE Id = {1}", quantity, id);

Use this way if you just want to modify few properties:如果您只想修改几个属性,请使用这种方式:

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();

a) EFCore.BulkExtensions - BatchUpdateAsync a) EFCore.BulkExtensions - BatchUpdateAsync

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

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

"EntityFrameworkCore extensions: Bulk operations (Insert, Update, Delete, Read, Upsert, Sync) and Batch (Delete, Update). Library is Lightweight and very Efficient, having all mostly used CRUD operation. Was selected in top 20 EF Core Extensions recommended by Microsoft ." “EntityFrameworkCore 扩展:批量操作(插入、更新、删除、读取、Upsert、同步)和批处理(删除、更新)。库轻量级且非常高效,所有主要使用 CRUD 操作。被选入推荐的前 20 名 EF Core 扩展由微软。”

b) Or EF Extensions - UpdateFromQuery b) 或 EF 扩展 - UpdateFromQuery

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

Resource:资源:

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

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

Why UpdateFromQuery is faster than SaveChanges, BulkSaveChanges, and BulkUpdate?为什么 UpdateFromQuery 比 SaveChanges、BulkSaveChanges 和 BulkUpdate 快?

UpdateFromQuery executes a statement directly in SQL such as UPDATE [TableName] SET [SetColumnsAndValues] WHERE [Key]. UpdateFromQuery 直接在 SQL 中执行语句,例如 UPDATE [TableName] SET [SetColumnsAndValues] WHERE [Key]。

Other operations normally require one or multiple database round-trips which makes the performance slower.其他操作通常需要一个或多个数据库往返,这会降低性能。

I found an easy way to do that without any 3rd party packages:我找到了一种简单的方法来做到这一点,而无需任何 3rd 方软件包:
By adding one generic extension method SetValue you can simply write:通过添加一个通用扩展方法SetValue您可以简单地编写:

Example:例子:

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();
}

As you can see, any value matching the Where condition can be set explicitly to a new value, so here Bike will be replaced by Bicycle .如您所见,任何匹配Where条件的值都可以显式设置为新值,因此这里Bike将被Bicycle替换。 You can query the table afterwards to see the changes really persisted.之后您可以查询该表以查看真正持续的更改。

Of course, you could also omit the Where statement, if you want to change all records like:当然,如果要更改所有记录,也可以省略Where语句,例如:

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

Entity framework (EF) / LINQ tracks those changes and when you call .SubmitChanges() - as you can see in the SQL tab if you're using LinqPad - it will create SQL code as follows:实体框架 (EF) / LINQ 会跟踪这些更改,当您调用 .SubmitChanges() 时 - 正如您在 SQL 选项卡中看到的那样,如果您使用的是 LinqPad - 它将创建 SQL 代码如下:

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

For small changes, this is ok, but for large tables it is becoming inefficient, because it uses the ID column to identify and change a record, and not the Description column as defined by .SetValue.对于小的更改,这是可以的,但是对于大表,它变得效率低下,因为它使用 ID 列来标识和更改记录,而不是 .SetValue 定义的 Description 列。

Theoretically EF could optimize this, but as you can see, it doesn't do it.理论上 EF 可以优化这一点,但正如您所见,它并没有这样做。 So if you want true bulk operations you need to run a SQL command instead or create a stored procedure (for complex queries) which you're calling via EF.因此,如果您想要真正的批量操作,您需要运行 SQL 命令或创建一个存储过程(用于复杂查询),您通过 EF 调用该过程。


Extension method SetValue扩展方法SetValue

This extension method does the trick ( no other 3rd party packages required ):这个扩展方法可以解决问题(不需要其他 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;
    }
}

Note: The example above uses the Nutshell example database, which you can easily create by following this link and the code is written for LinqPad 6 but can be adapted easily (LinqPad 6 uses .NET Core, but you can try it with LinqPad 5 as well for the .NET Framework).注意:上面的例子使用了Nutshell示例数据库,你可以通过这个链接轻松创建,代码是为LinqPad 6编写的,但可以很容易地改编(LinqPad 6 使用 .NET Core,但你可以用 LinqPad 5 作为.NET 框架很好)。

In EF 6 we have AddRange method in each table.在 EF 6 中,我们在每个表中都有 AddRange 方法。 Documents suggest this method is much faster than using many add methods.文档表明这种方法比使用许多添加方法要快得多。 So, one can insert all updatable records in a temp table and batch update main table using a single sql statement.因此,可以使用单个 sql 语句将所有可更新记录插入临时表并批量更新主表。

EDIT: This Document suggests that AddRange only optimizes change detection.编辑:本文档建议 AddRange 仅优化更改检测。 It does not change how the changes are applied to the database.它不会更改将更改应用于数据库的方式。

There will be the built-in BulkUpdate()<\/code> and BulkDelete<\/code> methods in EFCore which will be delivered in EFCore 7.0 EFCore 中将有内置的BulkUpdate()<\/code>和BulkDelete<\/code>方法,这些方法将在 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 });

Bulk Update can be done in three steps with simple EF instead of separate extension methods :-可以使用简单的 EF 而不是单独的扩展方法分三步完成批量更新:-

  • Load all the entities first.首先加载所有实体。
  • Foreach on each entity and change its field values. Foreach 对每个实体并更改其字段值。
  • After Foreach save the context changes once. Foreach 保存上下文更改一次后。

This will send multiple Update queries in single batch.这将在单个批次中发送多个更新查询。

Posible by using UpdateRange([NotNullAttribute] params TEntity[] entities)可能通过使用UpdateRange([NotNullAttribute] params TEntity[] entities)

       private void bulkTagUpdate()
        {
            RfidTag tag = new RfidTag
            {
                Id = 1,
                Status ="tight",
                TagId = "234353444",
                LocationId = "1",
                CreatedAt = DateTime.Now,
                UpdatedAt = DateTime.Now,
            };
            RfidTag tag2 = new RfidTag
            {
                Id = 2,
                Status = "tight",
                TagId = "3454544",
                LocationId = "1",
                CreatedAt = DateTime.Now,
                UpdatedAt = DateTime.Now,
            };

            List<RfidTag> tagList = new List<RfidTag>();
            tagList.Add(tag);
            tagList.Add(tag2);

            using (rfid_statusContext context = new rfid_statusContext())
            {
                context.RfidTags.UpdateRange(tagList);
                context.SaveChanges();
                MessageBox.Show("Update successfull !");
            }
        }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM