Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Return datareader from method

Tags:

c#

asp.net

I have the following 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;
    }

which im calling like this

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

however im getting the error message

Invalid attempt to call HasRows when reader is closed.

can anyone help me out

thanks

like image 287
andrew slaughter Avatar asked Nov 22 '13 14:11

andrew slaughter


Video Answer


2 Answers

Do you really need the reader, or do you just need some way to iterate over the rows inside it? I suggest an iterator block. You can iterate over your rows inside the source method, and yield each row in turn to the caller.

There is a twist with this technique: because you're yielding the same object with each iteration, there are cases where this can cause a problem, and so you're best off also asking for a delegate to copy the contents of the row somewhere. I also like to abstract this to a generic method that can be used for any query, and use the same delegate technique to handle parameter data, like so:

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( ... );
     }
 }
like image 179
Joel Coehoorn Avatar answered Oct 25 '22 03:10

Joel Coehoorn


As seen in https://msdn.microsoft.com/en-us/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;
     }
  }
like image 20
danan Avatar answered Oct 25 '22 04:10

danan