簡體   English   中英

如何批量更新實體框架中的記錄?

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

我正在嘗試使用 Entity Framework 批量更新記錄。 我試過 Entity Framework.Extensions Update方法。

Update方法能夠批量更新具有相同更新值集的一組記錄。

例子:

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

我們可以通過簡單的調用批量更新以上所有記錄

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

如何使用Entityframework.Extensions或任何其他方法批量更新不同數量的每條記錄,從而更快地完成批量更新?

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

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

如果您只想修改幾個屬性,請使用這種方式:

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

_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]。

其他操作通常需要一個或多個數據庫往返,這會降低性能。

我找到了一種簡單的方法來做到這一點,而無需任何 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() 時 - 正如您在 SQL 選項卡中看到的那樣,如果您使用的是 LinqPad - 它將創建 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 框架很好)。

在 EF 6 中,我們在每個表中都有 AddRange 方法。 文檔表明這種方法比使用許多添加方法要快得多。 因此,可以使用單個 sql 語句將所有可更新記錄插入臨時表並批量更新主表。

編輯:本文檔建議 AddRange 僅優化更改檢測。 它不會更改將更改應用於數據庫的方式。

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

可以使用簡單的 EF 而不是單獨的擴展方法分三步完成批量更新:-

  • 首先加載所有實體。
  • Foreach 對每個實體並更改其字段值。
  • Foreach 保存上下文更改一次后。

這將在單個批次中發送多個更新查詢。

可能通過使用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