Actualización/eliminación por lotes EF5


¿Cuál es la mejor manera de lidiar con las actualizaciones por lotes utilizando (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 la clave principal. Llamar a cada actualización por separado parece ser una sobrecarga y lleva mucho tiempo.

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

Cualquier bien ¿consejo?

Author: Frank, 2012-10-05

7 answers

  1. Hay dos proyectos de código abierto que permiten esto: EntityFramework.Extensiones extendidas y de Entity Framework. También puede consultar discusión sobre actualizaciones masivas en el sitio codeplex de EF.
  2. Insertar registros 100k a través de EF es, en primer lugar, una arquitectura de aplicación incorrecta. Debe elegir diferentes tecnologías ligeras para las importaciones de datos. Incluso el funcionamiento interno de EF con un conjunto de registros tan grande le costará mucho tiempo de procesamiento. Hay actualmente no hay solución para inserciones por lotes para EF, pero existe una amplia discusión sobre esta característica en el sitio code plex de EF.
 35
Author: Ladislav Mrnka,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-05 17:45:00

Veo las siguientes opciones:

1 . La forma más sencilla - crear su solicitud SQL por manos y ejecutar a través de ObjectContext.ExecuteStoreCommand

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

2 . Utilice EntityFramework.Extendido

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 este objetivo se logró heredando la clase ObjectContext. Vale la pena echar un vistazo. Bulk insert/update se puede implementar de la misma manera.

 21
Author: Alex Klaus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-23 05:57:45

Es posible que no desee 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 proc almacenado asignado a una función EF). También puede usar el Contexto.Método ExecuteStoreQuery para emitir una instrucción Update a la base de datos.

Para inserciones masivas, su mejor opción es usar Copia masiva o SSIS. EF requerirá una visita separada a la base de datos para cada fila que es insertar.

 3
Author: Jim Wooley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-05 17:44:09

Las inserciones masivas deben hacerse usando 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 (Bulk Copy command-line utility) o incluso OPEN ROWSET.

 2
Author: John Zabroski,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:02:14

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

 0
Author: Lukas Winzenried,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-02-09 22:50:13

Esto es lo que he hecho 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<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
Author: adam0101,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-27 15:24:54
    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;
    }
 0
Author: Jack CQ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-06 03:46:03