AppSettings convention for Castle Windsor by Mark Seemann
This post describes a generalized convention for Castle Windsor that handles AppSettings primitives.
In my previous post I explained how Convention over Configuration is the preferred way to use a DI Container. Some readers asked to see some actual convention implementations (although I actually linked to them in the post). In fact, I've previously showcased some simple conventions expressed with Castle Windsor's API.. In this post I'm going to show you another convention, which is completely reusable. Feel free to copy and paste :)
Most conventions are really easy to implement. Actually, sometimes it takes more effort to express the specification than it actually takes to implement it.
This convention deals with Primitive Dependencies. In my original post on the topic I included an AppSettingsConvention class as part of the code listing, but that implementation was hard-coded to only deal with integers. This narrow convention can be generalized:
The AppSettingsConvention should map AppSettings .config values into Primitive Dependencies.
- If a class has a dependency, the name of the dependency is assumed to be the name of the constructor argument (or property, for that matter). If, for example, the name of a constructor argument is top, this is the name of the dependency.
- If there's an appSettings key with the same name in the .config, and if there's a known conversion from string to the type of the dependency, the .config value is converted and used.
Example requirement: int top #
Consider this constructor:
public DbChartReader(int top, string chartConnectionString)
In this case the convention should look after an AppSettings key named top as well as check whether there's a known conversion from string to int (there is). Imagine that the .config file contains this XML fragment:
<appSettings> <add key="top" value="40" /> </appSettings>
The convention should read "40" from the .config file and convert it to an integer and inject 40 into a DbChartReader instance.
Example requirement: Uri catalogTrackBaseUri #
Consider this constructor:
public CatalogApiTrackLinkFactory(Uri catalogTrackBaseUri)
In this case the convention should look after an AppSettings key named catalogTrackBaseUri and check if there's a known conversion from string to Uri. Imagine that the .config file contains this XML fragment:
<appSettings> <add key="catalogTrackBaseUri" value="http://www.ploeh.dk/foo/img/"/> <add key="foo" value="bar"/> <add key="baz" value="42"/> </appSettings>
The convention should read "http://www.ploeh.dk/foo/img/" from the .config file and convert it to a Uri instance.
Implementation #
By now it should be clear what the conventions should do. With Castle Windsor this is easily done by implementing an ISubDependencyResolver. Each method is a one-liner:
public class AppSettingsConvention : ISubDependencyResolver { public bool CanResolve( CreationContext context, ISubDependencyResolver contextHandlerResolver, ComponentModel model, DependencyModel dependency) { return ConfigurationManager.AppSettings.AllKeys .Contains(dependency.DependencyKey) && TypeDescriptor .GetConverter(dependency.TargetType) .CanConvertFrom(typeof(string)); } public object Resolve( CreationContext context, ISubDependencyResolver contextHandlerResolver, ComponentModel model, DependencyModel dependency) { return TypeDescriptor .GetConverter(dependency.TargetType) .ConvertFrom( ConfigurationManager.AppSettings[dependency.DependencyKey]); } }
The ISubDependencyResolver interface is an example of the Tester-Doer pattern. Only if the CanResolve method returns true is the Resolve method invoked.
The CanResolve method performs two checks:
- Is there an AppSettings key in the configuration which is equal to the name of the dependency?
- Is there a known conversion from string to the type of the dependency?
The Resolve method simply reads the .config value and converts it to the appropriate type and returns it.
Adding the convention to an IWindsorContainer instance is easy:
container.Kernel.Resolver.AddSubResolver( new AppSettingsConvention());
Summary #
The AppSettingsConvention is a completely reusable convention for Castle Windsor. With it, Primitive Dependencies are automatically wired the appropriate .config values if they are defined.
Comments
- it would handle all the type conversion for you
- the approach, since the dependency is set up as part of the ComponentModel is statically analysable, whereas ISDR works dynamically so your components that depend on values from config file would show up as "Potentially misconfigured components".