In the last post I gave a solution to make possible to specify the default component in a dependency resolution with castle windsor. That solution is good for automatic dependency resolution, with respect to the previous post if you have a IPageDownloader that has a property of type ICache, if you do not set that property in castle configuration, at resolution time the Cache property of the PageDownloader will be the one marked with default=”true” if any.

All that stuff works, but if you ask directly for an ICache component with

IoC.Resolve<ICache>()

You still get the first component defined in the configuration file. Sometimes you want that all resolution will honor the new default attribute, to accomplish this you have to change the facility.

public class DefaultComponent : AbstractFacility { protected override void Init() { Kernel.ComponentRegistered += OnComponentRegistered; Kernel.AddSubSystem(SubSystemConstants.NamingKey, new NamingSubsystemForDefault(defaults)); } private Dictionary<Type, IHandler> defaults = new Dictionary<Type, IHandler>(); private void OnComponentRegistered(string key, IHandler handler) { if (handler.ComponentModel.Configuration != null && handler.ComponentModel.Configuration.Attributes["default"] == "true") { defaults.Add(handler.ComponentModel.Service, handler); } } }

The facility is quite the same as before, first of all the dictionary use type as key and IHandler as value, this permits me to store the default handler for each service that has one of the component marked with default=”true”, then I add a SubSystem to the Kernel. SubSystems can be of different types, so you have a SubSystemConstants that you can use to specify witch subsystem you want to add. In this example I add a NamingKey, a subsystem that is used by the kernel to get the right IHandler based on user request. Here is the SubSystem

1 internal class NamingSubsystemForDefault : DefaultNamingSubSystem 2 { 3 private Dictionary<Type, IHandler> defaults = new Dictionary<Type, IHandler>(); 4 5 public NamingSubsystemForDefault(Dictionary<Type, IHandler> defaults) 6 { 7 this.defaults = defaults; 8 } 9 10 public override IHandler GetHandler(Type service) 11 { 12 if (defaults.ContainsKey(service)) 13 return defaults[service]; 14 15 return base.GetHandler(service); 16 } 17 }

A Naming SubSystem can really be a big stuff to build, but I need only to make a little customization, so I use the DefaultNamingSubSystem as base class, so the default behaviour is the standard of the Kernel. The only function I need to override is the GetHandler(Type) that is called when the user ask to resolve a type without specifying any id, this means that this function gets called when the user want the default component for that service. The only thing you need to do is to check if you have a default for that service, and if one is found simply return the handler to the caller. Voilà!! the game is done.

Alk.

Tags:

 

One Response to “Again on castle windsor and default component”

  1. Thanks, a big help!

    We had a situation where the interface type was declared twice in the container, once for WCF client proxies and once once for the WCF service implementations. We wanted to enforce that the client proxy is the default implementation returned, and were hoping that the order of declaration in the Windsor config would enforce this, but it seems that this is not guaranteed when dependencies in the object graph come into play.

    Your technique now ensures that we can always specify which implementation is resolved by default. This is even more important during injection, since it is obviously not possible there to disambiguate by supplying a key as you would in a direct call to Resolve(string key).

    Thanks again.
    Craig.