views:

264

answers:

5

I have a dal layer with lots of methods, all of them call stored procedures, some return lists (so with a use of SqlDataReader), others only a specific value.

I have a helper method that creates the SqlCommand:

    protected SqlCommand CreateSprocCommand(string name, bool includeReturn, SqlDbType returnType)
    {
        SqlConnection con = new SqlConnection(this.ConnectionString);
        SqlCommand com = new SqlCommand(name, con);
        com.CommandType = System.Data.CommandType.StoredProcedure;

        if (includeReturn)
            com.Parameters.Add("ReturnValue", returnType).Direction = ParameterDirection.ReturnValue;

        return com;
    }

Now my average (overly simplified) method body look like:

SqlCommand cmd = CreateSprocCommand("SomeSprocName"); //an override of the above mentioned method
try {
   cmd.Connection.Open();
   using (var reader = cmd.ExecuteReader()) {
       //some code looping over the recors
   }
   //some more code to return whatever needs to be returned
}
finally {
   cmd.Connection.Dispose();
}

Is there a way to refactor this, so that I won't lose my helper function (it does quite a bit of otherwise repetitive work), and yet be able to use using?

A: 

How about:

using (SqlCommand cmd = CreateSprocCommand("whatever"))
{
  cmd.Connection.Open();
  using (var reader = cmd.ExecuteReader())
  {
   //blabla
  }
}
Rune Grimstad
i dont think that disposing the command will dispose the connection
Sam Saffron
sambo99 is right, i checked that in Reflector
Gidon
Does cmd.Dispose() call cmd.Connection.Dispose()?
Douglas Leeder
+1  A: 

You can nest using statements:

using (SqlCommand cmd = CreateSprocCommand("..."))
{
  using (var connection = cmd.Connection)
  {
     connection.Open();
     using (var reader = cmd.ExecuteReader())
     {
         ...
     }
     ...
  }
}
1800 INFORMATION
I usually omit the block braces from the outer levels and indent them to the same level to communicate that I intend all the disposables formatted that way to be used together. I haven't quite decided yet whether it's a bad idea or a really bad idea ;-)
Rytmis
A: 

Is this what you mean ?

using (SqlCommand cmd = CreateSprocCommand("SomeSprocName"))
{
    cmd.Connection.Open();
    using (var reader = cmd.ExecuteReader()) 
    {
        //some code looping over the recors
    }
}
Eoin Campbell
Like Rune, this wouldn't close the connection.
Gidon
+10  A: 

One way is to change it from returning a command to taking a delegate which uses the command:

protected void ExecuteSproc(string name,
                            SqlDbType? returnType,
                            Action<SqlCommand> action)
{
    using (SqlConnection con = new SqlConnection(this.ConnectionString))
    using (SqlCommand com = new SqlCommand(name, con))
    {
        con.Open();
        com.CommandType = System.Data.CommandType.StoredProcedure;

        if (returnType != null)
        {
            com.Parameters.Add("ReturnValue", returnType.Value).Direction = 
                ParameterDirection.ReturnValue;
        }
        action(com);
    }
}

(Note that I've also removed the includeReturn parameter and made returnType nullable instead. Just pass null for "no return value".)

You'd use this with a lambda expression (or anonymous method):

ExecuteSproc("SomeName", SqlDbType.DateTime, cmd =>
{
    // Do what you want with the command (cmd) here
});

That way the disposal is in the same place as the creation, and the caller just doesn't need to worry about it. I'm becoming quite a fan of this pattern - it's a lot cleaner now that we've got lambda expressions.

Jon Skeet
Really nice! :-)
Rune Grimstad
It probably makes sense to open the connection as a side effect in this instance ... (since its being closed as a side effect)
Sam Saffron
@sambo99: Yup, will edit.
Jon Skeet
Great, had already included the opening of the connection! Will start refactoring +/- 200 methods...
Gidon
+3  A: 

You can do this:

protected static SqlCommand CreateSprocCommand(SqlConnection con, string name, bool includeReturn, SqlDbType returnType)
{
    SqlCommand com = new SqlCommand(name, con);
    com.CommandType = System.Data.CommandType.StoredProcedure;

    if (includeReturn)
        com.Parameters.Add("ReturnValue", returnType).Direction = ParameterDirection.ReturnValue;

    return com;
}

and call it like this:

using (SqlConnection con = new SqlConnection(this.ConnectionString))
using (SqlCommand cmd = CreateSprocCommand(con, "SomeSprocName", true, SqlDbType.Int)
{
    cmd.Connection.Open();
    using (var reader = cmd.ExecuteReader())
    {
        //some code looping over the recors
    }
    //some more code to return whatever needs to be returned
}
Rashmi Pandit
Aha, so I'm not the only one who formats nested usings like this!
Rytmis