为什么在 SqlTransaction 中要使用 using 语句?

52

我在我的代码中使用了SqlTransaction,但遇到了一些问题。在搜索过程中,我看到很多人都在使用带有SqlTransaction的using语句。

使用这种类型的语句与SqlTransaction有什么好处和/或区别?

using (SqlConnection cn = new SqlConnection())
{
     using (SqlTransaction tr = cn.BeginTransaction())
     {
      //some code
      tr.Commit();
     }
}

目前我的代码是这样的:

SqlConnection cn = new SqlConnection(ConfigurationManager.AppSettings["T3"]);
cn.Open();
SqlTransaction tr = cn.BeginTransaction();

try
{
     //some code
     tr.Commit();
     cn.Close();
}
catch(Exception ex)
{
      tr.Rollback();
      cn.Close();
      throw ex;
}

一个比另一个更有优势是什么?


7
为了减少嵌套,我认为你可以跳过第一对花括号。(或者是大括号...) - Svish
8个回答

62

每次在代码块的作用域内创建一个实现了 IDisposable 接口的类的实例时,都应该使用 using 语句。这可以确保无论是否抛出异常,Dispose() 方法都会被调用。

尤其是,你的代码只捕获托管异常,然后通过抛出新异常而不是重新抛出现有异常来销毁堆栈帧。

正确的做法是:

using (SqlConnection cn = new SqlConnection(ConfigurationManager.AppSettings["T3"])) {
    cn.Open();
    using (SqlTransaction tr = cn.BeginTransaction()) {
        //some code
        tr.Commit();
    }
}

请注意,如果您的类具有实现了IDisposable接口的实例成员,则您的类必须实现IDisposable接口,并在其自己的Dispose()方法中处理这些成员的清理工作。


4
因为它保证了实现的IDisposable接口的“Dispose”方法将被调用-无论您的代码发生什么情况。 - marc_s
3
@Downvoter:能否说一下为什么?否则你就没什么意义了。 - John Saunders
3
如果在提交之前调用了SqlTransaction上的Dispose方法,那么事务将会被回滚。当然,如果在块内部引发异常并且未处理,则会调用Dispose方法。 - John Saunders
14
在 using 块中,我们是否需要在 catch 内明确声明 tran.rollBack(),还是这将由 using 块处理? - Zo Has
5
@ZoHas it will be handled in the Dispose @ZoHas 它将在Dispose中处理 - Erik Bergstedt
显示剩余6条评论

30

这是因为如果没有显式提交(例如,如果抛出异常),SqlTransaction对象将在其Dispose()方法中回滚。换句话说,它与您的代码具有相同的效果,只是更加简洁。


3
已通过反编译进行确认。在 this.Dispose() 上调用 this.Rollback()。 - ddotsenko
7
实际上,Dispose() 方法内是否调用 Rollback 取决于您使用的驱动程序的实现(请参见 http://msdn.microsoft.com/en-us/library/bf2cw321(v=vs.110).aspx)。驱动程序的实现者应该调用 Rollback,但 Microsoft 建议不要依赖它。因此,如果您知道您将使用的唯一驱动程序会在 Dispose() 内部调用 Rollback,则是安全的。否则,最好显式调用它。 - d512

16

实质上,“using”与您所做的事情相同,只是在finally块中而不是捕获所有异常:

using (SqlConnection cn = new SqlConnection())
{
     using (SqlTransaction tr = cn.BeginTransaction())
     {
      //some code
      tr.Commit();
     }
}

相同,只是代码更少 :)

{
    SqlConnection cn = null;
    try
    {
       cn = new SqlConnection();
       {
           SqlTransaction tr = null;
           try
           {
               tr = cn.BeginTransaction())

               //some code
               tr.Commit();
            }
            finally
            {
                if(tr != null && tr is IDisposable)
                {
                    tr.Dispose();
                }
            }
        }
    }
    finally
    {
        if(cn != null && cn is IDisposable)
        {
            cn.Dispose();
        }
    }
}

你的第一个例子中实际上有多余的一对花括号:你可以嵌套使用语句而不创建新块,例如 using (x = new X) using (y = new Y) { } - Robert Rossney
3
为了明确表达意思,我们总是在所有 if 语句周围加上大括号,即使它们只有一行代码。 - heavyd
2
在使用 using (SqlTransaction tr ... 之前,您还应该执行 cn.Open();。否则,您将收到 InvalidOperationException 异常。或者我有什么遗漏吗? - Laurynas Lazauskas

9
最终,“using”只是一种模式的快捷方式。但它是非常有用和有帮助的快捷方式,因为它确保您正确实现该模式并意味着您可以使用更少的代码来完成它。
在这种情况下,您没有正确地实现该模式。如果调用“tr.RollBack()”也引发异常,那么您的代码会发生什么?

4
使用语句(using statement)可以自动关闭和释放您的连接和事务。这相当于在try/catch块上加一个finally块来进行释放。
您还可以将使用语句块压缩为以下格式...
using (SqlConnection cn = new SqlConnection())
using (SqlTransaction tr = cn.BeginTransaction())     
{
      //some code
      tr.Commit();
}

这大致相当于:
SqlConnection cn = null;
SqlTransaction tr = null;
try
{
    cn = new SqlConnection());
    tr = cn.BeginTransaction());

    //some code
    tr.Commit();
}
finally
{
    if (cn != null)
        cn.Dispose();
    if (tr != null)    
        tr.Dispose();
}

非常接近,但其中还有一个额外的匿名作用域块。您现在的代码无法编译,因为finally块中cn和tr超出了范围。 - Joel Coehoorn
2
@ ZoHas 如果没有调用 Commit();,则在 Dispose(); 处自动回滚 参见 - WiiMaxx

3
如果您不使用using()块,则必须显式调用SqlConnection和SqlTransaction对象的.Dispose()方法。如果未能这样做,则未托管的资源将不会被释放,可能会导致内存泄漏或其他问题。

2
不会导致内存泄漏,但可能会导致资源泄漏。 - Joel Coehoorn
1
这将使你更糟,更快地失败。 - Andrei Rînea

1

使用 using 可以确保在代码返回后,连接对象将被处理。Dispose 方法有助于释放非托管资源。作为良好的实践,如果一个对象实现了 IDisposable 接口,应该始终调用 dispose 方法。


连接 - 以及 SqlTransaction 对象。 - John Saunders
如果一个对象实现了 IDisposable 接口,那么应该使用 using 块(或语句),它保证了 Dispose 方法会被调用。 - John Saunders

1
此外,它可以美化您的代码。7行代码不比14行更好看吗?每次看到using块时我都会松一口气。就像那个带有芳香的小喷雾一样。嗯,我是一个高效的漂亮代码块。看看我如何管理内存以及我对眼睛多么舒适。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接