2012-03-01 15 views
5

Czy muszę wywołać dispose w bloku finally dla SqlTransaction? Udawaj, że programista nie używał USING nigdzie i po prostu spróbuj/złap.Czy SqlTransaction wymaga wywołania Dispose?

+0

nie zaszkodzi mieć to. – Brian

+1

Jeśli nie używasz 'using' wokół' sqlTrans', to z pewnością nie zaszkodzi jawnie wywołać 'Dispose()' na nim. –

+0

@Cory Czy jest wymagane sprawdzenie, czy sqlTrans jest już usunięte przed wywołaniem sqlTrans.Dispose()? – Lijo

Odpowiedz

9

Czy muszę używać try-finally lub using -statement pozbyć się SqlTransaction?

Nie zaszkodzi mieć to. Dotyczy to każdej klasy implementującej IDisposable, w przeciwnym razie nie implementowałby tego interfejsu.

Ale zwykle śmieciarz poradziłby sobie z nim, gdyby obiekt nie był już przywoływany. Ponieważ nie chcę również wywoływać dispose na co drugiej zmiennej lub używać using-statement wszędzie, zawsze warto przyjrzeć się faktycznej implementacji metody klasy "Dispose.

SqlTransaction.Dispose:

protected override void Dispose(bool disposing) 
{ 
    if (disposing) 
    { 
     SNIHandle target = null; 
     RuntimeHelpers.PrepareConstrainedRegions(); 
     try 
     { 
      target = SqlInternalConnection.GetBestEffortCleanupTarget(this._connection); 
      if (!this.IsZombied && !this.IsYukonPartialZombie) 
      { 
       this._internalTransaction.Dispose(); 
      } 
     } 
     catch (OutOfMemoryException e) 
     { 
      this._connection.Abort(e); 
      throw; 
     } 
     catch (StackOverflowException e2) 
     { 
      this._connection.Abort(e2); 
      throw; 
     } 
     catch (ThreadAbortException e3) 
     { 
      this._connection.Abort(e3); 
      SqlInternalConnection.BestEffortCleanup(target); 
      throw; 
     } 
    } 
    base.Dispose(disposing); 
} 

Bez zrozumienia wszystkich (lub cokolwiek) co się dzieje tutaj, mogę powiedzieć, że jest to więcej niż prosty base.Dispose(disposing). Więc może być dobrym pomysłem zapewnienie, że SqlTransaction zostanie usunięty.

Ale ponieważ SqlConnection.BeginTransaction tworzy transakcję może to być dobry pomysł, aby reflect tym również:

public SqlTransaction BeginTransaction(IsolationLevel iso, string transactionName) 
{ 
    SqlStatistics statistics = null; 
    string a = ADP.IsEmpty(transactionName) ? "None" : transactionName; 
    IntPtr intPtr; 
    Bid.ScopeEnter(out intPtr, "<sc.SqlConnection.BeginTransaction|API> %d#, iso=%d{ds.IsolationLevel}, transactionName='%ls'\n", this.ObjectID, (int)iso, a); 
    SqlTransaction result; 
    try 
    { 
     statistics = SqlStatistics.StartTimer(this.Statistics); 
     SqlTransaction sqlTransaction = this.GetOpenConnection().BeginSqlTransaction(iso, transactionName); 
     GC.KeepAlive(this); 
     result = sqlTransaction; 
    } 
    finally 
    { 
     Bid.ScopeLeave(ref intPtr); 
     SqlStatistics.StopTimer(statistics); 
    } 
    return result; 
} 

Jak widać. Model GC będzie również utrzymywać połączenie połączenia podczas tworzenia transakcji. Nie ma również odniesienia do transakcji, ponieważ zwraca ją tylko. Dlatego może nie zostać usunięty, nawet gdy połączenie jest już usunięte. Kolejny argument do dyspozycji transakcji.

Możesz również rzucić okiem na TransactionScope class, który jest bardziej odporny na awarie niż BeginTransaction. Zajrzyj na stronę this question, aby uzyskać więcej informacji.

+0

Możesz również chcieć spojrzeć na BeginSqlTransaction() oraz kod Connection.Dispose(). SqlTransaction wydaje się być wrpapper wokół jego InternalTransaction, który jest wyczyszczone w Connection.Dispose(). –

+1

Z całym szacunkiem nie zgadzam się z komentarzem "Jest to prawdą dla każdej klasy implementującej IDisposable". A co z kontrolką TableCell i innymi formantami HTML. Zaleca się, aby NIE stosować do nich "używania". – Lijo

+1

@Lijo: Punkt, który był zbyt ogólny. Musisz tylko nadpisać implementację lub użyć instrukcji 'using', jeśli potrzebujesz pozbyć się niezarządzanych zasobów, takich jak połączenie z bazą danych. Kontrola implementuje "IDpozwala", ponieważ kontrola (np. "UserControl") może zawierać niezarządzane zasoby i zostanie usunięta pod koniec cyklu życia (strona rekursywnie do wszystkich kontrolek potomnych). –

7

W ogólnym przypadku, każdy obiekt IDisposable, który konstruujesz lub pozyskujesz i który posiadasz, musisz się pozbyć.

W konkretnym przypadku istnieją pewne wyjątki, ale SqlTransaction nie jest jednym z nich.

Jak na the documentation of SqlTransaction.Dispose:

Uwalnia niezarządzani zasoby wykorzystywane przez DbTransaction i opcjonalnie zwalnia zarządzanych zasobów.

(podkreślenie moje)

Ponieważ dokumentacja nie wskazuje, że te niezarządzani zasoby są uwalniane podczas wydawania commit lub rollback, trzeba będzie wyrzucać tego obiektu.

0

Myślę, że możesz po prostu zamknąć połączenie. Sprawdziłem kod BCL - wygląda na to, że połączenia dbają o jego transakcję - nie ma potrzeby zamykania go jawnie.