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

Posted

技术标签:

【中文标题】使用 SqlDataReader 作为资源的习惯用法【英文标题】:Idiom for using SqlDataReader as a resource 【发布时间】:2009-06-30 11:07:32 【问题描述】:

继this 问题之后,我发现自己一遍又一遍地编写以下代码:

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 上编写自己的包装器?

【问题讨论】:

其实,上面的SqlCommand也应该在using语句中,所以你会有3个嵌套语句。 【参考方案1】:

有一件事是编写一个为您处理的方法,用每个结果回调一个委托。例如:

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 是最干净的。我也在想同样的事情,但像往常一样,斯基特先生很快就抽签了:-) 我同意“你越能抽象出“打开/使用/关闭”的概念——这就是 DAAB 在某种程度上所做的。 是的......虽然使用.NET 2.0,但委托语法最终变得非常笨拙。 叹息【参考方案2】:

你可以这样写,并告诉dataReader在使用后关闭连接:

SqlCommand command = new SqlCommand();
command.Connection = openConnection();
using (SqlDataReader dataReader = command.ExecuteReader(CommandBehavior.CloseConnection))

    while (dataReader.Read())
    
        // Do stuff with results
    

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

【讨论】:

【参考方案3】:

您必须自己构建包装器。或者如果可以的话,你可以使用 ORM。

【讨论】:

【参考方案4】:

为什么不看看Enterprise Library DAAB?

这是来自documentation 的代码示例,已针对您的场景进行了调整:

Database db = DatabaseFactory.CreateDatabase();

using (IDataReader reader = db.ExecuteReader(CommandType.Text, "SQL here..." ))

   while (reader.Read())
    
        action(reader);
    

【讨论】:

因为 ADO.NET 适合大多数需求,而且通常没有必要在 [还] 另一个库中。 @Arbiter。我从来没有说过使用,我说看看。它解决了这个问题。真正由保罗来决定是否拥有“另一个库”是一个问题。 DAAB 的设计目标之一是防止开发人员重复编写相同的代码。 同意,决定权在作者 :)【参考方案5】:

使用诸如 Action 之类的委托当然是一种选择,但自 .NET 1.0 以来,我使用了一组类似于下面的重载。调用者使用 using 块来处理返回的读取器,而读取器又会处理连接。

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;
    

【讨论】:

以上是关于使用 SqlDataReader 作为资源的习惯用法的主要内容,如果未能解决你的问题,请参考以下文章

如何在 SqlDataReader 之前知道或获取数组大小?并将其作为 json 返回

C#-使用SqlDataReader读取过程的输出参数[duplicate]

SqlDataReader将Binary列转换为bool

关于SqlDataReader使用的一点疑惑

在foreach循环中声明时的SQLDataReader对象

SqlDataReader生成动态Lambda表达式