5

我有两个问题。

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

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

谢谢!

4

6 回答 6

7

这里要小心。您应该始终在任何实现 IDisposable 的本地对象上使用 using 语句。这不仅包括连接和阅读器,还包括命令。但有时可能会很棘手,而 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;
       }
     );
}
于 2010-04-03T16:23:47.407 回答
5

1) 你应该总是在连接上使用 using 语句吗?那么,我会在连接上使用它,然后在连接内的阅读器上使用另一个?所以我会使用两个 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 语句,并且在连接上还返回了一个阅读器。所以你有两个 using 语句。它是创建两个 Try{}Finally{} 块还是只创建一个?

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

于 2010-04-03T16:24:34.410 回答
4
  1. 当对象实现时,您应该始终使用using语句IDisposable。这包括连接。

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

于 2010-04-03T16:13:47.290 回答
2

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

于 2010-04-03T16:52:42.680 回答
1

回答每一个:

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

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

于 2010-04-03T16:14:56.700 回答
0

可能这篇文章对你来说会很有趣:如何实现 IDisposable 和终结器:3 条简单规则

于 2010-04-03T16:34:25.120 回答