Search code examples
asp.netado.netclassdata-access-layerencapsulation

Asp.Net: Returning a Reader from a Class


I was just wondering about the correct way to return a reader from a class?

My code below works, but I'm unsure if this is correct.

Also. I can't close the the connection in my class method and still access it from my ascx page, is

that OK?

// In my class I have the following method to return the record/reader -- it's a single record in this case.

public SqlDataReader GetPost()
    {
        SqlConnection conn = new SqlConnection(connectionString);
        SqlCommand cmd = new SqlCommand("con_spPost", conn);
        cmd.CommandType = CommandType.StoredProcedure;
        cmd.Parameters.AddWithValue("@blogid", blogid);
        try
        {
            conn.Open();
            return cmd.ExecuteReader();
        }
        finally
        {
          //  conn.Close();
        }
    }

//I then call the GetPost method in my ascx page like so:

protected void Page_Load(object sender, EventArgs e)
{

    //instantiate our class
    MyClass DB = new MyClass();

    //pass in the id of the post we want to view
    DB.PostID = Int32.Parse(Request.QueryString["p"]);

    ///call our GetPost method
    SqlDataReader reader = DB.GetPost();

   //output the result
    reader.Read();
    this.viewpost.InnerHtml = "<span id='post1_CreatedDate'>" + reader["CreatedDate"].ToString() + "</span><br>";
    this.viewpost.InnerHtml += "<span class='blogheads'>" + reader["BlogTitle"].ToString() + "</span><p><p>";
    this.viewpost.InnerHtml += reader["BlogText"].ToString();
    reader.Close();
}

I'd appreciate any comments on my code or tips, thanks.

Melt


Solution

  • Generally speaking it's fine to return a reader from a method, but the reader's consumer needs to take control of all the disposable objects that will be used during the reader's lifetime.

    To do this, you'd pass an IDbConnection into the GetPost method, then make sure your caller disposes both the connection and reader. The using keyword is the most convenient way to do this:

    protected void Page_Load(object sender, EventArgs e) {
    
        // Create the DB, get the id, etc.    
    
        using (IDbConnection connection = new SqlConnection(connectionString))
        using (IDataReader reader = DB.GetPost(connection)) {
            reader.Read();
            this.viewpost.InnerHtml = reader["BlogText"].ToString();
            // finishing doing stuff with the reader  
        }
    }
    

    As others have pointed out, this starts to clutter your application's presentation layer with way too much data-access infrastructure - so it isn't appropriate here. Until you find yourself with a performance problem or need to display an unreasonable amount of data, you shouldn't be handling data readers in your presentation layer. Just make DB.GetPost return a string, and encapsulate all the data-access code in there.