【问题标题】:Using statement question使用语句问题
【发布时间】:2011-02-04 00:49:38
【问题描述】:

我有两个问题。

1) 你应该总是在连接上使用 using 语句吗?那么,我会在连接上使用它,然后在连接内的阅读器上使用另一个?所以我会使用两个 using 语句。

2) 假设您在连接上使用了 using 语句,并且还在连接上返回了一个阅读器。所以你有两个 using 语句。它是创建两个 Try{}Finally{} 块还是只创建一个?

谢谢!

【问题讨论】:

    标签: c# .net using-statement


    【解决方案1】:

    这里要小心。您应该始终在实现 IDisposable 的 任何 本地对象上使用 using 语句。这不仅包括连接和阅读器,还包括命令。但有时可能会很棘手,正是 where using 语句。如果你不小心,它可能会导致问题。例如,在 using 语句后面的代码中,在您使用它之前将关闭您的阅读器:

    DataReader MyQuery()
    {
        string sql="some query";
        using (var cn = new SqlConnection("connection string"))
        using (var cmd = new SqlCommand(sql, cn))
        {
            cn.Open();
            using (var rdr = cmd.ExecuteReader())
            {
                return rdr;
            }
        }
    }
    

    相反,您有四个选择。一种是等待创建 using 块,直到您调用该函数:

    DataReader MyQuery()
    {
        string sql="some query";
        using (var cn = new SqlConnection("connection string"))
        using (var cmd = new SqlCommand(sql, cn))
        {
            cn.Open();
            return cmd.ExecuteReader();
        }
    }
    
    using (var rdr = MyQuery())
    {
        while (rdr.Read())
        {
            //...
        }
    }
    

    当然,你仍然要小心你的连接,这意味着记住在你使用函数的任何地方都写一个 using 块。

    选项二只是在方法本身中处理查询结果,但这会破坏数据层与程序其余部分的分离。第三种选择是让您的 MyQuery() 函数接受可以在 while (rdr.Read()) 循环内调用的 Action 类型的参数,但这很尴尬。

    我一般更喜欢方案四:把数据阅读器变成IEnumerable,像这样:

    IEnumerable<IDataRecord> MyQuery()
    {
        string sql="some query";
        using (var cn = new SqlConnection("connection string"))
        using (var cmd = new SqlCommand(sql, cn))
        {
            cn.Open();
            using (var rdr = cmd.ExecuteReader())
            {
                while (rdr.Read())
                  yield return rdr;
            }
        }
    }
    

    现在一切都将正确关闭,处理它的代码都在一个地方。您还可以获得一个不错的奖励:您的查询结果可以很好地与任何 linq 运算符一起使用。

    最后,下次我要构建一个将 IEnumerable 与传入委托参数相结合的全新项目时,我正在玩一些新的东西:

    //part of the data layer
    private static IEnumerable<IDataRecord> Retrieve(string sql, Action<SqlParameterCollection> addParameters)
    {
        //DL.ConnectionString is a private static property in the data layer
        // depending on the project needs, it can be implementing to read from a config file or elsewhere
        using (var cn = new SqlConnection(DL.ConnectionString))
        using (var cmd = new SqlCommand(sql, cn))
        {
            addParameters(cmd.Parameters);
    
            cn.Open();
            using (var rdr = cmd.ExecuteReader())
            {
                while (rdr.Read())
                  yield return rdr;
            }
        }
    }
    

    然后我会像这样在数据层中使用它:

    public IEnumerable<IDataRecord> GetFooChildrenByParentID(int ParentID)
    {
        //I could easily use a stored procedure name instead, and provide overloads for commandtypes.
        return Retrieve(
            "SELECT c.* 
             FROM [ParentTable] p 
             INNER JOIN [ChildTable] c ON c.ParentID = f.ID 
             WHERE f.ID= @ParentID", p => 
           {
              p.Add("@ParentID", SqlDbType.Int).Value = ParentID;
           }
         );
    }
    

    【讨论】:

    • "你应该总是在任何实现 IDisposable 的对象上使用一个 using 语句":这并不完全正确......你需要对实现 IDisposable 的 local 变量执行此操作,但不适用于班级成员,因为它们通常会在班级的其他地方重用。但是,在这种情况下,类本身应该实现 IDisposable,并在其 Dispose 方法中释放所有 IDisposable 成员
    • 还要注意,在 using 或 try/finally 块中执行“yield return”的类将实现 IDisposable,而未能正确 Dispose 它们将导致 using-Dispose 或 finally 代码不被执行.
    【解决方案2】:

    1) 你应该总是使用 using 关于连接的声明?所以,我会 在连接上使用它,然后 另一篇关于读者的 联系?所以我会用两个 使用语句。

    是的,因为它们实现了IDisposable。并且不要忘记命令中的using 声明:

    using (DbConnection connection = GetConnection())
    using (DbCommand command = connection.CreateCommand())
    {
        command.CommandText = "SELECT FOO, BAR FROM BAZ";
        connection.Open();
        using (DbDataReader reader = command.ExecuteReader())
        {
            while (reader.Read())
            {
                ....
            }
        }
    }
    

    2) 假设您使用 using 关于连接的声明以及 读者被退回 联系。所以你有两个使用 陈述。它会创建两个 尝试{}Finally{} 块还是只尝试一个?

    每个using 语句都会创建自己的try/finally

    【讨论】:

      【解决方案3】:
      1. 当对象实现IDisposable 时,您应该始终使用using 语句。这包括连接。

      2. 它将创建两个嵌套的try{}finally{} 块。

      【讨论】:

      • 您不应该总是使用using 语句 - 可能出现问题的一种情况是调用&lt;API&gt;Command.ExecuteReader。此方法可能会抛出异常,如果发生,它们将无法正确处理,您的软件将失败。更好地处理可能的异常并手动调用Dispose() 或其等效项。
      【解决方案4】:

      1) 的特别之处。当在asynchronous ADO.NET methods 中使用连接时,您需要特别避免该技术 - 例如 BeginExecuteReader,因为您很可能会超出范围并尝试在异步操作仍在进行时释放连接进行中。这类似于使用类变量而不是局部变量时的情况。连接引用通常存储在用作异步操作的“控制块”的类中。

      【讨论】:

        【解决方案5】:

        回答每一个:

        1) 是的,最好的做法是尽快处置两者。

        2) using() 将创建两个块,以相同的顺序相互包裹。它将首先处理内部对象(读取器),然后使用(连接)从外部处理对象。

        【讨论】:

          【解决方案6】:

          也许这篇文章会让你感兴趣:How to Implement IDisposable and Finalizers: 3 Easy Rules

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2012-09-11
            • 1970-01-01
            • 2021-10-09
            • 2010-09-25
            • 1970-01-01
            • 2023-03-05
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多