繁体   English   中英

从方法返回数据读取器

[英]Return datareader from method

我有以下方法

    public static SqlDataReader MenuDataReader(string url)
    {
        using (SqlConnection con = new SqlConnection(connectionString))
        {
            using (SqlCommand cmd = new SqlCommand("spR_GetChildMenus", con))
            {
                cmd.CommandType = CommandType.StoredProcedure;
                cmd.Parameters.AddWithValue("@PageUrl", url);
                cmd.Parameters.AddWithValue("@MenuId", ParameterDirection.Output);
                cmd.Parameters.AddWithValue("@ParentId", ParameterDirection.Output);
                cmd.Parameters.AddWithValue("@TitleText", ParameterDirection.Output);
                cmd.Parameters.AddWithValue("@ExternalUrl", ParameterDirection.Output);
                cmd.Parameters.AddWithValue("@FullUrl", ParameterDirection.Output);
                cmd.Parameters.AddWithValue("@ChildCount", ParameterDirection.Output);
                con.Open();
                SqlDataReader reader = cmd.ExecuteReader();
                if (reader.HasRows)
                {
                    //return reader;

                    while (reader.Read())
                    {
                        return reader;
                    }

                }
            }
        }
        return null;
    }

我这样称呼

        SqlDataReader reader = MenuDataReader(url);
        if (reader.HasRows)
        {
            while (reader.Read())
            { }}

但是我收到错误消息

阅读器关闭时调用 HasRows 的尝试无效。

谁能帮我吗

谢谢

https://msdn.microsoft.com/zh-cn/library/system.data.sqlclient.sqlcommand(v=vs.110).aspx中所示

  public static SqlDataReader ExecuteReader(String connectionString, String commandText,
      CommandType commandType, params SqlParameter[] parameters) {
     SqlConnection conn = new SqlConnection(connectionString);

     using (SqlCommand cmd = new SqlCommand(commandText, conn)) {
        cmd.CommandType = commandType;
        cmd.Parameters.AddRange(parameters);

        conn.Open();
        // When using CommandBehavior.CloseConnection, the connection will be closed when the 
        // IDataReader is closed.
        SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);

        return reader;
     }
  }

您是否真的需要阅读器,还是只需要某种方法来遍历其中的行? 我建议一个迭代器块 您可以将行遍历源法里面, yield依次调用者的每一行。

这项技术有一个缺点:因为每次迭代都会产生相同的对象,所以在某些情况下这可能会导致问题,因此最好也请委托在某个地方复制该行的内容。 我还喜欢将其抽象为可用于任何查询的通用方法,并使用相同的委托技术来处理参数数据,如下所示:

private IEnumerable<T> GetRows<T>(string sql, Action<SqlParameterCollection> addParameters, Func<IDataRecord, T> copyRow)
{
     using (var cn = new SqlConnection("Connection string here"))
     using (var cmd = new SqlCommand(sql, cn)
     {
         cmd.CommandType = CommandType.StoredProcedure;
         addParameters(cmd.Parameters);
         cn.Open();
         using (var rdr = cmd.ExecuteReader())
         {
             while (rdr.Read())
             {
                 yield return copyRow(rdr);
             }
             rdr.Close();
         }
     }
}

public IEnumerable<MenuItem> GetChildMenus(string url)
{
     return GetRows<MenuItem>("spR_GetChildMenus", p =>
     {
         //these lines are copied from your question, but they're almost certainly wrong
         p.AddWithValue("@PageUrl", url);
         p.AddWithValue("@MenuId", ParameterDirection.Output);
         p.AddWithValue("@ParentId", ParameterDirection.Output);
         p.AddWithValue("@TitleText", ParameterDirection.Output);
         p.AddWithValue("@ExternalUrl", ParameterDirection.Output);
         p.AddWithValue("@FullUrl", ParameterDirection.Output);
         p.AddWithValue("@ChildCount", ParameterDirection.Output);
     }, r =>
     {
         return new MenuItem( ... );
     }
 }

我不会退还阅读器- Dispose您的连接和命令将关闭连接。 相反,我将返回您数据的代表性模型。

当您在using语句内部返回时,代码在SqlConnection上调用Dispose 这将关闭DataReader ,从而导致错误。

Danan's answer一个问题触发,这里是一个基于抽象的解决方案。 此外,它使用良好的实践,如使用声明、异步编程(为简洁起见省略了取消标记)和适当的对象处理(特别是关于连接)。

// Example invocation
public async Task DemonstrateUsage()
{
    var query = @"SELECT * FROM Order WHERE Id = @Id;";

    var parameters = new Dictionary<string, object>()
    {
        ["@Id"] = 1,
    };

    // Caller disposes the reader, which disposes the connection too
    await using var reader = await this.ExecuteReader(this.CreateConnection, query, parameters);

    while (await reader.ReadAsync())
        Console.Log("And another row!");
}

// Concrete implementation of how we produce connections
private DbConnection CreateConnection()
{
    return new SqlConnection("ConnectionString");
}

// Fully abstract solution
private async Task<DbDataReader> ExecuteReader(Func<DbConnection> connectionFactory,
    string query, IReadOnlyDictionary<string, object> parameters,
    CommandType commandType = CommandType.Text)
{
    var connection = connectionFactory();

    try
    {
        await using var command = connection.CreateCommand();

        command.CommandType = commandType;
        command.CommandText = query;
        foreach (var pair in parameters)
        {
            var parameter = command.CreateParameter();
            parameter.ParameterName = pair.Key;
            parameter.Value = pair.Value;
            command.Parameters.Add(parameter);
        }

        await connection.OpenAsync();
        return await command.ExecuteReaderAsync(CommandBehavior.CloseConnection);
    }
    catch
    {
        // We have failed to return a disposable reader that can close the connection
        // We must clean up by ourselves
        await connection.DisposeAsync();

        throw;
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM