It's not bad but it can be better with a small tweak. (Ok, maybe not real small.)
If your classes depend on a static class and that class depends on AppSettings
then your classes are still coupled to AppSettings
. In other words, there's no other way for them to get their settings. That's a challenge if you want to unit test your class. That means that your unit test project has to have the same
section. But what if you want two tests that use two different values for a setting? It's impossible. Or what if you have a class that needs settings and in a few years you want to use it in an ASP.NET Core application and there is no web.config? Then it won't work at all.
To avoid that you can do this:
public interface IMySettings
{
string Setting1 {get;}
string Setting2 {get;}
}
public class MyConfigurationSettings : IMySettings
{
public string Setting1
{
get { return ConfigurationManager.AppSettings["SomeKey"].ToString(); }
}
public string Setting2
{
get { return ConfigurationManager.AppSettings["SomeOtherKey"].ToString(); }
}
}
Then, in the class that needs the setting:
public class ClassThatNeedsSettings
{
private readonly IMySettings _settings;
public ClassThatNeedsSettings(IMySettings settings)
{
_settings = settings;
}
}
Then, when ever you create an instance of ClassThatNeedsSettings
you pass an instance of a class that implements IMySettings
, and the class will use that to retrieve settings. When your application is running you pass in MyConfigurationSettings
so that your values come from AppSettings
. But ClassThatNeedsSettings
never knows that. It only knows that it's using an instance of IMySettings
.
This is called "dependency injection." ClassThatNeedsSettings
depends on IMySettings
so you're "injecting" it into the constructor. That way ClassThatNeedsSettings
receives what it needs. It's not responsible for creating it.
If you want to unit test, you can "mock" IMySettings
. That is, you can create other classes that implement the interface and use them to pass in whatever values you want to test with. There are even tools like Moq that help you create those classes.
Typically if you use dependency injection you're also going to use a framework like Windsor, Unity, Autofac, or others to manage creating objects for you. It feels a little bit like bait-and-switch introducing that at the very end because it requires more learning and perhaps changing the way an application is configured. But this is why we use it, to prevent one class from having an absolute dependency on another which makes it less flexible and harder to test.