改进实体框架插入

时间:2015-09-22 12:00:51

标签: c# sql-server entity-framework-6

我已经处理了2天而且找不到解决方案。

using (TaxablePersonContext context = new TaxablePersonContext(this.ConnectionString))
{
    context.Configuration.AutoDetectChangesEnabled = false;
    foreach(TaxablePerson p in persons) // Persons has always size 1000
    {
      // TaxablePerson has some other properties e.g. Name, VatId, Street,...
      p.RecCreatedBy = "application name";
      p.RecCreatedOn = this.SynchronizationStartDateTime;
      p.RecModifiedBy = "application name";
      p.RecModifiedOn = this.SynchronizationStartDateTime;
      p.RecSyncDate = this.SynchronizationStartDateTime;
      p.RecActive = true;
    }
    DateTime start1 = DateTime.Now;
    context.TaxablePersons.AddRange(persons);
    TimeSpan end1 = DateTime.Now.Subtract(start1);

    DateTime start2 = DateTime.Now;
    context.SaveChanges();
    TimeSpan end2 = DateTime.Now.Subtract(start1);
}

我需要将近10秒的时间来插入1000条记录,并且需要98秒才能在sql server中插入10.000条记录。您能否建议如何改进实体框架插入性能。我阅读了这篇文章Fastest Way of Inserting in Entity Framework并包含了这篇文章中提到的提示,但仍然插入很慢。我需要插入260.000条记录需要52分钟。我正在以1000的批量插入,上面的代码演示了。从文件中读取数据,当我点击1000条记录时,我会与数据库进行同步。我还可以做些什么?有人提到使用时设置context.Configuration.AutoDetectChangesEnabled = false;性能从几分钟提高到几乎几秒钟。我错过了什么?我正在使用实体框架6.1.3。

1 个答案:

答案 0 :(得分:0)

使用sql profiler,我发现Entity框架逐个发送查询,例如

INSERT INTO MyTable (id, name) VALUES (1, 'Bob')
INSERT INTO MyTable (id, name) VALUES (2, 'Peter')
INSERT INTO MyTable (id, name) VALUES (3, 'Joe')

所以实际上sql server在这种情况下执行1000次插入非常慢 - 差不多10秒(虽然在事务中执行)。然后我构造了具有多个值的插入 - 具有多个值的SQL并且插入1000个记录花费了5秒(50%更好 - 更早10秒)。 Sql server有一个你可以传递的sql参数限制,即2100,所以这是你用这种方法做的最好的。

private void MultiRecordsInsert(TaxablePersonContext context, List<TaxablePerson> personsToAdd)
{
  List<SqlParameter> parameters = new List<SqlParameter>();
  string firstQuery = @"insert into TaxablePerson (c1, c2, c3) values ";
  string query = firstQuery;

  for (int i = 0; i < personsToAdd.Count; i++)
  {
    query += "(@c1" + i.ToString();
    query += ",@c2" + i.ToString();
    query += ",@c3" + i.ToString() + "),";
    parameters.Add(new SqlParameter("@c1" + i.ToString(), personsToAdd[i].c1));
    parameters.Add(new SqlParameter("@c2" + i.ToString(), personsToAdd[i].c2));
    parameters.Add(new SqlParameter("@c3" + i.ToString(), personsToAdd[i].c3));

    // table has 16 columns (I reduced here for simplicity) so: 2100 / 16 = 131, 
    // used 100
    //
    if (i % 100 == 0)
    {
      query = query.Substring(0, query.Length - 1); // remove last comma
      context.Database.ExecuteSqlCommand(query, parameters.ToArray());

      query = firstQuery;
      parameters = new List<SqlParameter>();
    }
  }

  if (parameters.Count > 0) // what is left
  {
    query = query.Substring(0, query.Length - 1);
    context.Database.ExecuteSqlCommand(query, parameters.ToArray());
  }
}