views:

103

answers:

2

Okay, so here's a problem I'm running into.

I have some classes in my application that have methods that require a database connection. I am torn between two different ways to design the classes, both of which are centered around dependency injection:

  1. Provide a property for the connection that is set by the caller prior to method invocation. This has a few drawbacks.

    • Every method relying on the connection property has to validate that property to ensure that it isn't null, it's open and not involved in a transaction if that's going to muck up the operation.

    • If the connection property is unexpectedly closed, all the methods have to either (1.) throw an exception or (2.) coerce it open. Depending on the level of robustness you want, either case is appropriate. (Note that this is different from a connection that is passed to a method in that the reference to the connection exists for the lifetime of the object, not simply for the lifetime of the method invocation. Consequently, the volatility of the connection just seems higher to me.)

    • Providing a Connection property seems (to me, anyway) to scream out for a corresponding Transaction property. This creates additional overhead in the documentation, since you'd have to make it fairly obvious when the transaction was being used, and when it wasn't.

    On the other hand, Microsoft seems to favor the whole set-and-invoke paradigm.

  2. Require the connection to be passed as an argument to the method. This has a few advantages and disadvantages:

    • The parameter list is naturally larger. This is irksome to me, primarily at the point of call.

    • While a connection (and a transaction) must still be validated prior to use, the reference to it exists only for the duration of the method call.

    • The point of call is, however, quite clear. It's very obvious that you must provide the connection, and that the method won't be creating one behind your back automagically.

    • If a method doesn't require a transaction (say a method that only retrieves data from the database), no transaction is required. There's no lack of clarity due to the method signature.

    • If a method requires a transaction, it's very clear due to the method signature. Again, there's no lack of clarity.

    • Because the class does not expose a Connection or a Transaction property, there's no chance of callers trying to drill down through them to their properties and methods, thus enforcing the Law of Demeter.

I know, it's a lot. But on the one hand, there's the Microsoft Way: Provide properties, let the caller set the properties, and then invoke methods. That way, you don't have to create complex constructors or factory methods and the like. Also, avoid methods with lots of arguments.

Then, there's the simple fact that if I expose these two properties on my objects, they'll tend to encourage consumers to use them in nefarious ways. (Not that I'm responsible for that, but still.) But I just don't really want to write crappy code.

If you were in my shoes, what would you do?

A: 

I would prefer the latter method. It sounds like your classes use the database connection as a conduit to the persistence layer. Making the caller pass in the database connection makes it clear that this is the case. If the connection/transaction were represented as a property of the object, then things are not so clear and all of the ownership and lifetime issues come out. Better to avoid them from the start.

D.Shawley
+1  A: 

Here is a third pattern to consider:

  • Create a class called ConnectionScope, which provides access to a connection
  • Any class at any time, can create a ConnectionScope
  • ConnectionScope has a property called Connection, which always returns a valid connection
  • Any (and every) ConnectionScope gives access to the same underlying connection object (within some scope, maybe within the same thread, or process)

You then are free to implement that Connection property however you want, and your classes don't have a property that needs to be set, nor is the connection a parameter, nor do they need to worry about opening or closing connections.

More details:

  • In C#, I'd recommend ConnectionScope implement IDisposable, that way your classes can write code like "using ( var scope = new ConnectionScope() )" and then ConnectionScope can free the connection (if appropriate) when it is destroyed
  • If you can limit yourself to one connection per thread (or process) then you can easily set the connection string in a [thread] static variable in ConnectionScope
  • You can then use reference counting to ensure that your single connection is re-used when its already open and connections are released when no one is using them

Updated: Here is some simplified sample code:

public class ConnectionScope : IDisposable
{
   private static Connection m_Connection;
   private static int m_ReferenceCount;

   public Connection Connection
   {
      get
      {
          return m_Connection;
      }
   }

   public ConnectionScope()
   {
      if ( m_Connection == null )
      {
          m_Connection = OpenConnection();
      }
      m_ReferenceCount++;
   }

   public void Dispose()
   {
      m_ReferenceCount--;
      if ( m_ReferenceCount == 0 )
      {
         m_Connection.Dispose();
         m_Connection = null;
      }
   }
}

Example code of how one (any) of your classes would use it:

using ( var scope = new ConnectionScope() )
{
   scope.Connection.ExecuteCommand( ... )
}
Alex Black
So, if I create another class, I still have to get it into my other classes. All I've really done is wrapped the connection in a container. Now, does the container wrapper get exposed as a property on the object, or passed as a method parameter? It *seems* like we've just obfuscated the problem.
Mike Hofer
@Mike: The point is that you get it into the classes by them *newing* their own instance. Their instance then gets the real connection through a private static property. This avoids having to give every class the same instance.
Alex Black
I am not certain that that would work. In many cases, you *want* to reuse the connection; this is especially the case if the connection is involved in a transaction. Is it not?
Mike Hofer
@Mike: This method ensures the connection is re-used, which you're right you most definitely want. Each ConnectionScope instance returns the *same* underlying connection. The purpose of each ConnectionScope instance is to *declare* that the class needs a connection, and to *scope* the lifetime of its need.
Alex Black
Ah....I got it now. (Must be the caffeine wearing off.) I could likely use a similar pattern for transactions. Hm, that's really good food for thought. ((Wanders off to find a whiteboard...))
Mike Hofer
Cool. Conceptually its similar to the Microsoft class TransactionScope. It works well, because connections are always reused, and only kept around for as long as needed, and none of your classes need to open or close connections, they merely declare through the ConnectionScope that they need one for some scope.
Alex Black
You might want to also consider a factory instead. You could pass a factory instance into your constructor. The only problem that I've encountered with the `ScopedConnection` approach is that I always seem to end up with different scoping rules - sometimes the same connection per thread is good, but other times you need some other policy to select what pool to pick the thread from. The answer is to use an explicit factory or Pool to pick the connection from. Just some more food for thought.
D.Shawley