c# - Entity Framework - 批量更新/插入/删除缓慢

标签 c# .net performance entity-framework

我已经编写了一种使用 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 方法的任何地方都可能发生同样的错误。

待添加

这里的一切看起来都很完美。

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

待删除

对于您删除的每个实体,您都会进行一次数据库往返(自从您使用 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);
});

待修改

我不确定 CustomEqualityComparer 方法到底做了什么,但同样,您可能在对 listMultiple IQueryable 执行多个查询时遇到问题。

保存更改

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

因此,如果您需要对 50000 行执行操作,则执行 50000 次数据库往返是 INSANE

免责声明:我是Entity Framework Extensions的所有者

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

例如,BulkSaveChanges 与 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;
});

关于c# - Entity Framework - 批量更新/插入/删除缓慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42714897/

相关文章:

c# - 动态添加具有指向动态生成的自签名证书的 HTTPS 绑定(bind)的网站

c# - 如何使用 SendMessage (WM_COPYDATA) 从 C# 向 C++ (MESSAGE ONLY WINDOW) 发送消息?

performance - 有 VB6 性能工具吗?

php - ob_flush 需要很长时间才能执行

Java 重绘在某些情况下很慢

c# - 在连接打开时更改串行端口波特率

c# - 如何在wpf中获取文本框的绑定(bind)

c# - Directory.GetFiles(...) 模式 *.???返回有趣的结果

c# - 在 .Net 3.5 中实现随机提供程序

java - 当您在 Java 中实现接口(interface)时,它是显式的还是隐式的?