Compartir a través de


Actualización eficaz

Procesamiento por lotes

EF Core ayuda a minimizar los recorridos de ida y vuelta mediante el procesamiento por lotes automático de todas las actualizaciones en un solo recorrido de ida y vuelta. Tenga en cuenta lo siguiente:

var blog = await context.Blogs.SingleAsync(b => b.Url == "http://someblog.microsoft.com");
blog.Url = "http://someotherblog.microsoft.com";
context.Add(new Blog { Url = "http://newblog1.microsoft.com" });
context.Add(new Blog { Url = "http://newblog2.microsoft.com" });
await context.SaveChangesAsync();

Lo anterior carga un blog de la base de datos, cambia su dirección URL y, a continuación, agrega dos blogs nuevos; para aplicar esto, se envían dos instrucciones SQL INSERT y una instrucción UPDATE a la base de datos. En lugar de enviarlos uno por uno, a medida que se agregan instancias de blog, EF Core realiza un seguimiento de estos cambios internamente y los ejecuta en un solo recorrido de ida y vuelta cuando se llama a SaveChanges.

El número de instrucciones que EF procesa por lotes en un solo recorrido de ida y vuelta depende del proveedor de base de datos que se use. Por ejemplo, el análisis de rendimiento ha mostrado que el procesamiento por lotes suele ser menos eficaz para SQL Server cuando intervienen menos de 4 instrucciones. De forma similar, las ventajas del procesamiento por lotes se degradan después de aproximadamente 40 instrucciones para SQL Server, por lo que EF Core solo ejecutará hasta 42 instrucciones en un solo lote y ejecutará instrucciones adicionales en recorridos de ida y vuelta independientes.

Los usuarios también pueden ajustar estos umbrales para lograr un rendimiento potencialmente mayor, pero realizar pruebas comparativas cuidadosamente antes de modificarlos:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder.UseSqlServer(
        @"Server=(localdb)\mssqllocaldb;Database=Blogging;Trusted_Connection=True",
        o => o
            .MinBatchSize(1)
            .MaxBatchSize(100));
}

Usar ExecuteUpdate y ExecuteDelete cuando sea pertinente

Supongamos que quiere dar a todos los empleados un aumento. Una implementación típica para esto en EF Core tendría el siguiente aspecto:

foreach (var employee in context.Employees)
{
    employee.Salary += 1000;
}
await context.SaveChangesAsync();

Aunque este código es perfectamente válido, vamos a analizar lo que hace desde una perspectiva de rendimiento:

  • Se realiza un recorrido de ida y vuelta a la base de datos para cargar todos los empleados pertinentes; tenga en cuenta que esto traerá todos los datos de la fila Empleados al cliente, incluso si solo se necesitan datos de salario.
  • El seguimiento de cambios de EF Core crea instantáneas al cargar las entidades y, a continuación, compara esas instantáneas con las instancias para averiguar qué propiedades han cambiado.
  • Normalmente, se realiza un segundo recorrido de ida y vuelta de base de datos para guardar todos los cambios (tenga en cuenta que algunos proveedores de bases de datos dividen los cambios en varios recorridos de ida y vuelta). Aunque este comportamiento de procesamiento por lotes es mucho mejor que realizar un recorrido de ida y vuelta para cada actualización, EF Core sigue enviando una instrucción UPDATE por empleado y la base de datos debe ejecutar cada instrucción por separado.

A partir de EF Core 7.0, puede usar los métodos ExecuteUpdateAsync y ExecuteDeleteAsync para hacer lo mismo de forma mucho más eficaz:

await context.Employees.ExecuteUpdateAsync(s => s.SetProperty(e => e.Salary, e => e.Salary + 1000));

Esto envía la siguiente instrucción SQL a la base de datos:

UPDATE [Employees] SET [Salary] = [Salary] + 1000;

Esta UPDATE realiza toda la operación en un solo recorrido de ida y vuelta, sin cargar ni enviar datos reales a la base de datos, y sin hacer uso de la maquinaria de seguimiento de cambios de EF, lo que impone una sobrecarga adicional. Para obtener más información, vea ExecuteUpdate y ExecuteDelete.

Si usa una versión anterior de EF Core que aún no admite ExecuteUpdate y ExecuteDelete, o quiere ejecutar una instrucción SQL compleja que no es compatible con estos métodos, puede seguir usando una consulta SQL para realizar la operación:

context.Database.ExecuteSql($"UPDATE [Employees] SET [Salary] = [Salary] + 1000");

Para obtener más información sobre las diferencias entre SaveChanges y ExecuteUpdate/ExecuteDelete, consulte la página información general de sobre cómo guardar datos.