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

C# 实体框架-批量更新/插入/删除缓慢,c#,.net,performance,entity-framework,C#,.net,Performance,Entity Framework,我已经编写了一个方法,使用实体框架将行批量更新/删除/插入到数据库表中。我已将代码粘贴到下面。DBTable有23列,DBTableRow是一个类,它的属性映射到DBTable的每一列。该方法的输入参数是IEnumerables,在IEnumerables上使用自定义相等比较器进行一些比较,以得到需要添加、删除或修改的行列表。通常,可枚举的大小可以达到50000-60000 我面临的问题是该方法的缓慢性。对于200行的网络(跨越所有操作-添加、删除和更新),需要30分钟。对于一个2000行的网络

我已经编写了一个方法,使用实体框架将行批量更新/删除/插入到数据库表中。我已将代码粘贴到下面。DBTable有23列,DBTableRow是一个类,它的属性映射到DBTable的每一列。该方法的输入参数是IEnumerables,在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;
}
private void InsertIntoDB(DbContext DbContext,IEnumerable fromLatestB,IEnumerable olderB,
IEnumerable tobeddedb,IEnumerable tobedeleteb,
IEnumerable toBeModifiedB,IQueryable listMultiple)
{
dbContext.Configuration.AutoDetectChangesEnabled=false;
dbContext.Configuration.ValidateOnSaveEnabled=false;
int-TypeId=30;
if(tobeadedb!=null&&tobeadedb.Any())
tobeadedb.ToList().ForEach(s=>dbContext.DBTable.Add);
if(toBeDeletedB!=null&&toBeDeletedB.Any())
{
toBeDeletedB.ToList().ForEach(s=>
{
如果(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);
});
}
}
其他的
{
dbContext.DBTable.Attach;
dbContext.DBTable.Remove;
}
});
}
if(toBeModifiedB!=null&&toBeModifiedB.Any())
{
var eqComp=新的CustomEqualityComparer(listMultiple);
var toBeModifiedNew=fromLatestB.Intersect(olderB,newcustomequalitycomparer(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;
}

您看过使用()和()方法吗?我相信这是EF6的新功能。

您看过使用()和()方法吗?我相信EF6是新的。

任何

Any方法看起来不错,因为您检查了可枚举文件是否包含实体,但通常在可枚举文件上非常糟糕,因为您可能会多次枚举

例如,在delete部分中,需要两个数据库往返

  • 对于任何方法,一次为一次
  • 一次用于ToList方法
例如:

if (toBeDeletedB != null && toBeDeletedB.Any())
{
    toBeDeletedB.ToList().ForEach(s =>
因此,在调用Any方法之前执行ToList

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

    toBeDeletedBList.ForEach(s => ...
同样的错误可能发生在你使用任何方法的任何地方

领先

这里的一切似乎都很完美

由于将AutoDetectChangesEnabled设置为false,因此Add&&AddRange将提供大致相同的性能

删除

对于您删除的每个实体,都会进行一次数据库往返(两次,因为您使用了任何实体)

这是一个性能问题:

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

  • 从toBeDeletedB获取类型==TypeId的所有项
  • 使用contains方法或类似方法来减少所需的数据库往返。根据排序规则,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);
});
// 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;
});
toBeModified

我不确定CustomEqualityComparer方法到底做了什么,但同样,在listMultiple IQueryable上执行多个查询可能会有问题

保存更改

对于需要插入、更新或删除的每个实体,都会执行数据库往返

所以,如果您需要对50000行执行操作,那么使用执行50000数据库往返是不明智的

免责声明:我是

此库允许您执行批量操作并提高性能

例如,BulkSaveChanges与SaveChanges完全相同,但由于大大减少了所需的数据库往返,因此速度要快得多

  • 批量保存更改
  • 批量插入
  • 批量删除
  • 批量更新
  • 批量合并
范例

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

Any方法看起来不错,因为您检查了可枚举文件是否包含实体,但通常在可枚举文件上非常糟糕,因为您可能会多次枚举

例如,在delete部分中,需要两个数据库往返

  • 对于任何方法,一次为一次
  • 一次用于ToList方法
例如:

if (toBeDeletedB != null && toBeDeletedB.Any())
{
    toBeDeletedB.ToList().ForEach(s =>
因此,在调用Any方法之前执行ToList

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

    toBeDeletedBList.ForEach(s => ...
同样的错误可能发生在你使用任何方法的任何地方

领先

这里的一切似乎都很完美

由于将AutoDetectChangesEnabled设置为false,因此Add&&AddRange将提供大致相同的性能

删除

对于您删除的每个实体,都要创建一个数据库