Inject Sql Connection Dependency?

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

I have a class as such,

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

This is a simplified view of what a class really looks like, but essentially what it is.

Now I have a new requirement. I need to be able to change the connection string either during class initialization or in a method. For example.

 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 do this using the DI framework of your choice?

+6
c # sql dependency-injection
source share
6 answers

Simply, you do not need a framework. Just an overloaded constructor for your GeoData class.

 GeoData geo = new GeoData(yourConnString); 

A string is your addiction. Since this is not a complex type, you have an dependency injection right there.

DI is not rocket science, although some may like it when you believe it.

+5
source share

Technically, Wim Hollebrandse already answered your question, but I just wanted to indicate that I would personally do it differently because I don't like going through the connection string every time I create an instance of the class. I understand that you have a default constructor, but I think we could make it a little cleaner, still.

First, I would create a static class to get your connection, as shown below:

 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 would use it as follows:

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

With this approach, if the connection string changes by default, you only need to change one line of code, and not go to each line that refers to the connection string from your configuration file. However, it provides you with the ability to redefine the connection string if necessary.

Hope this helps ...

+8
source share

The first question you ask yourself is GeoData? In other words, what is the responsibility of the class?

It appears to be part of the Domain Level and, as such, may contain business logic. It can be used ( associated with other classes.

If this is the case, what are the dependencies? One way to determine this is to try to write unit tests that isolate GeoData. If testing requires significant customization, the tested class is either closely related to other classes or has several 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 method GetCountries? Well, first we created a database with known test data ...

It is time consuming and fragile (what if someone updates the data?), And the test will run relatively slowly (it should connect to the database).

Well, we could pass an object that implements IDbConnection into the constructor (constructor injection). Note that dependency injection usually involves passing in interfaces or abstract classes. To verify this, we need to create a fake IDbConnection. We could use the mocking framework . But then we need to create a fake IDbCommand when calling CreateCommand ...

To quote Jeremy Miller (author of StructureMap ) "Too much effort for too little gain." See His article Best and Worst Practices for Object Layouts .

One option is to use the Repository Pattern . You must pass the interface to a specific repository with the GeoData constructor. It would be easy to fake (manually or with a mock library) for testing. A specific repository will handle all data access. It can be combined with ORM to further abstract data access. The connection string will be managed through the ORM or through the repository (preferably in a different dependency or base class).

If it sounds complicated, because it is. You have chosen one of the most difficult cases for Injection Dependency (which, unfortunately, is also one of the most common).

Dependency injection itself is a pretty simple concept. If your class calls a web service, you can put the web service code in a separate class that does nothing, implements an interface, and passes that interface to your original class. DI / IoC container frameworks can make this easier if you have many classes and / or dependencies, but they are optional.

EDIT: To be clear, dependency injection is not the hard part. Sharing data access.

+4
source share

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

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

+1
source share

Martin Fowler has an article on the topic here that explains the various approaches. In a personal note, I prefer interface injection, but it is a matter of taste.

0
source share

What I would do is create a new class to contain the connection string selection logic, and then use it to get the connection string for the GeoData instance:

 public class ConnectionStringManager { public string GeoDataConnectionString { get { return x ? ConfigurationManager.ConnectionString["LIVEGeoDataConnection"]) : ConfigurationManager.ConnectionString["STAGINGGeoDataConnection"]); } } } 

You can then introduce this into a class containing the Do method to set up GeoData instances as follows:

 public class Blah(ConnectionStringManager connManager) { public void Do() { var geoData = new GeoData { ConnectionString = connManager.GeoDataConnectionString }; geoData.GetCountries(); } } 
0
source share

All Articles