A ConnectionScope class.
I’ve heard a few comments from people who would like an easier way to manage connection lifetime & use across multiple methods. Most often, the problem is due to using a TransactionScope at an higher level, but opening and closing connections inside the methods – generally resulting in a distributed transaction unless you manually move a single connection around. For example:
void OuterMethod() {
using (TransactionScope tx = new TransactionScope(TransactionScopeOption.RequiresNew, opts)) {
InnerMethod("select * from testtable");
InnerMethod("update testtable set col1 = N'new value'");
tx.Complete();
}
}
static void InnerMethod(string sqlText) {
using (SqlConnection conn = SqlConnection(connStr)) {
conn.Open();
SqlCommand cmd = conn.CreateCommand();
cmd.ExecuteNonQuery();
}
}
To avoid the distributed transaction, you would need to create the connection in the OuterMethod and pass it in as a parameter (somewhat tedious) or assign it to a member variable (somewhat risky, as you are then probably maintaining a reference to the connection beyond it’s intended lifetime).
I've implemented a simple scope class for db connections that can simplify the process (see the attached file). Feel free to use this class directly or modify it as needed.
To use it, simply create a new DbConnectionScope in the OuterMethod and follow one of the two patterns for getting your connection to the inner scope:
- Create, open and place your connection into the scope prior to use with AddConnection (generally the OuterMethod), assigning it a key for identification. In the InnerMethod, pull it out using GetConnection and assign it to your command before executing.
- Use GetOpenConnection() in the InnerMethod and the scope will construct & open your connection as needed, using the connection string as the key.
The example, re-written using the second pattern, looks like this:
void OuterMethod() {
using (TransactionScope tx = new TransactionScope(TransactionScopeOption.RequiresNew, opts)) {
using (DbConnectionScope db = new DbConnectionScope()) {
InnerMethod("select * from testtable");
InnerMethod("update testtable set col1 = N'new value'");
tx.Complete();
}
}
}
static void InnerMethod(string sqlText) {
SqlCommand cmd = new SqlCommand();
cmd.Connection = (SqlConnection) DbConnectionScope.Current.GetOpenConnection(SqlClientFactory.Instance, connStr);
cmd.ExecuteNonQuery();
}
This class is only something you’d want to use only if you specifically want to re-use the same open connection – the connection pool does a much better job of handling connection re-use when your logic allows for the connection being reset, and you don't need any particular state associated with it. You also need to keep in mind the problems that using the same connection can cause – for example, only one command executing at a time if MARS is not on.