批量更新/删除EF5

使用(entity framework)EF5处理批量更新的最佳方法是什么? 我有两个我感兴趣的特殊情况:

  1. 更新一个字段(例如UpdateDate)的列表(列表)之间的100到100.000 Id的主键。 分别调用每个更新似乎要花费很多开销,并且需要很长时间。

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

有什么好build议?

  1. 有两个开源项目允许这样做: EntityFramework.Extended和Entity Framework Extensions 。 您也可以在EF的codeplex网站上查看关于批量更新的讨论 。
  2. 通过EF插入100Klogging是第一个错误的应用程序体系结构。 您应该select不同的轻量级技术进行数据导入。 即使英孚这样大的logging集的内部操作也会花费你大量的处理时间。 目前还没有用于EF的批量插入的解决scheme,但在EF的代码plex站点上有关于此function的广泛讨论 。

我看到以下选项:

1。 最简单的方法 – 手动创buildSQL请求,并通过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制作自己的扩展。 有一篇文章批量删除这个目标是通过inheritanceObjectContext类来实现的。 值得一看。 批量插入/更新可以用相同的方法实现。

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

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

批量插入应该使用SqlBulkCopy类来完成。 请参阅已有的StackOverflow问题和解决scheme,以整合两者: SqlBulkCopy和Entity Framework

SqlBulkCopy比bcp(Bulk Copy命令行实用程序)甚至OPEN ROWSET更加用户友好。

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

以下是我所做的成功:

 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 = $@"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 = $@"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); } 
  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; }