2012-06-06 56 views
5

Korzystanie z Entity Framework 4.3.1 Baza danych Po pierwsze, jaki jest dobry sposób na częste zatwierdzanie/zapisywanie zmian obiektów w bazie danych? W poniższej części chciałbym zapisać fakturę bezpośrednio po wywołaniu skróconych instrukcji i nie ryzykować czekania na opublikowanie wszystkich faktur. Ale nie mogę wywołać SaveChanges za każdym razem w pętli, to rzuci wyjątek.Częste zapisy ze strukturą encji

Przydałoby się mieć metodę .Save() na każdym obiekcie, być może jest na to dobry sposób?

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

Jest to wyjątek zgłoszony podczas wywoływania SaveChanges() w pętli.

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

Jak to się dzieje, gdy wywołujesz 'SaveChanges' w pętli? –

+0

Dodałem wyjątek do pytania. – RyanW

Odpowiedz

6

To pytanie może pomóc: https://stackoverflow.com/questions/2113498

Nie możesz rozpocząć nową transakcję to jeszcze podczas czytania wynik set i SaveChanges tworzy transakcję, jeśli jeszcze nie istnieje.

Jednym z rozwiązań jest ukończenie czytania w pierwszej kolejności, a następnie powtórzenie zestawu wyników w pamięci.

Jeśli zmienić ten wiersz:

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

... można umieścić SaveChanges powrotem wewnątrz pętli?

+0

Tak, to działa. Dzięki! – RyanW

2

EF będzie śledzić wszystkie zmiany i będzie aktualizować DB kiedy zadzwonić SaveChanges(). Nie będę wtedy wywoływać SaveChanges() w pętli (nawet jeśli nie powinno zawieść).

Pamiętaj, że EF tworzy oddzielną bazę danych dla wszystkich rekordów, które chcesz wstawić, zaktualizować lub usunąć, więc generalnie nie ma znaczenia, jak często dzwonisz do SaveChanges. Unikanie tego jest w większości możliwe tylko przy użyciu bezpośredniego SQL i tworzeniu pojedynczego SqlCommand wykonującego wszystkie inserty na raz.

Zresztą ten błąd jest spowodowany Entity Framework tworzenia transakcji niejawny podczas SaveChanges() rozmowy

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

Tak, chcę wydać aktualizację (obieg bazy danych) za każdym razem za pośrednictwem pętli, a nie wszystkich na końcu. Ale nie działa wywoływanie SaveChanges() za każdym razem. Ponieważ używam sprocs dla insertów i aktualizacji, przyjrzę się tylko temu, że importuję funkcję i aktualizuję ją bezpośrednio w kontekście. – RyanW

+0

Edytowałem moją odpowiedź –

+0

Dzięki, to ma sens. W tym przypadku chcę, aby każda aktualizacja działała sama i nie będzie podlegać wycofywaniu, jeśli później nie powiedzie się faktura w pętli. – RyanW

Powiązane problemy