使用SqlDataReader作为资源的习惯用语

4

继承于这个问题,我发现自己一遍又一遍地写以下代码:

SqlCommand command = new SqlCommand();
// Code to initialize command with what we want to do
using (SqlConnection connection = openConnection())
{
    command.Connection = connection;
    using (SqlDataReader dataReader = thisCommand.ExecuteReader())
    {
        while (dataReader.Read())
        {
            // Do stuff with results
        }
    }
}

嵌套两个using语句有些繁琐。有没有一种方法可以“告诉”SqlDataReader它拥有命令,并告诉命令它拥有连接?

如果有这样的方法,那么我可以编写一个帮助方法,可以这样调用:

// buildAndExecuteCommand opens the connection, initializes the command
// with the connection and returns the SqlDataReader object. Dispose of the
// SqlDataReader to dispose of all resources that were acquired
using(SqlDataReader reader = buildAndExecuteCommand(...))
{
    // Do stuff with reader
}

还是我必须硬着头皮自己编写SqlDataReader的包装器?(注:SqlDataReader是一个用于从数据库中读取数据的类)

实际上,在上述代码中,SqlCommand 也应该放在 using 语句中,这样你就会有三个嵌套的语句。 - John Saunders
5个回答

8

有一种方法是编写一个方法来为您处理处理,通过调用委托并返回每个结果。例如:

using (SqlConnection connection = openConnection())
{
    command.Connection = connection;
    ExecuteReaderWithCommand(command, reader =>
    {
        // Do stuff with the result here.
    });
}

然后 `ExecuteReaderWithCommand` 应该是这样的:
public static void ExecuteReaderWithCommand(SqlCommand command,
    Action<SqlDataReader> action)
{
    using (SqlDataReader dataReader = thisCommand.ExecuteReader())
    {
        while (reader.Read())
        {
            action(reader);
        }
    }
}

如果您愿意,您可以将此方法作为扩展方法添加到 SqlCommand 上。如果您愿意,您甚至可以让它为您打开连接...抽象掉“打开/使用/关闭”这个概念的程度越高,效果就越好。


+1,使用lambda表达式会更简洁。我也是这么想的,但像往常一样,Skeet先生反应非常迅速 :-) - Dan F
我同意“你可以尽可能地抽象化‘打开/使用/关闭’的概念”- 这正是DAAB在某种程度上所做的。 - RichardOD
是的...不过我们使用的是.NET 2.0,所以委托语法变得相当笨拙。叹气 - Paul Hollingsworth

1
你可以这样写,然后告诉 dataReader 在使用完毕后关闭连接:
SqlCommand command = new SqlCommand();
command.Connection = openConnection();
using (SqlDataReader dataReader = command.ExecuteReader(CommandBehavior.CloseConnection))
{
    while (dataReader.Read())
    {
        // Do stuff with results
    }
}

然而最好显式地关闭连接,因为在连接打开和ExecuteReader之间可能会发生异常。


0

为什么不看一下企业库 DAAB呢?

这是来自文档的代码示例,已针对您的情况进行了调整:

Database db = DatabaseFactory.CreateDatabase();

using (IDataReader reader = db.ExecuteReader(CommandType.Text, "SQL here..." ))
{
   while (reader.Read())
    {
        action(reader);
    }
}

因为ADO.NET适用于大多数需求,通常没有必要再使用另一个库。 - arbiter
1
@Arbiter。我从未说过使用,我说过看一下。它解决了问题。是否拥有“另一个库”是问题,这真的取决于Paul的决定。DAAB的设计目标之一是防止开发人员重复编写相同的代码。 - RichardOD
同意,决定始终由作者做出 :) - arbiter

0

当然可以使用委托,如 Action<T>,但自 .NET 1.0 以来我已经使用了一组类似下面的重载。调用者使用 using 块来释放返回的 reader,从而释放连接。

public IDataReader ExecuteReader(string commandText, CommandType commandType, 
                                      IDbDataParameter[] parameters)
{
    IDbConnection connection = CreateConnection();
    try
    {
        IDbCommand command = CreateCommand(commandText, connection);
        command.CommandType = commandType;
        AppendParameters(command, parameters);
        connection.Open();
        return command.ExecuteReader(CommandBehavior.CloseConnection);
    }
    catch
    {
        connection.Close();
        throw;
    }
}

0

你需要自己建立包装器,或者如果可行的话,可以使用ORM。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接