2012-06-06 9 views
5

Usando la base de datos de Entity Framework 4.3.1 Primero, ¿cuál es una buena manera de confirmar/guardar cambios de objetos en la base de datos con frecuencia? En lo que sigue, me gustaría guardar la factura inmediatamente después de la llamada a los libros rápidos, y no arriesgarme a esperar a que se publiquen todas las facturas. Pero, no puedo llamar a SaveChanges cada vez en el ciclo, lanzará una excepción.Guarda con frecuencia con el marco de entidad

Sería útil tener un método .Save() en cada objeto, ¿tal vez hay una buena manera de hacerlo?

var unpostedInvoices = entities.GetUnpostedInvoices(); 
foreach (Invoice invoice in unpostedInvoices) 
{ 
    // this takes a long time 
    var invoiceDto = quickbooks.PostInvoice(invoice); 

    invoice.Posted = true; 
    invoice.TransactionId = invoiceDto.TransactionId; 

    // I'd like to save here rather than after the foreach loop, but this will fail 
    //entities.SaveChanges(); 
} 

// this works, but I don't want to risk waiting this long to save 
entities.SaveChanges(); 

Esta es la excepción lanzada al llamar a SaveChanges() en el ciclo.

New transaction is not allowed because there are other threads running in the session. 

at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction) 
at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction) 
at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose) 
at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady) 
at System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj) 
at System.Data.SqlClient.TdsParser.TdsExecuteTransactionManagerRequest(Byte[] buffer, TransactionManagerRequestType request, String transactionName, TransactionManagerIsolationLevel isoLevel, Int32 timeout, SqlInternalTransaction transaction, TdsParserStateObject stateObj, Boolean isDelegateControlRequest) 
at System.Data.SqlClient.SqlInternalConnectionTds.ExecuteTransactionYukon(TransactionRequest transactionRequest, String transactionName, IsolationLevel iso, SqlInternalTransaction internalTransaction, Boolean isDelegateControlRequest) 
at System.Data.SqlClient.SqlInternalConnectionTds.ExecuteTransaction(TransactionRequest transactionRequest, String name, IsolationLevel iso, SqlInternalTransaction internalTransaction, Boolean isDelegateControlRequest) 
at System.Data.SqlClient.SqlInternalConnection.BeginSqlTransaction(IsolationLevel iso, String transactionName) 
at System.Data.SqlClient.SqlInternalConnection.BeginTransaction(IsolationLevel iso) 
at System.Data.SqlClient.SqlConnection.BeginDbTransaction(IsolationLevel isolationLevel) 
at System.Data.Common.DbConnection.BeginTransaction(IsolationLevel isolationLevel) 
at System.Data.EntityClient.EntityConnection.BeginDbTransaction(IsolationLevel isolationLevel) 
+1

¿Cómo falla al llamar 'SaveChanges' dentro del ciclo? –

+0

Agregué la excepción a la pregunta. – RyanW

Respuesta

6

Esta pregunta podría ayudar: https://stackoverflow.com/questions/2113498

no puede iniciar una nueva transacción mientras se está leyendo resultado set y SaveChanges crea una transacción si aún no existe.

Una solución es completar primero la lectura, luego iterar sobre el conjunto de resultados en la memoria.

Si cambia esta línea:

var unpostedInvoices = entities.GetUnpostedInvoices().ToList(); 

... se puede poner SaveChanges de nuevo en el circuito?

+0

Sí, eso hace el trabajo. ¡Gracias! – RyanW

2

EF hará un seguimiento de todos los cambios y actualizará la base de datos cuando se llama SaveChanges(). No llamaría luego al SaveChanges() dentro de un bucle (aunque no debería fallar).

Tenga en cuenta que EF realiza una ida y vuelta de base de datos separada para cada registro que desee insertar, actualizar o eliminar, por lo que generalmente no importa con qué frecuencia llama a SaveChanges. Evitar esto es principalmente posible solo cuando se usa SQL directo y se crea un solo SqlCommand ejecutando todas las inserciones a la vez.

De todos modos este error se debe a Entity Framework crear una transacción implícita durante el SaveChanges() llamada

using (var transaction = new TransactionScope()) 
{ 
    using (var context = new MyContext()) 
    { 
     foreach (Invoice invoice in unpostedInvoices) 
     { 
      // Change to invoice 
      context.SaveChanges(); 
     } 
    } 
    transaction.Complete(); 
} 
+0

Sí, deseo emitir la actualización (ida y vuelta a la base de datos) cada vez a través del ciclo en lugar de hacerlo al final. Pero no funciona llamar a SaveChanges() cada vez. Como uso sprocs para inserciones y actualizaciones, veré solo hacer una importación de funciones en la actualización y llamarla directamente en el contexto. – RyanW

+0

He editado mi respuesta –

+0

Gracias, eso tiene sentido. En este caso, quiero que cada actualización viva sola y no esté sujeta a reversión si falla una factura más adelante en el bucle. – RyanW

Cuestiones relacionadas