2011-05-12 18 views
5

Tengo> 67000 registros llegando a mi sistema desde otra fuente. Después de aplicar las reglas comerciales a esos registros, debo almacenarlos en la base de datos. Yo uso siguiente código para hacerlo:Entity Framework (EF) OutOfMemoryException

 using (var context = new MyEntities()) 
     { 
      var importDataInfo = context.ImportDataInfoes.First(x => x.ID == importId); 

      importedRecords.ForEach(importDataInfo.ValuationEventFulls.Add); 

      context.SaveChanges(); 
     } 

Después de ejecutar el código que estoy recibiendo error de seguimiento (OutOfMemoryException)

Error in executing code|Exception of type 'System.OutOfMemoryException' was thrown.* at System.Data.Mapping.Update.Internal.KeyManager.<WalkGraph>d__5.MoveNext() 
    at System.Data.Mapping.Update.Internal.KeyManager.GetPrincipalValue(PropagatorResult result) 
    at System.Data.Mapping.Update.Internal.UpdateCompiler.GenerateValueExpression(EdmProperty property, PropagatorResult value) 
    at System.Data.Mapping.Update.Internal.UpdateCompiler.BuildSetClauses(DbExpressionBinding target, PropagatorResult row, PropagatorResult originalRow, TableChangeProcessor processor, Boolean insertMode, Dictionary`2& outputIdentifiers, DbExpression& returning, Boolean& rowMustBeTouched) 
    at System.Data.Mapping.Update.Internal.UpdateCompiler.BuildInsertCommand(PropagatorResult newRow, TableChangeProcessor processor) 
    at System.Data.Mapping.Update.Internal.TableChangeProcessor.CompileCommands(ChangeNode changeNode, UpdateCompiler compiler) 
    at System.Data.Mapping.Update.Internal.UpdateTranslator.<ProduceDynamicCommands>d__0.MoveNext() 
    at System.Linq.Enumerable.<ConcatIterator>d__71`1.MoveNext() 
    at System.Data.Mapping.Update.Internal.UpdateCommandOrderer..ctor(IEnumerable`1 commands, UpdateTranslator translator) 
    at System.Data.Mapping.Update.Internal.UpdateTranslator.ProduceCommands() 
    at System.Data.Mapping.Update.Internal.UpdateTranslator.Update(IEntityStateManager stateManager, IEntityAdapter adapter) 
    at System.Data.EntityClient.EntityAdapter.Update(IEntityStateManager entityCache) 
    at System.Data.Objects.ObjectContext.SaveChanges(SaveOptions options) 

estoy usando EF 4.0.

Mi pregunta es ¿existe una limitación en el número de registros para guardar? ¿Cuál es la mejor práctica para guardar una gran cantidad de registros (guárdelos en fragmentos? ¿Qué pasa con las transacciones?).

Gracias a todos de antemano.

Respuesta

4

Es probable que desee enviar esos datos en lotes de probablemente 1024 registros a la vez.

Puede envolver el bucle que agrupa los registros en una transacción para que pueda deshacer la secuencia completa si lo desea. Tenga en cuenta que esta transacción probablemente escalará a una transacción distribuida.

Las transacciones distribuidas solo se pueden aplicar contra un servidor que tenga el servicio Microsoft Coordinated Transaction Coordinator (MS-DTC) en ejecución. Existe una penalización de rendimiento notable cuando se trabajan transacciones distribuidas.

+0

Gracias por su respuesta. - Sí, cambié mi código para probar si funciona con fragmentos de registros y, de hecho, funciona, pero mi pregunta es: no he leído nada sobre la limitación del número de registros en SaveChanges() en EF. ¿Has tenido noticias de MS sobre esta restricción? –

+0

@Vlad Bezden, la restricción no está conectada específicamente con EF, es solo parte de trabajar con la tecnología .NET. Cuando registra ETL 67K, es probable que tenga 200k + objetos en la memoria. Tienes los 67k registros originales, tienes los 67k objetos EF, y cada uno crea una cantidad de entidades de seguimiento de estado dentro del contexto para cada objeto, así que ahí ya están 67k + 67k + 67k, no me sorprendería que un par de otros objetos auxiliares se crean a lo largo del camino también. EF trabaja en un patrón de consulta diferida, no hace un trabajo real hasta que no llame a SaveChanges, es por eso que ocurre el OOM. –

+0

Tengo un problema similar. No está claro cómo se debe cambiar el fragmento. Sería bueno ver como una actualización de la pregunta original. Tengo el código existente donde hay .Add dentro de foreach loop y SaveChanges inmediatamente después del ciclo. No está claro si incluye SaveChanges en un bucle, ¿siempre se borra lo que se agregó a través de.Agregue – MicMit

4

En general, .NET se limita a direccionar 2 GB de memoria en una sola colección u otro objeto. Esto se debe a que incluso en un entorno de 64 bits, los indexadores usan enteros de 32 bits (con un valor máximo de 2 mil millones y un cambio). Incluso para tipos de datos simples como enteros, el tamaño de una sola int significa que solo se pueden almacenar 500 millones de ints en una sola matriz. Para tipos de valor más grandes, como estructuras, la cantidad máxima de elementos de una colección es muy pequeña.

Si se encuentra en un entorno de 32 bits como Windows XP, existen incluso límites más bajos; el espacio de memoria máximo para el PROGRAMA COMPLETO no puede ser mayor a 2 GB. Esto coloca algunas restricciones bastante altas en un ETL como el suyo, y no me sorprendería que su programa se esté quedando sin memoria tratando de procesar 67k registros en memoria a la vez.

La solución es manejar los registros en lotes más pequeños si es posible. Intente construir una declaración basada en la ID, donde puede devolver los 100 registros principales donde la ID (con suerte autogenerada) es mayor que la ID más grande que ya recuperó. Una vez que se procesa un registro, deséchelo (o simplemente hágalo huérfano y deje que el GC haga su trabajo).

+0

Gracias por responder. Estoy recibiendo OOME cuando estoy llamando al método SaveChanges. No tengo ningún problema con el código .NET con mi aplicación, probé el procesamiento de registros de importación con> 1,000,000 y el sistema está bien, solo falla cuando estoy llamando a SaveChanges en EF –

+0

Probablemente porque los ORM como EF deben determinar qué cambió para que puedan hacer una declaración de ACTUALIZACIÓN eficiente. Eso requiere tirar nuevamente del registro y almacenar los cambios en alguna colección que usará para generar la actualización. Además, las cadenas que forman los comandos de actualización son colecciones de caracteres y multiplican 67,000 por hasta un par de miles de caracteres por ACTUALIZACIÓN y puede alcanzar fácilmente los límites de longitud de una cadena. Una vez más, intente establecer un "tamaño de lote" para los registros entrantes y salientes, lo que limitará la cantidad de datos entrantes Y el tamaño de los comandos que salen. – KeithS

+0

Lo siento por la entrada tardía: ¿Es la llamada a SaveChanges() la que causa el OOME, por lo que si llamo a SaveChanges con más frecuencia, entonces puedo minimizar el riesgo de esto? Tengo un bucle foreach, pero estaba procesando TODOS los elementos antes de guardar, mientras que no hay nada que me impida guardar cada elemento individualmente. – BlueChippy