使用陈述问题

发布于 2024-08-27 18:38:33 字数 204 浏览 5 评论 0原文

我有两个问题。

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

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

谢谢!

I have two questions.

1) Should you always use a using statement on a connection? So, I would use it on the connection and then another one on a reader within the connection? So I would be using two using statements.

2) Lets say you use the using statement on the connection and also a reader being returned on the connection. So you have two using statements. Does it create two Try{}Finally{} blocks or just one?

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

微凉 2024-09-03 18:38:34

这里要小心。您应该始终任何实现IDisposable的本地对象上使用语句。这不仅包括连接和读者,还包括命令。但有时 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() 函数接受 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;
       }
     );
}

Be careful here. You should always have a using statement on any local object that implements IDisposable. That includes not only connections and readers, but also the command. But it can be tricky sometimes exactly where that using statement goes. If you're not careful it can cause problems. For example, in the code that follows the using statement will close your reader before you ever get to use it:

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

Instead, you have four options. One is to wait to create the using block until you call the function:

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

Of course, you still have to careful with your connection there and it means remember to write a using block everywhere you use the function.

Option two is just process the query results in the method itself, but that breaks separation of your data layer from the rest of the program. A third option is for your MyQuery() function to accept an argument of type Action that you can call inside the while (rdr.Read()) loop, but that's just awkward.

I generally prefer option four: turn the data reader into an IEnumerable, like this:

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

Now everything will be closed correctly, and the code that handles it is all in one place. You also get a nice bonus: your query results will work well with any of the linq operators.

Finally, something new I'm playing with for the next time I get to build a completely new project that combines the IEnumerable with passing in a delegate argument:

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

And then I'll use it within the data layer like this:

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;
       }
     );
}
箜明 2024-09-03 18:38:34

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

1) Should you always use a using
statement on a connection? So, I would
use it on the connection and then
another one on a reader within the
connection? So I would be using two
using statements.

Yes, because they implement IDisposable. And don't forget a using statement on the command too :

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) Lets say you use the using
statement on the connection and also a
reader being returned on the
connection. So you have two using
statements. Does it create two
Try{}Finally{} blocks or just one?

Each using statement will create its own try/finally block

心碎无痕… 2024-09-03 18:38:34
  1. 当对象实现 IDisposable 时,您应该始终使用 using 语句。这包括连接。

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

  1. You should always use a using statement when an object implements IDisposable. This includes connections.

  2. It will create two nested try{}finally{} blocks.

娇妻 2024-09-03 18:38:34

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

Special point on 1). You need to specifically avoid that technique when the connection is used in asynchronous ADO.NET methods - like BeginExecuteReader, because more than likely, you will fall out of scope and try to dispose the connection while the async operation is still in progress. This is similar to the case when you are using class variables and not local variables. Often times the connection reference is stored in a class used as the "control block" for the asynchronous operation.

纵性 2024-09-03 18:38:34

回答每个问题:

1)是的,最好的做法是尽快处理掉这两个问题。

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

To answer each one:

1) Yes, this would be best practice to dispose both as soon as possible.

2) using() will create two blocks, wrapped in each other in the same order. It will dispose the inner object (the reader) first, then dispose the object from the outer using (the connection).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文