.NET实体框架插入与批量插入

时间:2013-06-12 14:31:03

标签: c# .net entity-framework

当我使用我的xxxContext对象并向表发出几个Add时,那么SaveChanges()实体框架如何将其解析为SQL?它只是循环执行插入xxx 或者如果有数百行,它是否足够智能发出批量插入命令?

奖金问题:如果它没有发出批量插入有没有办法强制它,所以我的数据库性能不会被单独的插入杀死?或者批量到临时表,然后像Upsert一样合并到原始表?

6 个答案:

答案 0 :(得分:6)

任何ORM工具的垮台都是“喋喋不休”。大多数时候这都足够了。有时它不是。

简短的回答是“不”。

这就是为什么我有时会选择IDataReader而不是EF或NHibernate等。 对于批量插入操作,我将xml发送到存储过程,然后将其粉碎并从那里批量插入/更新或合并。

所以即使我使用ORM,我创建的域库也不依赖于EF(或NHibernate)......因此我在某些情况下通过ORM会有一个“安全阀”。

答案 1 :(得分:4)

实体框架中存在若干改进的机会:

集:

yourContext.Configuration.AutoDetectChangesEnabled = false;
yourContext.Configuration.ValidateOnSaveEnabled = false;

在100个插页的包中执行SaveChanges() ...尝试使用1000并查看更改。

因为在所有这些插入期间,上下文是相同的,所以您可以每1000次插入重建上下文对象。 var yourContext = new YourContext();

在我的导入数据流程中进行此改进,从7分钟到6秒。

实际数字......在你的情况下不可能是100的1000 ...尝试并调整它。

答案 2 :(得分:3)

如果您的插入查询是ANSI SQL,或者您不关心使用您的代码库支持多重数据库,您仍然有后门从EF创建ADO.NET提供程序并执行一些原始SQL调用

https://stackoverflow.com/a/1579220/98491

我会做这样的事情

private void BulkInsert(IEnumerable<Person> Persons)
{

    // use the information in the link to get your connection
    DbConnection conn = ...
    using (DbCommand cmd = conn.CreateCommand())
    {

       var sb = new StringBuilder();
       sb.Append("INSERT INTO person (firstname, lastname) VALUES ");
       var count = 0;
       foreach(var person in persons)
       {
           if (count !=0) sb.Append(",");
           sb.Append(GetInsertCommand(person, count++, cmd));
       }

       if (count > 0)
       {
           cmd.CommandText = sb.ToString();
           cmd.ExecuteNonQuery();
       }
    }



   if (sb.Length > 0)
       ExecuteNonQuery(sb.ToString());
}

private string GetInsertCommand(Person person, int count, DbCommand cmd)
{
    var firstname = "@firstname" + count.ToString();
    var lastname = "@lastname" + count.ToString();
    cmd.Parameters.Add(firstname, person.Firstname);
    cmd.Parameters.Add(lastname, person.Firstname);
    return String.Format("({0},{1})", firstname, lastname);
}

我必须承认我没有对它进行过测试,但这应该是一种快速而肮脏的方法,可以绕过一些批量插入的EF,直到批量插入是核心的一部分。

<强>更新

只是一个简单的想法。您是否尝试过迁移命名空间中的...方法? 也许这个是批量插入,没有考虑它但是值得一试:

private void BatchInsert(IEnumerable<Person> persons)
{
    context.Persons.AddOrUpdate(persons);
}

我知道如果你定义一个像AddOrUpdate(p => p.Firstname, persons)这样的Key列,这个方法可能会很慢,但我猜不指定它,那应该是所有插入(不保证)

答案 3 :(得分:3)

您可以使用bulk insert extension

用法:

using EntityFramework.BulkInsert.Extensions;

context.BulkInsert(myEntities);

使用DbContext:

using (var ctx = GetContext())
{
    using (var transactionScope = new TransactionScope())
    {
        // some stuff in dbcontext    
        ctx.BulkInsert(entities);    
        ctx.SaveChanges();
        transactionScope.Complete();
    }
}

答案 4 :(得分:0)

我担心EF不支持批量插入或更新。正如您所说,EF目前会生成一堆Insert命令并单独执行它们(但所有这些命令都包含在一个事务中)。有一些计划实施批处理,不确定最近是否有一些进展。希望在EF6,但我有点怀疑。

您可以在此discussion中阅读更多内容。

答案 5 :(得分:-1)

从.NET存储库中插入ASP .NET核心版快速方法。

public virtual void AddRangeFastAndCommit(IEnumerable<T> entities)
{
    MyDbContext localContext = new MyDbContext(_context.Options);
    localContext.ChangeTracker.AutoDetectChangesEnabled = false;

    foreach (var entity in entities)
    {
        localContext.Add(entity);
    }

    localContext.SaveChanges();
    localContext.Dispose();
}