在实体框架中最快的插入方式

ε祈祈猫儿з 提交于 2020-01-07 11:48:03

【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>

我正在寻找插入实体框架的最快方法。

我之所以这样问,是因为您有一个活动的TransactionScope且插入量很大(超过4000个)。 它可能会持续10分钟以上(事务默认超时),这将导致事务不完整。


#1楼

这种组合足以提高速度。

context.Configuration.AutoDetectChangesEnabled = false;
context.Configuration.ValidateOnSaveEnabled = false;

#2楼

我同意亚当·拉基斯(Adam Rackis)的观点。 SqlBulkCopy是将批量记录从一个数据源传输到另一个数据源的最快方法。 我用它来复制2万条记录,花费了不到3秒的时间。 看下面的例子。

public static void InsertIntoMembers(DataTable dataTable)
{           
    using (var connection = new SqlConnection(@"data source=;persist security info=True;user id=;password=;initial catalog=;MultipleActiveResultSets=True;App=EntityFramework"))
    {
        SqlTransaction transaction = null;
        connection.Open();
        try
        {
            transaction = connection.BeginTransaction();
            using (var sqlBulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.TableLock, transaction))
            {
                sqlBulkCopy.DestinationTableName = "Members";
                sqlBulkCopy.ColumnMappings.Add("Firstname", "Firstname");
                sqlBulkCopy.ColumnMappings.Add("Lastname", "Lastname");
                sqlBulkCopy.ColumnMappings.Add("DOB", "DOB");
                sqlBulkCopy.ColumnMappings.Add("Gender", "Gender");
                sqlBulkCopy.ColumnMappings.Add("Email", "Email");

                sqlBulkCopy.ColumnMappings.Add("Address1", "Address1");
                sqlBulkCopy.ColumnMappings.Add("Address2", "Address2");
                sqlBulkCopy.ColumnMappings.Add("Address3", "Address3");
                sqlBulkCopy.ColumnMappings.Add("Address4", "Address4");
                sqlBulkCopy.ColumnMappings.Add("Postcode", "Postcode");

                sqlBulkCopy.ColumnMappings.Add("MobileNumber", "MobileNumber");
                sqlBulkCopy.ColumnMappings.Add("TelephoneNumber", "TelephoneNumber");

                sqlBulkCopy.ColumnMappings.Add("Deleted", "Deleted");

                sqlBulkCopy.WriteToServer(dataTable);
            }
            transaction.Commit();
        }
        catch (Exception)
        {
            transaction.Rollback();
        }

    }
}

#3楼

如果您的Add()实体依赖于上下文中的其他预加载实体(例如,导航属性),则Dispose()上下文会产生问题

我使用类似的概念来使上下文保持较小以实现相同的性能

但是我没有分离Dispose()上下文并重新创建,而是简单地分离了已经SaveChanges()的实体

public void AddAndSave<TEntity>(List<TEntity> entities) where TEntity : class {

const int CommitCount = 1000; //set your own best performance number here
int currentCount = 0;

while (currentCount < entities.Count())
{
    //make sure it don't commit more than the entities you have
    int commitCount = CommitCount;
    if ((entities.Count - currentCount) < commitCount)
        commitCount = entities.Count - currentCount;

    //e.g. Add entities [ i = 0 to 999, 1000 to 1999, ... , n to n+999... ] to conext
    for (int i = currentCount; i < (currentCount + commitCount); i++)        
        _context.Entry(entities[i]).State = System.Data.EntityState.Added;
        //same as calling _context.Set<TEntity>().Add(entities[i]);       

    //commit entities[n to n+999] to database
    _context.SaveChanges();

    //detach all entities in the context that committed to database
    //so it won't overload the context
    for (int i = currentCount; i < (currentCount + commitCount); i++)
        _context.Entry(entities[i]).State = System.Data.EntityState.Detached;

    currentCount += commitCount;
} }

如果需要,可以使用try catch和TrasactionScope()包装,此处不显示它们以保持代码的清洁


#4楼

我已经研究了Slauma的答案(这太了不起了,谢谢这个主意的人),并且我减小了批量大小,直到达到最佳速度为止。 查看Slauma的结果:

  • commitCount = 1,recreateContext = true:超过10分钟
  • commitCount = 10,recreateContext = true:241秒
  • commitCount = 100,recreateContext = true:164秒
  • commitCount = 1000,recreateContext = true:191秒

可以看出,当从1移到10,从10移到100时,速度会增加,但是插入速度从100移到1000时又会下降。

因此,我专注于将批量大小减小到介于10到100之间的值时发生的情况,这是我的结果(我使用的行内容不同,所以我的时间值也不同):

Quantity    | Batch size    | Interval
1000    1   3
10000   1   34
100000  1   368

1000    5   1
10000   5   12
100000  5   133

1000    10  1
10000   10  11
100000  10  101

1000    20  1
10000   20  9
100000  20  92

1000    27  0
10000   27  9
100000  27  92

1000    30  0
10000   30  9
100000  30  92

1000    35  1
10000   35  9
100000  35  94

1000    50  1
10000   50  10
100000  50  106

1000    100 1
10000   100 14
100000  100 141

根据我的结果,批量的实际最佳值约为30。 它小于10和100。问题是,我不知道为什么30是最佳值,也找不到任何合理的解释。


#5楼

您是否曾经尝试通过后台工作人员或任务插入?

就我而言,我将插入7760个寄存器,这些寄存器分布在具有外键关系的182个不同表中(按NavigationProperties)。

没有任务,花了2分半钟。 在一个Task( Task.Factory.StartNew(...) )中,花费了15秒。

我只是在将所有实体添加到上下文后才执行SaveChanges() 。 (以确保数据完整性)

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!