11

当我使用我的 xxxContext 对象并向表发出多个添加时,SaveChanges() 实体框架如何将其解析为 SQL?它会循环插入到 xxx中还是如果有数百行,是否足够聪明地发出批量插入命令?

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

4

6 回答 6

7

任何 ORM 工具的缺点是它很“健谈”。大多数时候这已经足够了。有时不是。

最简洁的答案是不”。

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

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

于 2013-06-12T14:36:59.983 回答
4

Entity Framework 有几个改进的机会:

放:

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

以 100 个插入的包装执行SaveChanges()...尝试 1000 并查看更改。

由于在所有这些插入过程中,上下文是相同的,因此您可以每 1000 次插入重建上下文对象。var yourContext = new YourContext();

在我的导入数据过程中进行此改进,将其从 7 分钟缩短到 6 秒。

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

于 2013-08-25T22:46:18.447 回答
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,直到批量插入成为核心的一部分。

更新

只是一个快速的想法。您是否尝试过 Migrations 命名空间中的 ... 方法?也许这个做批量插入,还没有研究过,但值得一试:

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

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

于 2013-06-12T15:13:25.213 回答
3

您可以使用批量插入扩展

用法:

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();
    }
}
于 2014-07-07T23:04:17.343 回答
0

恐怕 EF 不支持批量插入或更新。正如您所说,目前 EF 将生成一堆插入命令并分别执行它们(但全部包装在一个事务中)。有一些计划实施批处理,不确定最近是否有一些进展。希望在 EF6 中,但我有点怀疑。

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

于 2013-06-12T14:37:35.260 回答
-1

从存储库插入 ASP .NET Core 版本的快速方法。

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();
}
于 2017-04-19T14:44:08.793 回答