2012-06-06 13 views
5

Utilizzo di Entity Framework 4.3.1 Database In primo luogo, quale è un buon metodo per salvare/salvare frequentemente le modifiche agli oggetti nel database? Nel seguito, vorrei salvare la fattura immediatamente dopo la chiamata ai quickbook e non rischiare di attendere che tutte le fatture vengano pubblicate. Ma, non posso chiamare SaveChanges ogni volta nel ciclo, genererà un'eccezione.Salvataggi frequenti con il framework entità

Sarebbe utile avere un metodo .Save() su ciascun oggetto, forse c'è un buon modo per farlo?

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(); 

Questa è l'eccezione generata quando si chiama SaveChanges() nel 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

Come fallisce quando si chiama 'SaveChanges' all'interno del ciclo? –

+0

Ho aggiunto l'eccezione alla domanda. – RyanW

risposta

6

Questa domanda potrebbe aiutare: https://stackoverflow.com/questions/2113498

Non puoi iniziare una nuova transazione mentre si sta ancora leggendo il risultato impostato e SaveChanges crea una transazione se non ne esiste già una.

Una soluzione è completare prima la lettura, quindi scorrere il set di risultati in memoria.

Se si cambia questa linea:

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

... si può mettere SaveChanges indietro all'interno del ciclo?

+0

Sì, questo è il lavoro. Grazie! – RyanW

2

EF non mancherà di tenere traccia di tutte le modifiche e aggiornerà la DB quando si chiama SaveChanges(). Non chiamerei quindi SaveChanges() all'interno di un ciclo (anche se non dovrebbe fallire).

Tenere a mente EF esegue un roundtrip del database separato per ogni record che si desidera inserire, aggiornare o eliminare, quindi in genere non importa quanto spesso si chiami SaveChanges. Evitare questo è per lo più possibile solo quando si utilizza l'SQL diretto e si crea un singolo SqlCommand che esegue tutti gli inserimenti contemporaneamente.

In ogni caso questo errore è dovuto a Entity Framework creazione di una transazione implicita durante la SaveChanges() chiamata

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

Sì, desidero inviare l'aggiornamento (round di andata e ritorno del database) ogni volta attraverso il ciclo piuttosto che tutto alla fine. Ma non funziona per chiamare SaveChanges() ogni volta. Dal momento che utilizzo sprocs per inserti e aggiornamenti, cercherò di eseguire una funzione import nell'aggiornamento e di chiamarla direttamente nel contesto. – RyanW

+0

Ho modificato la mia risposta –

+0

Grazie, ha senso. In questo caso, voglio che ogni aggiornamento viva da solo e non sia soggetto a rollback se una fattura successiva non riesce. – RyanW

Problemi correlati