I use IoC (DI) approach and usually have parameters, which are being read from configuration settings (i.e. connection strings, static values etc) by the lowest layer (DB layer etc). What is the best way to do it?
Read directly in this the lowest layer, i.e.:
string sendGridApiKey = ConfigurationManager.AppSettings["SendGridApiKey"];
It works, but need to add also this key to config file of unit test project. Also, assembly depends on configuration file
Also there is a problem when different implementations of the the lowest layers can require different parameters. I.e. SendMail1 can require SMTP/login/password, but SendMail2 can require only ApiKey, but SendMail1 and SendMail2 should implement the same interface. So, it creates difficulties to use approach #2
Neither approach you've outlined works well - first (read configuration in services) prevents unit testing as you've mentioned, second (pass config from top level) requires knowledge of all possible implementations of each service by top layer.
I like approaches that rely on DI container's knowledge of both configuration storage and types of objects registered for each interface:
pass configuration during registration time - i.e. if container supports registering factory methods such factory method can read configuration and than invoke particular constructor of concrete service
// constructor: publc ConcreteServiceX(int setting1, string setting2)...
container.RegisterFactory<IServiceX>(
container => return new ConcreteServiceX(42, ReadSetting("X"));
register configuration for each of the service in container as class/interface
// constructor: publc ConcreteServiceX(IConcreteServiceXSettings settings)...
container.RegisterType<IService,ConcreteServiceX>();
container.RegisterInstance<IConcreteServiceXSettings>(
new ConcreteServiceXSettings(42, ReadSetting("X"));
Both approaches localize knowledge of configuration system to one place (container configuration) and allow easier unit testing of each service (no dependency on type of configuration storage) as well as higher level objects (no need to know any settings for services).
Note: sample use Unity-like syntax, adopt to container of your choice
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With