Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dependency Inject Sql Connection?

Firstly, I'm starting to use StructureMap, but an example in any DI framework will do.

I have a class as so,

public class GeoData
{
   public List<Country> GetCountries()
   {
      IDbConnection con = new SqlConnection(ConfigurationManager.ConnectionString["GeoDataConnection"])    
      //Sql stuff to return countries from a database
   }
}

It's a simplistic view of what the class actually looks like, but essentially, that's it.

Now, I have a new requirement. I need to be able to change connectionstring either on class initialization or on method. E.g.

public void Do()
{
   var geoData = new GeoData();

   if(x)
   {
      geoData.ConnectionString = ConfigurationManager.ConnectionString["LIVEGeoDataConnection"]);
   }
   else
   {
      geoData.ConnectionString = ConfigurationManager.ConnectionString["STAGINGGeoDataConnection"]);
   }

   geoData.GetCountries();
}

Is there a better solution for this using dependency injection? How would you you do this using a DI framework of your choice?

like image 794
Jaimal Chohan Avatar asked Nov 15 '09 15:11

Jaimal Chohan


4 Answers

Technically, Wim Hollebrandse has already answered your question, but I just wanted to point out that I would personally do it another way because I don't like having to pass the connection string in each time I instantiate the class. I realize that you have a default constructor, but I think we could make this a little cleaner, still.

First, I'd create a static class for getting your connection, as follows:

public static class ConnectionFactory
{
    public static IDbConnection GetConnection()
    {
        return GetConnection(ConfigurationManager.ConnectionString["GeoDataConnection"]);
    }

    public static IDbConnection GetConnection(string connectionString)
    {
        return new SqlConnection(connectionString);
    }
}

Then, I'd use it like this:

public class GeoData
{
   public List GetCountries()
   {
      using (IDbConnection con = ConnectionFactory.GetConnection())
      {
        //Sql stuff to return countries from a database
      }
   }
}

With this approach, if the default connection string changes, you need only change one line of code, rather than going to each line that referenced the connection string from your configuration file. It does, however, provide you with the ability to override the connection string, if necessary.

Hope that helps...

like image 112
senfo Avatar answered Oct 11 '22 13:10

senfo


Simple, you don't need a framework. Just have an overloaded constructor for your GeoData class.

GeoData geo = new GeoData(yourConnString);

The string is your dependency. As it is not a complex type, you got your dependency injection right there.

DI is no rocket science, though some may like you to believe that.

like image 32
Wim Avatar answered Oct 11 '22 14:10

Wim


The first question to ask yourself is what is GeoData? In other words, what is the class's responsibility?

It appears to be part of the Domain Layer, and as such might contain business logic. It may be used by (coupled to) other classes.

If that's the case, what are the dependencies? One way of determining this is by attempting to write unit tests that test GeoData in isolation. If testing requires significant setup, the class under test is either tightly coupled to other classes or has multiple responsibilities (low cohesion).

Let's say we change the class so that the constructor takes a connection string parameter. How can we test the public GetCountries method? Well, first we set up a database with known test data...

That's time-consuming and fragile (what if someone updates the data?), and the test will run relatively slowly (it has to connect to the database).

Well, we could pass in an object implementing IDbConnection to the constructor (constructor injection). Note that dependency injection normally involves passing in interfaces or abstract classes. To test it, we'd have to create a fake IDbConnection. We could use an isolation (mocking) framework. But then we'd need it to create a fake IDbCommand when CreateCommand was called...

To quote Jeremy Miller (author of StructureMap) "It’s too much effort for too little gain." See his article Best and Worst Practices for Mock Objects.

One possibility is to use the Repository Pattern. You would pass in an interface to the specific repository to the GeoData constructor. This would be easy to fake (manually or with a mocking library) for testing. The concrete repository would handle all the data access. It could be combined with an ORM framework to further abstract the data access. Connection string management would be done through the ORM or through the repository (preferably in another dependency or a base class).

If this sounds complex it's because it is. You picked one of the most difficult cases for Dependency Injection (which, unfortunately, is also one of the most common).

Dependency Injection by itself is a fairly simple concept. If your class is calling a Web Service, you can place the Web Service code in a separate class that does nothing else, implement an interface, and pass that interface to your original class. DI/IoC container frameworks can make this easier to do if you have a lot of classes and/or dependencies, but they aren't a requirement.

EDIT: Just to be clear, Dependency Injection is not the complex part. Separating the data access is.

like image 38
TrueWill Avatar answered Oct 11 '22 14:10

TrueWill


I'd create a factory that creates an instance of the GeoData class, which, in turn, implements an interface with the Do method (say, IDoCommand).

It's the factory's responsibility to either use a global context to determine which connection string to inject to the GeoData instance (constructor is my preferred technique), or have it in its Create method as an argument.

like image 39
Ron Klein Avatar answered Oct 11 '22 12:10

Ron Klein