SqlTransaction doit-il avoir été convoqué?
-
15-11-2019 - |
Question
Dois-je appeler Disser dans le bloc enfin pour SQLTRansaction? Imaginez que le développeur n'utilisait en utilisant nulle part et essayez / attrapez / attrape.
SqlTransaction sqlTrans = con.BeginTransaction();
try
{
//Do Work
sqlTrans.Commit()
}
catch (Exception ex)
{
sqlTrans.Rollback();
}
finally
{
sqlTrans.Dispose();
con.Dispose();
}
La solution
Dois-je utiliser
try-finally
ou lausing
-Sédition pour disposer leSqlTransaction
?
Cela ne fait pas de mal de l'avoir. Cela est vrai pour chaque mise en œuvre de classe Idisposable, Sinon, il n'implémenterait pas cette interface.
Mais normalement, le collecteur des ordures le traiterait si l'objet n'est plus référencé. Parce que je ne veux pas non plus appeler dispose
à chaque seconde variable ou utilisez le Utilisation de stades Partout, il vaut toujours la peine d'examiner la mise en œuvre réelle de la classe ' Dispose
méthode.
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);
}
Sans comprendre tout (ou quoi que ce soit) ce qui se passe ici, je peux dire que c'est plus qu'un simple base.Dispose(disposing)
. Il pourrait donc être une bonne idée de s'assurer qu'un SQLTransaction est éliminé.
Mais parce que SqlConnection.BeginTransaction
crée la transaction, il pourrait également être une bonne idée de refléter ça aussi:
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;
}
Comme tu peux le voir. La GC maintiendra également la connexion en vie lors de la création d'une transaction. Il ne détient pas non plus de référence à la transaction car il ne le renvoie. Par conséquent, il peut ne pas être disposé même lorsque la connexion est déjà disposée. Un autre argument pour éliminer la transaction.
Vous pourriez également jeter un œil au TransactionScope
classer ce qui est plus sûr que BeginTransaction
. Jettes un coup d'oeil à cette question pour plus d'informations.
Autres conseils
Dans le cas général, chaque IDisposable
Objet que vous construisez ou obtenez, et possédez, vous devez éliminer.
Dans le cas spécifique, il y a quelques exceptions, mais SqlTransaction
n'est pas l'un d'eux.
Selon le la documentation de SqlTransaction.Dispose
:
Libère le Ressources non gérées Utilisé par le DBTransaction et libère éventuellement les ressources gérées.
(mon accent)
Étant donné que la documentation ne stipule pas que ces ressources non gérées sont publiées lorsque vous émettez un commit ou un retour en arrière, vous devrez éliminer cet objet.
Je pense que vous pouvez simplement clôturer la connexion. J'ai vérifié le code de BCL - semble que les connexions s'occupent de sa transaction - pas besoin de la fermer explicitement.