6

Entity Framework 4.3.1 データベースの使用 まず、オブジェクトの変更をデータベースに頻繁にコミット/保存するための良い方法は何ですか? 以下では、quickbooks 呼び出しの直後に請求書を保存し、すべての請求書が投稿されるのを待つ危険を冒したくありません。しかし、ループ内で毎回 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)
4

2 に答える 2

7

この質問が役立つかもしれません: https://stackoverflow.com/questions/2113498

結果セットの読み取り中に新しいトランザクションを開始することはできず、トランザクションがまだSaveChanges存在しない場合は作成されます。

1 つの解決策は、最初に読み取りを完了してから、メモリ内の結果セットを反復処理することです。

この行を変更した場合:

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

...SaveChangesループ内に戻すことはできますか?

于 2012-06-06T14:31:39.483 に答える
2

EF はすべての変更を追跡し、呼び出したときに DB を更新しますSaveChanges()SaveChanges()その後、ループ内で呼び出しません(失敗しない場合でも)。

EF は、挿入、更新、または削除するすべてのレコードに対して個別のデータベース ラウンドトリップを行うため、通常、SaveChanges を呼び出す頻度は重要ではないことに注意してください。これを回避できるのは、ダイレクト SQL を使用し、すべての挿入を一度に実行する単一の SqlCommand を作成する場合のみです。

とにかく、このエラーは、Entity Framework がSaveChanges()呼び出し中に暗黙的なトランザクションを作成することが原因です。

using (var transaction = new TransactionScope())
{
    using (var context = new MyContext())
    {
      foreach (Invoice invoice in unpostedInvoices)
        {
            // Change to invoice
            context.SaveChanges();
        }
    }
    transaction.Complete();
}
于 2012-06-06T13:44:31.167 に答える