【问题标题】:Why use a using statement with a SqlTransaction?为什么使用带有 SqlTransaction 的 using 语句?
【发布时间】:2022-01-13 01:37:18
【问题描述】:

我遇到了一些关于我在代码中使用的 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;
}

一种方式比另一种方式有什么优势?

【问题讨论】:

  • 对于“少”的嵌套,我相信你可以跳过第一对大括号。 (或者是大括号...)

标签: c# using-statement sqltransaction system.data system.data.sqlclient


【解决方案1】:

每次创建实现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() 调用期间释放这些成员。

【讨论】:

  • ....因为它保证将调用正在实现的 IDisposble 接口的“Dispose”方法 - 无论您的代码中发生什么。
  • +1,一般正确,但不能总是使用using语句,有时需要自己实现IDisposable。我会说:“只要有可能”,这意味着“只要创建一个仅在一个集团内使用的 IDisposable 实例”或类似的东西。
  • 是的,如果在SqlTransaction 上调用DisposeCommit 之前,那么事务将被回滚。当然,Dispose 会在块内抛出异常且未处理时被调用。
  • 我们是否必须在 catch 内显式声明 tran.rollBack() 还是由 using 块处理?
  • @ZoHas 将在 Dispose 中处理
【解决方案2】:

这样做的原因是,如果 SqlTransaction 对象没有显式提交(例如,如果抛出异常),它将在其 Dispose() 方法中回滚。换句话说,它的效果和你的代码一样,只是更简洁一些。

【讨论】:

  • 通过反编译确认了这一点。在 this.Dispose() 上调用 this.Rollback()。
  • 实际上,是否在 Dispose() 中调用 Rollback 取决于您正在使用的驱动程序的实现(请参阅msdn.microsoft.com/en-us/library/bf2cw321(v=vs.110).aspx)。驱动程序实现者应该调用 Rollback,但 Microsoft 建议不要指望它。因此,如果您知道您将使用的唯一驱动程序确实在 Dispose() 中调用 Rollback,那么您是安全的。否则,显式调用它会更安全。
【解决方案3】:

基本上 using 与您正在做的事情相同,除了 int a 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 语句而无需创建新块,例如using (x = new X) using (y = new Y) { }
  • 明确地说,同样的原因,我们总是在所有 if 语句周围加上大括号,即使它们是单行的
  • 您还应该在using (SqlTransaction tr ... 之前执行cn.Open();。否则您将收到 InvalidOperationException。还是我错过了什么?
【解决方案4】:

最后,using 只是模式的快捷方式。但它是一个非常有用和有用的快捷方式,因为它可以确保您正确实现该模式,并且意味着您可以用更少的代码完成它。

在这种情况下,您没有正确实现该模式。如果对tr.RollBack() 的调用也引发异常,您的代码会发生什么情况?

【讨论】:

    【解决方案5】:

    using statement 正在为您关闭并处理您的连接和事务。这相当于在你的 try/catch 上有一个 finally 块来执行 dispose。

    你也可以像这样压缩 using 块...

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

    【讨论】:

    • 非常接近,但其中还有一个额外的匿名范围块。您拥有的代码不会编译,因为 cn 和 tr 在 finally 块中超出范围。
    • @ZoHas 如果没有调用Commit(); see also,它会自动回滚到Dispose();
    【解决方案6】:

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

    【讨论】:

    • 不会导致内存泄漏,但可能会导致资源泄漏。
    • ...这会让你变得更糟更快。
    【解决方案7】:

    使用 using 保证,您的连接对象将在代码返回后被释放。 Dispose 对释放未管理的资源很有用,作为一个好习惯,如果一个对象实现了 IDisposable,则应始终调用 dispose 方法

    【讨论】:

    • 连接 - 和 SqlTransaction 对象。
    • 如果一个对象实现了IDisposable,那么应该使用using块(或语句),这保证Dispose会被调用。
    【解决方案8】:

    除此之外,它还美化了您的代码。 7行代码不比14行好看吗?每次看到 using 块时,我都会松一口气。就像从那个令人愉快的臭东西中喷出的小雾一样。嗯,我是一个非常高效的代码块。看看我管理记忆的能力有多好,我有多赏心悦目。

    【讨论】:

      猜你喜欢
      • 2010-11-10
      • 1970-01-01
      • 1970-01-01
      • 2014-06-04
      • 1970-01-01
      • 2014-07-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多