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?
If both answers are true, then the CanResolve method returns true.

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

Actually IComponentModelContributor would be an even better place to put the logic than ISDR.
- 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".
2012-11-07 21:19 UTC
Krzysztof, if I try to implement an interface called "IComponentModelContributor" IntelliSense gives me nothing. Where is that interface defined? (I'm drawing a blank on Google too...)
2012-11-07 21:29 UTC
a@http://docs.castleproject.org/Windsor.ComponentModel-construction-contributors.ashx@this thing
2012-11-07 21:31 UTC
How would you implement the above convention with that interface?
2012-11-07 21:49 UTC
I guess that means a blogpost :)
2012-11-07 21:51 UTC


Wish to comment?

You can add a comment to this post by sending me a pull request. Alternatively, you can discuss this post on Twitter or somewhere else with a permalink. Ping me with the link, and I may respond.

Published

Wednesday, 07 November 2012 16:54:43 UTC

Tags



"Our team wholeheartedly endorses Mark. His expert service provides tremendous value."
Hire me!
Published: Wednesday, 07 November 2012 16:54:43 UTC