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 = context.Blogs.Single(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" });
context.SaveChanges();

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 enviarlas una a una, 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. Del mismo modo, las ventajas del procesamiento por lotes se reducen después de aproximadamente 40 instrucciones en SQL Server, por lo que EF Core solo ejecutará de forma predeterminada 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 antes de modificarlos hay que realizar pruebas comparativas:

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 subir el sueldo a todos sus empleados. A continuación se muestra una implementación típica en EF Core:

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

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 a la 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 ExecuteUpdate y ExecuteDelete para hacer lo mismo de forma mucho más eficaz:

context.Employees.ExecuteUpdate(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;

Este 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, 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 sobre cómo guardar datos.