使用陈述问题

时间:2010-04-03 16:10:40

标签: c# .net using-statement

我有两个问题。

1)您是否应始终在连接上使用using语句?那么,我会在连接上使用它,然后在连接中的读者上使用另一个?所以我将使用两个使用语句。

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

谢谢!

6 个答案:

答案 0 :(得分:7)

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

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

相反,您有四种选择。一种是等到创建使用块,直到你调用函数:

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())
    {
        //...
    }
}

当然,你仍然需要小心你的连接,这意味着记得在你使用该功能的任何地方写一个使用块。

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

我通常更喜欢选项四:将数据读取器转换为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;
       }
     );
}

答案 1 :(得分:5)

  

1)你应该总是使用   关于连接的声明?所以,我愿意   在连接上使用它然后   另一个在读者内部   连接?所以我会用两个   使用陈述。

是的,因为他们实施了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)让我们说你使用了   关于连接的声明以及a   读者被退回了   连接。所以你有两个使用   声明。它创造了两个   尝试{}最后{}阻止或只阻止一个?

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

答案 2 :(得分:4)

  1. 当对象实现using时,您应始终使用IDisposable语句。这包括连接。

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

答案 3 :(得分:2)

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

答案 4 :(得分:1)

回答每一个:

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

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

答案 5 :(得分:0)

这篇文章可能对你很有意思:How to Implement IDisposable and Finalizers: 3 Easy Rules