简体   繁体   English

实体框架 - 批量更新/插入/删除缓慢

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

I have written a method to bulk update/delete/insert rows into a database table using Entity Framework. 我编写了一个使用Entity Framework批量更新/删除/插入数据库表的方法。 I have pasted the code below. 我已粘贴下面的代码。 The DBTable has 23 columns and the DBTableRow is a class which has properties that map to each column of the DBTable. DBTable有23列,DBTableRow是一个具有映射到DBTable每列的属性的类。 The input parameters to the method are IEnumerables, on which some comparison is done using a custom equality comparer to arrive at the list of rows that need to be added, deleted or modified. 该方法的输入参数是IEnumerables,在其上使用自定义相等比较器进行一些比较,以得到需要添加,删除或修改的行列表。 Typically, the enumerable can get to a size of 50000-60000. 通常,可枚举的大小可以达到50000-60000。

The problem I am facing is with the method's slowness. 我面临的问题是方法的缓慢。 For a net of 200 rows (across all operations - add, delete and update), it takes 30 minutes. 对于200行的净额(在所有操作中 - 添加,删除和更新),需要30分钟。 For a net of 2000 rows, it has taken close to 6 hours and hasn't finished yet. 对于2000行的净额,它已经花费了近6个小时并且还没有完成。

Could the experts point out the performance bottlenecks in the code? 专家能否指出代码中的性能瓶颈? Thanks a lot in advance... 非常感谢...

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 任何

The Any method look great since you check if the enumerable contains entities but are normally very bad on enumerable since you may enumerate more than once. Any方法看起来很棒,因为你检查枚举是否包含实体但通常在枚举上非常糟糕,因为你可能会枚举多次。

By example, in the delete part, two database round trip is required. 例如,在删除部分中,需要两次数据库往返。

  • Once for the Any method 一旦为Any方法
  • Once for the ToList method 一次为ToList方法

Example: 例:

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

So perform the ToList before calling the Any method 因此在调用Any方法之前执行ToList

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

    toBeDeletedBList.ForEach(s => ...

The same mistake can happen everywhere you are using the Any method. 在使用Any方法的任何地方都会发生同样的错误。

toBeAdded toBeAdded

Everything seems perfect here. 这里的一切看起来都很完美

Because you set AutoDetectChangesEnabled to false, Add && AddRange will provide around the same performance. 因为您将AutoDetectChangesEnabled设置为false,所以Add && AddRange将提供大致相同的性能。

toBeDeleted toBeDeleted

For every entity you delete, you make a database round-trip (twice since you use Any) 对于您删除的每个实体,您都会进行数据库往返(因为您使用Any)两次

This line is a performance issue: 此行是性能问题:

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

You should instead: 你应该改为:

  • Take all items with the Type == TypeId from toBeDeletedB 从toBeDeletedB获取Type == TypeId的所有项目
  • Use contains methods or similar to reduce database round-trip required. 使用包含方法或类似方法来减少所需的数据库往返。 The contains method may not work depending on collations 取决于排序规则,contains方法可能无效

Example

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 toBeModified

I'm not sure exactly what the CustomEqualityComparer method do, but again, you may have an issue with performing multiple queries on the listMultiple IQueryable. 我不确定CustomEqualityComparer方法究竟做了什么,但同样,您可能在listMultiple IQueryable上执行多个查询时遇到问题。

SaveChanges 保存更改

For every entity you need to insert, update, or delete, a database round-trip is performed. 对于需要插入,更新或删除的每个实体,都会执行数据库往返。

So you if need to perform operation on 50000 rows, 50000 database round-trip is performed with is INSANE 因此,如果需要在50000行上执行操作,则使用INSANE执行50000数据库往返

Disclaimer : I'm the owner of Entity Framework Extensions 免责声明 :我是Entity Framework Extensions的所有者

This library allows you to perform bulk operations and improve performance. 此库允许您执行批量操作并提高性能。

By example, the BulkSaveChanges is exactly like SaveChanges but way faster by dramatically reducing the database round-trip required. 例如,BulkSaveChanges与SaveChanges完全相同,但通过大幅减少所需的数据库往返速度更快。

  • Bulk SaveChanges 批量SaveChanges
  • Bulk Insert 批量插入
  • Bulk Delete 批量删除
  • Bulk Update 批量更新
  • Bulk Merge 批量合并

Example

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

Have you looked at using the AddRange () & RemoveRange () methods? 你看过使用AddRange ()和RemoveRange ()方法吗? New in EF6 I believe. 我相信EF6的新功能。

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

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