Actualización / eliminación de lote EF5

¿Cuál es la mejor manera de lidiar con las actualizaciones por lotes usando (Entity Framework) EF5? Tengo 2 casos particulares que me interesan:

  1. Actualización de un campo (por ejemplo, UpdateDate) para una lista (Lista) de entre 100 y 100.000 Id, que es la clave principal. Llamar a cada actualización por separado parece ser muy costoso y lleva mucho tiempo.

  2. Insertar muchos, también entre 100 y 100.000, de los mismos objetos (por ejemplo, usuarios) de una sola vez.

¿Algún buen consejo?

  1. Hay dos proyectos de código abierto que permiten esto: EntityFramework.Extended y Entity Framework Extensions . También puede consultar la discusión sobre las actualizaciones masivas en el sitio codeplex de EF.
  2. Insertar 100k registros a través de EF es, en primer lugar, una architecture de aplicación incorrecta. Debe elegir diferentes tecnologías livianas para la importación de datos. Incluso el funcionamiento interno de EF con un gran conjunto de registros le costará mucho tiempo de procesamiento. Actualmente no existe una solución para las inserciones por lotes para EF, pero existe una amplia discusión sobre esta característica en el sitio de códigos de EF.

Veo las siguientes opciones:

1. La forma más simple: crea tu solicitud de SQL a mano y ejecuta a través de ObjectContext.ExecuteStoreCommand

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

2. Usar EntityFramework.Extended

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

3. Crea tu propia extensión para EF. Hay un artículo Bulk Delete donde se logró este objective al heredar la clase ObjectContext . Vale la pena echarle un vistazo. La inserción / actualización masiva se puede implementar de la misma manera.

Puede que no quiera escucharlo, pero su mejor opción es no usar EF para operaciones masivas. Para actualizar un campo en una tabla de registros, use una instrucción Update en la base de datos (posiblemente llamada a través de un proceso almacenado asignado a una Función EF). También puede usar el método Context.ExecuteStoreQuery para emitir una instrucción Update en la base de datos.

Para inserciones masivas, la mejor opción es usar Bulk Copy o SSIS. EF requerirá un golpe separado a la base de datos para cada fila que se inserte.

Las inserciones masivas deben hacerse utilizando la clase SqlBulkCopy. Consulte las Preguntas y Respuestas de StackOverflow preexistentes sobre la integración de los dos: SqlBulkCopy y Entity Framework

SqlBulkCopy es mucho más fácil de usar que bcp (utilidad de línea de comandos de Bulk Copy) o incluso OPEN ROWSET.

Estoy de acuerdo con la respuesta aceptada de que ef es probablemente la tecnología incorrecta para inserciones masivas. Sin embargo, creo que vale la pena echarle un vistazo a EntityFramework.BulkInsert .

Esto es lo que hice con éxito:

 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 GetSqlParametersForIQueryable(IQueryable queryable) { var objectQuery = GetObjectQueryFromIQueryable(queryable); return objectQuery.Parameters.Select(x => new SqlParameter(x.Name, x.Value)); } private static ObjectQuery GetObjectQueryFromIQueryable(IQueryable queryable) { var dbQuery = (DbQuery)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)oqProp.GetValue(iq, null); } 
  public static bool BulkDelete(string tableName, string columnName, List 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; }