Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Frequent saves with entity framework

Using Entity Framework 4.3.1 Database First, what is a good way to commit/save object changes to the database frequently? In the below, I'd like to save the invoice immediately after the quickbooks call, and not risk waiting for all invoices to be posted. But, I cannot call SaveChanges each time in the loop, it will throw an exception.

It would be handy to have a .Save() method on each object, perhaps there's a good way to do that?

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

This is the exception thrown when calling SaveChanges() in the loop.

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)
like image 768
RyanW Avatar asked Jun 06 '12 13:06

RyanW


1 Answers

This question might help: https://stackoverflow.com/questions/2113498

You cannot start a new transaction while you are still reading a result set and SaveChanges creates a transaction if one does not already exist.

One solution is to complete the read first, then iterate over the in-memory result set.

If you change this line:

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

... can you put SaveChanges back inside the loop?

like image 124
Nick Butler Avatar answered Sep 25 '22 07:09

Nick Butler