Entity Framework actualiza / inserta múltiples entidades

Solo un resumen de lo que estoy tratando de lograr. Mantenemos una copia local de una base de datos remota (tercero) dentro de nuestra aplicación. Para descargar la información utilizamos una api. Actualmente, descargamos la información en un horario que luego inserta nuevos registros en la base de datos local o actualiza los registros existentes. Así es como funciona actualmente.

public void ProcessApiData(List apiData) { // get the existing accounts from the local database List existingAccounts = _accountRepository.GetAllList(); foreach(account in apiData) { // check if it already exists in the local database var existingAccount = existingAccounts.SingleOrDefault(a => a.AccountId == account.AccountId); // if its null then its a new record if(existingAccount == null) { _accountRepository.Insert(account); continue; } // else its a new record so it needs updating existingAccount.AccountName = account.AccountName; // ... continue updating the rest of the properties } CurrentUnitOfWork.SaveChanges(); } 

Esto funciona bien, sin embargo, parece que podría mejorarse.

  1. Existe uno de estos métodos por Entidad, y todos hacen lo mismo (solo actualizando diferentes propiedades) o insertando una Entidad diferente. ¿Habría alguna forma de hacer esto más genérico?
  2. Simplemente parece una gran cantidad de llamadas a bases de datos, de todas formas habría que hacer “Bulk”. He echado un vistazo a este paquete que he visto mencionado en algunas otras publicaciones https://github.com/loresoft/EntityFramework.Extended. Pero parece centrarse en la actualización masiva de una sola propiedad con el mismo valor, o así. puedo decir.

Cualquier sugerencia sobre cómo puedo mejorar esto sería genial. Todavía soy bastante nuevo en c #, así que todavía estoy buscando la mejor manera de hacer las cosas.

Estoy usando .net 4.5.2 y Entity Framework 6.1.3 con MSSQL 2014 como la base de datos backend

  1. Suponiendo que las clases en apiData son las mismas que sus entidades, debería poder usar Attach(newAccount, originalAccount) para actualizar una entidad existente.
  2. Para inserciones AddRange(listOfNewEntitities) utilizo AddRange(listOfNewEntitities) . Si tienes muchas entidades para insertarlo es recomendable que las agrupes. También es posible que desee disponer y recrear el DbContext en cada lote para que no use demasiada memoria.

     var accounts = new List(); var context = new YourDbContext(); context.Configuration.AutoDetectChangesEnabled = false; foreach (var account in apiData) { accounts.Add(account); if (accounts.Count % 1000 == 0) // Play with this number to see what works best { context.Set().AddRange(accounts); accounts = new List(); context.ChangeTracker.DetectChanges(); context.SaveChanges(); context?.Dispose(); context = new YourDbContext(); } } context.Set().AddRange(accounts); context.ChangeTracker.DetectChanges(); context.SaveChanges(); context?.Dispose(); 

Para actualizaciones masivas, no hay nada integrado en LINQ to SQL. Sin embargo, hay bibliotecas y soluciones para hacer frente a esto. Ver, por ejemplo, aquí para una solución utilizando árboles de expresión.

Para EFCore puedes usar esta biblioteca:
https://github.com/borisdj/EFCore.BulkExtensions

Y para EF 6 este:
https://github.com/tiagoln/BulkExtensions

Ambos están extendiendo DbContext con operaciones DbContext y tienen la misma llamada de syntax:

 context.BulkInsert(entitiesList); context.BulkUpdate(entitiesList); context.BulkDelete(entitiesList); 

La versión EFCore tiene además el método BulkInsertOrUpdate .

Lista vs. Diccionario

Se verifica en una lista cada vez que exista la entidad que es mala. Debes crear un diccionario para mejorar el rendimiento.

 var existingAccounts = _accountRepository.GetAllList().ToDictionary(x => x.AccountID); Account existingAccount; if(existingAccounts.TryGetValue(account.AccountId, out existingAccount)) { // ...code.... } 

Agregar vs. AgregarRango

Debe tener en cuenta el rendimiento de Agregar frente a Agregar Rango cuando agregue varios registros.

  • Agregar: Llame a DetectChanges después de agregar cada registro
  • AddRange: Llame a DetectChanges después de agregar todos los registros

introduzca la descripción de la imagen aquí

Así que en 10,000 entidades, el método Add ha tomado 875 veces más tiempo para agregar entidades en el contexto simplemente.

Arreglarlo:

  1. Crear una lista
  2. AÑADIR entidad a la lista
  3. USE AddRange con la lista
  4. Guardar cambios
  5. ¡Hecho!

En su caso, deberá crear un método InsertRange en su repository.

EF extendido

Tienes razón. Esta biblioteca actualiza todos los datos con el mismo valor. Eso no es lo que buscas.

Descargo de responsabilidad : soy el propietario del proyecto Entity Framework Extensions

Esta biblioteca puede adaptarse perfectamente a su empresa si desea mejorar su rendimiento de forma espectacular.

Puede realizar fácilmente:

  • BulkSaveChanges
  • BulkInsert
  • Actualización masiva
  • BulkDelete
  • BulkMerge

Ejemplo:

 public void ProcessApiData(List apiData) { // Insert or Update using the primary key (AccountID) CurrentUnitOfWork.BulkMerge(apiData); }