簡體   English   中英

實體框架 - 批量更新/插入/刪除緩慢

[英]Entity Framework - Bulk update/insert/delete slowness

我編寫了一個使用Entity Framework批量更新/刪除/插入數據庫表的方法。 我已粘貼下面的代碼。 DBTable有23列,DBTableRow是一個具有映射到DBTable每列的屬性的類。 該方法的輸入參數是IEnumerables,在其上使用自定義相等比較器進行一些比較,以得到需要添加,刪除或修改的行列表。 通常,可枚舉的大小可以達到50000-60000。

我面臨的問題是方法的緩慢。 對於200行的凈額(在所有操作中 - 添加,刪除和更新),需要30分鍾。 對於2000行的凈額,它已經花費了近6個小時並且還沒有完成。

專家能否指出代碼中的性能瓶頸? 非常感謝...

private void InsertIntoDB(DbContext dbContext, IEnumerable<DBTableRow> fromLatestB, IEnumerable<DBTableRow> olderB,
                                             IEnumerable<DBTableRow> toBeAddedB, IEnumerable<DBTableRow> toBeDeletedB,
                                             IEnumerable<DBTableRow> toBeModifiedB, IQueryable<int> listMultiple)
{
    dbContext.Configuration.AutoDetectChangesEnabled = false;
    dbContext.Configuration.ValidateOnSaveEnabled = false;

    int TypeId = 30;    

    if (toBeAddedB != null && toBeAddedB.Any())
        toBeAddedB.ToList().ForEach(s => dbContext.DBTable.Add(s));
    if (toBeDeletedB != null && toBeDeletedB.Any())
    {
        toBeDeletedB.ToList().ForEach(s =>
        {
            if (s.Type == TypeId)
            {
                var rlRows = dbContext.DBTable.Where(x => x.Type == TypeId && x.Url.Equals(s.Url, StringComparison.OrdinalIgnoreCase));
                if (rlRows != null && rlRows.Any())
                {
                    rlRows.ToList().ForEach(y =>
                    {
                        if (dbContext.Entry(y).State == EntityState.Detached)
                            dbContext.DBTable.Attach(y);

                            dbContext.DBTable.Remove(y);
                    });
                }
            }
            else
            {
                dbContext.DBTable.Attach(s);
                dbContext.DBTable.Remove(s);
            }
        });
    }
    if (toBeModifiedB != null && toBeModifiedB.Any())
    {
        var eqComp = new CustomEqualityComparer(listMultiple);
        var toBeModifiedNew = fromLatestB.Intersect(olderB, new CustomEqualityComparer(true, listMultiple));
        toBeModifiedB.ToList().ForEach(x =>
        {
            var rowNew = ReturnRowFromModifiedNewList();

            if (rowNew != null)
            {
                x.Type = rowNew.Type;
                x.Url = rowNew.Url;
                x.Data = rowNew.Data;
                x.LastModified = DateTime.UtcNow;

                dbContext.Entry(x).State = EntityState.Modified;
            }
        });
    }

    dbContext.SaveChanges();

    dbContext.Configuration.AutoDetectChangesEnabled = true;
    dbContext.Configuration.ValidateOnSaveEnabled = true;
}

任何

Any方法看起來很棒,因為你檢查枚舉是否包含實體但通常在枚舉上非常糟糕,因為你可能會枚舉多次。

例如,在刪除部分中,需要兩次數據庫往返。

  • 一旦為Any方法
  • 一次為ToList方法

例:

if (toBeDeletedB != null && toBeDeletedB.Any())
{
    toBeDeletedB.ToList().ForEach(s =>

因此在調用Any方法之前執行ToList

if (toBeDeletedB != null)
{
    var toBeDeletedBList = toBeDeletedB.ToList();

    toBeDeletedBList.ForEach(s => ...

在使用Any方法的任何地方都會發生同樣的錯誤。

toBeAdded

這里的一切看起來都很完美

因為您將AutoDetectChangesEnabled設置為false,所以Add && AddRange將提供大致相同的性能。

toBeDeleted

對於您刪除的每個實體,您都會進行數據庫往返(因為您使用Any)兩次

此行是性能問題:

var rlRows = dbContext.DBTable.Where(x => x.Type == TypeId && x.Url.Equals(s.Url, StringComparison.OrdinalIgnoreCase));

你應該改為:

  • 從toBeDeletedB獲取Type == TypeId的所有項目
  • 使用包含方法或類似方法來減少所需的數據庫往返。 取決於排序規則,contains方法可能無效

var toBeDeletedBList = toBeDeletedB.ToList();

var listA = toBeDeletedBList.Where(x => x.Type == TypeId);
var listB = toBeDeletedBList.Where(x => x.Type != TypeId);

var rlRows = dbContext.DBTable.Where(x => x.Type == typeId && listA.Contains(s.Url);

listB.ForEach(s => {
    dbContext.DBTable.Attach(s);
    dbContext.DBTable.Remove(s);
});

toBeModified

我不確定CustomEqualityComparer方法究竟做了什么,但同樣,您可能在listMultiple IQueryable上執行多個查詢時遇到問題。

保存更改

對於需要插入,更新或刪除的每個實體,都會執行數據庫往返。

因此,如果需要在50000行上執行操作,則使用INSANE執行50000數據庫往返

免責聲明 :我是Entity Framework Extensions的所有者

此庫允許您執行批量操作並提高性能。

例如,BulkSaveChanges與SaveChanges完全相同,但通過大幅減少所需的數據庫往返速度更快。

  • 批量SaveChanges
  • 批量插入
  • 批量刪除
  • 批量更新
  • 批量合並

// Easy to use
context.BulkSaveChanges();

// Easy to customize
context.BulkSaveChanges(bulk => bulk.BatchSize = 100);

// Perform Bulk Operations
context.BulkDelete(endItems);
context.BulkInsert(endItems);
context.BulkUpdate(endItems);

// Customize Primary Key
context.BulkMerge(endItems, operation => {
   operation.ColumnPrimaryKeyExpression = 
        endItem => endItem.Code;
});

你看過使用AddRange ()和RemoveRange ()方法嗎? 我相信EF6的新功能。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM