Question

I'm not sure even if nested SqlConnections are possible but I would really like to stay away from it. I've run into a scope issue now in my code and I'm trying to figure out how to get around it.

So until recently I had a global SqlConnection which is opened when the application starts and closed when it finished. I've now discovered the concept of .NET's connection pool so I've changed my code such that every SqlCommand (or group of them) uses its own, freshly created and opened, SqlConnection, trusting .NET to manage the pool and the accompanying overhead.

The problem I'm now having is that I have several blocks of code that look something like this:

using (SqlConnection sqlConnection = new SqlConnection(ClassGlobal.ConnectionString))
{       
    sqlConnection.Open();
    using (SqlCommand sqlCommand1 = new SqlCommand("SQL code here", sqlConnection))
    {
        sqlCommand1.ExecuteNonQuery();
    }
    using (SqlCommand sqlCommand2 = new SqlCommand("SQL code here", sqlConnection))
    {
        sqlCommand2.ExecuteNonQuery();
    }
    .
    .
    .
    ClassGlobal.WriteAction(action);
}

while the ClassGlobal.WriteAction() function looks something like this:

public static void WriteAction(MyActionClass action)
{
    using (SqlConnection sqlConnection = new SqlConnection(ClassGlobal.ConnectionString))
    {       
        sqlConnection.Open();
        using (SqlCommand sqlCommand = new SqlCommand("Write the action to the DB", sqlConnection))
        {
            sqlCommand.ExecuteNonQuery();
        }
    }
}

As you can see, a new SqlConnection is created inside WriteAction() which is called from inside the scope of the first SqlConnection. Not good! I'm trying to avoid this.

In the past it wouldn't have been a problem seeing as there were none of these using (SqlConnection) blocks and all SqlCommands pointed to the same (global) SqlConnection. Obviously I could simply move my call to WriteAction() down below the closing curly brace of the using (SqlCommand) but:

  1. The action instance that I pass to it is often instantiated and populated while inside the scope of the SqlConnection so I'd have to make more changes (lots of them) to move those out of the SqlConnection scope. There are loads of them and it will be hairy.
  2. I'd actually prefer if the WriteAction() call could be inside the SqlConnection scope as in the example above so that I can wrap all of it in a TransactionScope, something that wasn't possible thus far but certainly seems like a good idea.

So here's what I plan on doing but I'd like to hear if any of you would consider this not to be good practice or whether you can suggest a better approach. (I've recently discovered that my global SqlConnection was not a good practice and it resulted in lots of time spent on fixing it. I'd like to avoid such a discovery in future).

How about I add a parameter to the WriteAction() function so that it looks as follows:

public static void WriteAction(MyActionClass action, SqlConnection sqlConnection)
{   
    using (SqlCommand sqlCommand = new SqlCommand("Write the action to the DB", sqlConnection))
    {
        sqlCommand.ExecuteNonQuery();
    }
}

This means that, rather than moving the calls to WriteAction() outside of the SqlConnection scope, I can simply add the SqlConnection as a parameter to the function so that the SQLCommand inside the function make use of the same connection, even if that connection is enlisted to a TransactionScope.

For the few instances where I call WriteAction() from outside the scope of any SqlConnection, I can write an overloaded function that looks like this:

public static void WriteAction(MyActionClass action)
{
    using (TransactionScope transactionScope = new TransactionScope())
    {
        using (SqlConnection sqlConnection = new SqlConnection(ClassGlobal.ConnectionString))
        {
            sqlConnection.Open();
            WriteAction(action, sqlConnection);
        }
        transactionScope.Complete();
    }
}

Does this look like a good idea or am I going to rue this decision in another two years?

Was it helpful?

Solution

Passing the SqlConnection instance to the method is absolutely fine. However, i'm not sure if you last approach, the overloaded method without the SqlConnection is a good idea. It hides the fact that you should better use the other, new overload. It makes the code compile which prevents you from fixing code which should be fixed now.

Note that the using block is not the problem but an opened connection. As long as you don't open the connection, the connection-pool does not need to open a physical connection.

So it is also a viable option to close the connection before you call WriteAction:

using (SqlConnection sqlConnection = new SqlConnection(ClassGlobal.ConnectionString))
{       
    sqlConnection.Open();
    using (SqlCommand sqlCommand1 = new SqlCommand("SQL code here", sqlConnection))
    {
        sqlCommand1.ExecuteNonQuery();
    }
    using (SqlCommand sqlCommand2 = new SqlCommand("SQL code here", sqlConnection))
    {
        sqlCommand2.ExecuteNonQuery();
    }
    // ...
    sqlConnection.Close();

    ClassGlobal.WriteAction(action);

    // ... perhaps open it again here
}

From MSDN:

Whenever a user calls Open on a connection, the pooler looks for an available connection in the pool. If a pooled connection is available, it returns it to the caller instead of opening a new connection. When the application calls Close on the connection, the pooler returns it to the pooled set of active connections instead of closing it. Once the connection is returned to the pool, it is ready to be reused on the next Open call.

So you can see that the nested using in WriteAction is not a problem so long as you don't keep the outer connection open. Don't confuse the connection instance with a physical connection.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top