2012-10-05 149 views
40

使用(实体框架)EF5处理批量更新的最佳方式是什么? 我有2特定情况下我感兴趣的:批量更新/删除EF5

  1. 更新的字段(例如UpdateDate)为100和100.000之间的Id,该主键列表(列表)。单独调用每个更新似乎需要很多开销并需要很长时间。

  2. 在同一个对象(例如用户)中插入很多,也在100和100.000之间。

有什么好建议吗?

回答

35
  1. 有两个开源项目允许这样做:EntityFramework.ExtendedEntity Framework Extensions。您也可以在EF的codeplex网站上查询discussion有关批量更新的信息。
  2. 通过EF插入100K记录是第一个错误的应用程序体系结构。您应该为数据导入选择不同的轻量级技术。即使EF内部操作如此大的记录集也会花费您大量的处理时间。目前还没有针对EF的批量插入解决方案,但在EF的代码plex站点上有关于此功能的broad discussion
+1

你知道的EntityFramework.Extended支持EF v4的? 似乎实体框架扩展是死了的项目(最后一个版本是在2010年) –

+1

有一个扩展的ef大容量插入codeplex http://stackoverflow.com/a/27983392/937411 –

+0

配料仍然重要吗? – Jordan

3

您可能不想听到它,但最好的选择是不使用EF进行批量操作。为了跨记录表更新字段,请在数据库中使用Update语句(可能通过映射到EF函数的存储过程调用)。您还可以使用Context.ExecuteStoreQuery方法向数据库发出Update语句。

对于大量插入,最好的选择是使用批量复制或SSIS。对于插入的每一行,EF都需要单独命中数据库。

21

我看到如下选项:

1。最简单的方法 - 通过手动创建SQL请求并执行ObjectContext.ExecuteStoreCommand

context.ExecuteStoreCommand("UPDATE TABLE SET FIELD1 = {0} WHERE FIELD2 = {1}", value1, value2); 

2。使用EntityFramework.Extended

context.Tasks.Update(
    t => t.StatusId == 1, 
    t => new Task {StatusId = 2}); 

3。为EF制作自己的扩展。有一篇文章Bulk Delete这个目标是通过继承ObjectContext类实现的。值得一看。批量插入/更新可以用相同的方式实现。

+1

在EF6中的'bContext.Database.ExecuteSqlCommand()'? – Jess

+0

使用此代替ctx.Database.ExecuteSqlCommand – Ovis

2

批量插入应该使用SqlBulkCopy类完成。请参阅预先存在的StackOverflow Q & A集成两个:SqlBulkCopy and Entity Framework

SqlBulkCopy比bcp(批量复制命令行实用程序)甚至OPEN ROWSET更加用户友好。

+0

没有像SqlBulkCopy批量更新那样的事情,只BulkInsert – Pleun

+0

我可能回答问题的后半部分,并意外键入“批量更新”,而不是“批量插入”。固定。 –

0

我同意接受的答案,即ef可能是大容量插入的错误技术。 但是,我认为值得看看EntityFramework.BulkInsert

0

这里是我已经成功地完成:

private void BulkUpdate() 
{ 
    var oc = ((IObjectContextAdapter)_dbContext).ObjectContext; 
    var updateQuery = myIQueryable.ToString(); // This MUST be above the call to get the parameters. 
    var updateParams = GetSqlParametersForIQueryable(updateQuery).ToArray(); 
    var updateSql = [email protected]"UPDATE dbo.myTable 
         SET col1 = x.alias2 
         FROM dbo.myTable 
         JOIN ({updateQuery}) x(alias1, alias2) ON x.alias1 = dbo.myTable.Id"; 
    oc.ExecuteStoreCommand(updateSql, updateParams); 
} 

private void BulkInsert() 
{ 
    var oc = ((IObjectContextAdapter)_dbContext).ObjectContext; 
    var insertQuery = myIQueryable.ToString(); // This MUST be above the call to get the parameters. 
    var insertParams = GetSqlParametersForIQueryable(insertQuery).ToArray(); 
    var insertSql = [email protected]"INSERT INTO dbo.myTable (col1, col2) 
         SELECT x.alias1, x.alias2 
         FROM ({insertQuery}) x(alias1, alias2)"; 
    oc.ExecuteStoreCommand(insertSql, insertParams.ToArray()); 
}  

private static IEnumerable<SqlParameter> GetSqlParametersForIQueryable<T>(IQueryable<T> queryable) 
{ 
    var objectQuery = GetObjectQueryFromIQueryable(queryable); 
    return objectQuery.Parameters.Select(x => new SqlParameter(x.Name, x.Value)); 
} 

private static ObjectQuery<T> GetObjectQueryFromIQueryable<T>(IQueryable<T> queryable) 
{ 
    var dbQuery = (DbQuery<T>)queryable; 
    var iqProp = dbQuery.GetType().GetProperty("InternalQuery", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); 
    var iq = iqProp.GetValue(dbQuery, null); 
    var oqProp = iq.GetType().GetProperty("ObjectQuery", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); 
    return (ObjectQuery<T>)oqProp.GetValue(iq, null); 
} 
0
public static bool BulkDelete(string tableName, string columnName, List<object> val) 
    { 
     bool ret = true; 

     var max = 2000; 
     var pages = Math.Ceiling((double)val.Count/max); 
     for (int i = 0; i < pages; i++) 
     { 
      var count = max; 
      if (i == pages - 1) { count = val.Count % max; } 

      var args = val.GetRange(i * max, count); 
      var cond = string.Join("", args.Select((t, index) => $",@p{index}")).Substring(1); 
      var sql = $"DELETE FROM {tableName} WHERE {columnName} IN ({cond}) "; 

      ret &= Db.ExecuteSqlCommand(sql, args.ToArray()) > 0; 
     } 

     return ret; 
    }