2

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?

5 Answers 5

13

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.

Sign up to request clarification or add additional context in comments.

3 Comments

It probably makes sense to open the connection as a side effect in this instance ... (since its being closed as a side effect)
Great, had already included the opening of the connection! Will start refactoring +/- 200 methods...
We need a new word to describe ideas like this.. something along the lines of "superfablutastic"
3

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
}

1 Comment

Aha, so I'm not the only one who formats nested usings like this!
1

You can nest using statements:

using (SqlCommand cmd = CreateSprocCommand("..."))
{
  using (var connection = cmd.Connection)
  {
     connection.Open();
     using (var reader = cmd.ExecuteReader())
     {
         ...
     }
     ...
  }
}

1 Comment

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 ;-)
0

How about:

using (SqlCommand cmd = CreateSprocCommand("whatever"))
{
  cmd.Connection.Open();
  using (var reader = cmd.ExecuteReader())
  {
   //blabla
  }
}

2 Comments

i dont think that disposing the command will dispose the connection
Does cmd.Dispose() call cmd.Connection.Dispose()?
-1

Is this what you mean ?

using (SqlCommand cmd = CreateSprocCommand("SomeSprocName"))
{
    cmd.Connection.Open();
    using (var reader = cmd.ExecuteReader()) 
    {
        //some code looping over the recors
    }
}

1 Comment

Like Rune, this wouldn't close the connection.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.