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.