2012-06-06 72 views
5

使用实体框架4.3.1数据库首先,经常提交/保存对象更改到数据库的好方法是什么?在下面,我想在快速电话通话后立即保存发票,而不用冒着等待发布所有发票的风险。但是,我无法每次在循环中调用SaveChanges,它都会抛出异常。经常用实体框架保存

在每个对象上都有一个.Save()方法会很方便,也许有一种很好的方法可以做到这一点?

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

这是在循环中调用SaveChanges()时引发的异常。

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

当你在循环中调用'SaveChanges'时它是如何失败的? –

+0

我在问题中添加了异常。 – RyanW

回答

6

期间,由于实体框架创建一个隐式事务这个问题可能会有所帮助:https://stackoverflow.com/questions/2113498

你不能启动新的事务,而你还在读出结果如果不存在,则创建一个事务并且SaveChanges创建一个事务。

一种解决方案是首先完成读取操作,然后遍历内存中的结果集。

如果改变这一行:

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

...你可以把SaveChanges回到内循环?

+0

是的,这是工作。谢谢! – RyanW

2

EF将跟踪所有更改,并在您致电SaveChanges()时更新数据库。 我不会在循环中调用SaveChanges()(即使它不应该失败)。

请记住,EF会为要插入,更新或删除的每条记录创建单独的数据库往返程序,因此通常无论您多久打电话一次SaveChanges都无关紧要。避免这种情况大多只有在使用直接SQL和创建单个SqlCommand一次执行所有插入操作时才是可能的。

反正这个错误是SaveChanges()呼叫

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

是的,我想每次通过循环发出更新(数据库往返),而不是最后。但是,每次都无法调用SaveChanges()。由于我使用sprocs进行插入和更新,因此我只会在更新上执行函数导入并直接在上下文中调用它。 – RyanW

+0

我编辑了我的答案 –

+0

谢谢,这是有道理的。在这种情况下,我希望每个更新都能独立运行,但如果循环后面的发票发生故障,则不会进行回滚。 – RyanW