'How to Bulk Update records in Entity Framework?

I am trying to bulk update records using Entity Framework. I have tried Entity Framework.Extensions Update method.

The Update method is able to bulk update for a set of records with same set of update values.

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?



Solution 1:[1]

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 :

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

Solution 2:[2]

Use ExecuteSqlCommand:

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

Or ExecuteStoreCommand:

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

Solution 3:[3]

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

Solution 4:[4]

a) EFCore.BulkExtensions - BatchUpdateAsync

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

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."

b) Or EF Extensions - UpdateFromQuery

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

Resource:

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

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

Why UpdateFromQuery is faster than SaveChanges, BulkSaveChanges, and BulkUpdate?

UpdateFromQuery executes a statement directly in SQL such as UPDATE [TableName] SET [SetColumnsAndValues] WHERE [Key].

Other operations normally require one or multiple database round-trips which makes the performance slower.

Solution 5:[5]

I found an easy way to do that without any 3rd party packages:
By adding one generic extension method SetValue you can simply write:

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. 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:

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:

-- 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.

Theoretically EF could optimize this, but as you can see, it doesn't do it. 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.


Extension method SetValue

This extension method does the trick (no other 3rd party packages required):

// 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).

Solution 6:[6]

There will be the built-in BulkUpdate() and BulkDelete methods in EFCore which will be delivered in 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 });

Solution 7:[7]

In EF 6 we have AddRange method in each table. 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.

EDIT: This Document suggests that AddRange only optimizes change detection. It does not change how the changes are applied to the database.

Solution 8:[8]

Bulk Update can be done in three steps with simple EF instead of separate extension methods :-

  • Load all the entities first.
  • Foreach on each entity and change its field values.
  • After Foreach save the context changes once.

This will send multiple Update queries in single batch.

Solution 9:[9]

Posible by using 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 !");
            }
        }

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2
Solution 3
Solution 4
Solution 5
Solution 6 Majid Shahabfar
Solution 7
Solution 8 Jaswinder
Solution 9 SL codegen