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

vsdwdz23  于 2023-02-27  发布在  其他
关注(0)|答案(9)|浏览(222)

我正在尝试使用实体框架批量更新记录。我已尝试实体框架。扩展Update方法。
Update方法能够批量更新具有相同更新值集的一组记录。
示例:

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

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

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

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

3bygqnnd

3bygqnnd1#

如果不想使用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;
    }
}
jogvjijk

jogvjijk2#

使用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);
pbwdgjma

pbwdgjma3#

EFCore中有内置的ExecuteUpdateExecuteDelete方法,这些方法是由EFCore 7.0提供的

context.Customers.Where(...).ExecuteDeleteAsync();
context.Customers.Where(...).ExecuteUpdateAsync(c => new Customer { Age = c.Age + 1 });
context.Customers.Where(...).ExecuteUpdateAsync(c => new { Age = c.Age + 1 });
yvt65v4c

yvt65v4c4#

如果只想修改少数属性,请使用此方法:

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

lztngnrs5#

a)E核心.批量扩展-批处理更新异步

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

https://github.com/borisdj/EFCore.BulkExtensions
“实体框架核心扩展:批量操作(插入、更新、删除、读取、上插、同步)和批处理(删除、更新)。库是轻量级的,非常高效,具有所有最常用的CRUD操作。被Microsoft推荐的前20个EF核心扩展选中。”

B)或EF扩展-从查询更新

_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比保存更改、批量保存更改和批量更新快?
UpdateFromQuery直接在SQL中执行语句,例如UPDATE [表名] SET [设置列和值] WHERE [键]。
其他操作通常需要一次或多次数据库往返,这会降低性能。

ibrsph3r

ibrsph3r6#

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

3bygqnnd

3bygqnnd7#

在EF 6中我们在每个表中都有AddRange方法。文档建议这个方法比使用许多add方法要快得多。因此,可以在一个临时表中插入所有可更新的记录,并使用一条sql语句批量更新主表。
编辑:这个Document建议AddRange只优化变更检测,它不改变变更应用到数据库的方式。

vfhzx4xs

vfhzx4xs8#

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

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

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

h22fl7wq

h22fl7wq9#

可能通过使用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 !");
            }
        }

相关问题