2013-07-18 21 views
5

Używam EF5.0 z SQL Server 2008. Mam dwie bazy danych na tej samej instancji serwera. Potrzebuję zaktualizować tabele w obu bazach danych i chcę, aby były to same transakcje. Więc użyłem TransactionScope. Poniżej znajduje się kod -Wiele baz danych (datacontext) na tym samym serwerze bez MS DTC

public void Save() 
{ 
     var MSObjectContext = ((IObjectContextAdapter)MSDataContext).ObjectContext; 
     var AWObjectContext = ((IObjectContextAdapter)AwContext).ObjectContext; 

     using (var scope = new TransactionScope(TransactionScopeOption.Required, 
              new TransactionOptions 
               { 
                IsolationLevel = IsolationLevel.ReadUncommitted 
               })) 
     {    
      MSObjectContext.SaveChanges(SaveOptions.DetectChangesBeforeSave); 
      AWObjectContext.SaveChanges(SaveOptions.DetectChangesBeforeSave); 

      scope.Complete(); 
     } 
    } 

Kiedy używam powyższego kodu Transakcja zostanie awansowana do DTC. Po przeszukaniu w Internecie stwierdziłem, że dzieje się tak z powodu dwóch różnych połączeń/połączeń. Ale nie rozumiem, jeśli piszę procedurę przechowywaną na jednej bazie danych, która aktualizuje tabelę w innej bazie danych (na tym samym serwerze), nie jest wymagany kod DTC. Dlaczego EF lub TransactionScope promuje to w DTC? Czy jest w tym jakaś inna praca?

Proszę doradzić

góry dziękuję

Sai

Odpowiedz

4

zwykły DbConnection s, można zapobiec eskalacji DTC dla wielu baz danych na tym samym serwerze, przy użyciu tego samego ciąg połączenia (z dowolnej bazy danych jak) i ręcznie zmienić bazę danych na otwartym obiekcie połączenia, tak:

using (var tx = new TransactionScope()) 
{ 
    using (var conn = new SqlConnection(connectStr)) 
    { 
     conn.Open(); 
     new SqlCommand("INSERT INTO atest VALUES (1)", conn).ExecuteNonQuery(); 
    } 
    using (var conn = new SqlConnection(connectStr)) 
    { 
     conn.Open(); 
     conn.ChangeDatabase("OtherDB"); 
     new SqlCommand("INSERT INTO btest VALUES (2)", conn).ExecuteNonQuery(); 
    } 
    tx.Complete(); 
} 

nie będzie eskalować do DTC, ale jeśli użyjesz innych wartości dla connectStr,

Nie jestem zaznajomiony z EF i sposobem, w jaki zarządza on połączeniami i kontekstami, ale korzystając z powyższego wglądu, możesz uniknąć eskalacji kodu DTC, wykonując conn.ChangeDatabase(..), a następnie tworząc kontekst, taki jak new DbContext(conn, ...).

Ale proszę pamiętać, że nawet ze wspólną ciąg połączenia, tak szybko, jak masz dwa połączenia otwarte w tym samym czasie, DTC będzie angażować się, jak w tym zmodyfikowanym przykład:

using (var tx = new TransactionScope()) 
{ 
    using (var conn = new SqlConnection(mssqldb)) 
    { 
     conn.Open(); 
     new SqlCommand("INSERT INTO atest VALUES (1)", conn).ExecuteNonQuery(); 
     using (var conn2 = new SqlConnection(mssqldb)) 
     { 
      conn2.Open(); 
      conn2.ChangeDatabase("otherdatabase"); 
      new SqlCommand("INSERT INTO btest VALUES (2)", conn2).ExecuteNonQuery(); 
     } 
    } 
    tx.Complete(); 
} 
+0

chcę popełnić transakcje na dwóch całkowicie różnych dbs na tym samym serwerze w ramach jednego zakresu transakcji. Mogłem to zrobić za pomocą procedur przechowywanych, ale nie poprzez EF. Dziękuję za odpowiedź. – Sai

+0

Moja odpowiedź pokazuje, jak użyć 'TransactionScope', aby to zrobić bez odwoływania się do zapisanych procesów. Niejawne 'tx.Dispose()', które występuje po linii 'tx.Complete()', spowoduje zatwierdzenie pojedynczej transakcji, która obejmuje oba INSERTy do dwóch różnych baz danych. Jak wspomniano, w EF istnieją sposoby na zapewnienie połączeń, w których ręcznie nazwano 'ChangeDatabase()', ale w zależności od bazy kodu, które mogą skutkować bardziej ściśle powiązanym kodem (podczas gdy punktem 'TransactionScoep' jest rozłączanie kodu), tylko Ty możesz powiedzieć. –

+0

Nie sądzę, abym mógł używać ChangeDatabase(), ponieważ używam podejścia Databasefirst, a mój dbcontext jest ściśle związany z encjami. Jeśli zmienię łańcuch połączeń w środowisku wykonawczym na zupełnie inną bazę danych, spowoduje to błąd. – Sai