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

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


当前回答

对于您在问题评论中的评论:

“…保存更改(每个记录)。。。"

这是你能做的最糟糕的事情!对每个记录调用SaveChanges()会大大降低批量插入的速度。我会做一些简单的测试,这很可能会提高性能:

在所有记录后调用SaveChanges()一次。例如,在100条记录之后调用SaveChanges()。例如,在100条记录之后调用SaveChanges(),并释放上下文并创建新的上下文。禁用更改检测

对于批量插入,我正在使用这样的模式进行工作和试验:

using (TransactionScope scope = new TransactionScope())
{
    MyDbContext context = null;
    try
    {
        context = new MyDbContext();
        context.Configuration.AutoDetectChangesEnabled = false;

        int count = 0;            
        foreach (var entityToInsert in someCollectionOfEntitiesToInsert)
        {
            ++count;
            context = AddToContext(context, entityToInsert, count, 100, true);
        }

        context.SaveChanges();
    }
    finally
    {
        if (context != null)
            context.Dispose();
    }

    scope.Complete();
}

private MyDbContext AddToContext(MyDbContext context,
    Entity entity, int count, int commitCount, bool recreateContext)
{
    context.Set<Entity>().Add(entity);

    if (count % commitCount == 0)
    {
        context.SaveChanges();
        if (recreateContext)
        {
            context.Dispose();
            context = new MyDbContext();
            context.Configuration.AutoDetectChangesEnabled = false;
        }
    }

    return context;
}

我有一个测试程序,它将560.000个实体(9个标量财产,没有导航财产)插入数据库。使用此代码,它只需不到3分钟即可工作。

为了提高性能,在“许多”记录之后调用SaveChanges()非常重要(“许多”约为100或1000)。它还提高了在SaveChanges之后释放上下文并创建新上下文的性能。这将从所有实体中清除上下文,SaveChanges不会这样做,实体仍然以状态Unchanged附加到上下文。正是由于上下文中附加实体的大小不断增加,才导致插入过程一步步变慢。因此,在一段时间后清除它是有帮助的。

以下是我的560000个实体的一些测量值:

commitCount=1,recreateContext=false:许多小时(这是您当前的过程)commitCount=100,recreateContext=false:超过20分钟commitCount=1000,recreateContext=false:242秒commitCount=10000,recreateContext=false:202秒commitCount=100000,recreateContext=false:199秒commitCount=1000000,recreateContext=false:内存不足异常commitCount=1,recreateContext=true:超过10分钟commitCount=10,recreateContext=true:241秒commitCount=100,recreateContext=true:164秒commitCount=1000,recreateContext=true:191秒

上述第一个测试中的行为是,性能非常非线性,并且随着时间的推移会大大降低。(“很多小时”是一个估计,我从未完成过这项测试,20分钟后我停在了50000个实体。)这种非线性行为在所有其他测试中都不那么重要。

其他回答

下面是使用实体框架和使用SqlBulkCopy类之间的性能比较,具体示例为:如何将复杂对象批量插入SQL Server数据库

正如其他人已经强调的,ORM不应用于批量操作。它们提供了灵活性、关注点分离和其他好处,但批量操作(批量读取除外)不是其中之一。

这种组合可以很好地提高速度。

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

秘密是插入到相同的空白暂存表中。插件快速发光。然后在主大表中运行一个单独的插入。然后截断临时表,为下一批做好准备。

ie.

insert into some_staging_table using Entity Framework.

-- Single insert into main table (this could be a tiny stored proc call)
insert into some_main_already_large_table (columns...)
   select (columns...) from some_staging_table
truncate table some_staging_table

我同意亚当·拉基斯的观点。SqlBulkCopy是将批量记录从一个数据源传输到另一数据源的最快方法。我用这个复制了20K张唱片,只用了不到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();
        }

    }
}

您可以使用Bulk包库。大容量插入1.0.0版本用于实体框架>=6.0.0的项目。

更多描述可在此处找到-Bulkoperation源代码