I like Castle, a lot. The thing that I like about it is not only its clean api&design, but also the extensibility points it has.
Sometimes, there are cases when you need to resolve a type that is not registered in the container. Such examples for those cases may be an array of all services of a specific type. An example to this can be found at hammet’s post
This post will be an answer to a question raised in the Castle Project User List.
The question is that he needs his service to be aware of his Id registered in windsor. This is possible with the use of ISubDependencyResolver. Even though this design seems violation of SoC, I believe that it will demonstrate the ISubDependencyResolver well enough.
Tests are first, here they are
public class SampleService1
{
public SampleService1(string serviceId)
{
this.ServiceId = serviceId;
}
public string ServiceId { get; set; }
}
public class SampleService2
{
public SampleService2(string serviceId)
{
this.ServiceId = serviceId;
}
public string ServiceId { get; set; }
}
public class SampleService3
{
public SampleService3(int serviceId)
{
this.ServiceId = serviceId;
}
public int ServiceId { get; set; }
}
public class ServiceIdResolverTests
{
public ServiceIdResolverTests()
{
this.container = new WindsorContainer();
this.container.Kernel.Resolver.AddSubResolver(new ServiceIdResolver());
this.container.Register(Component.For<SampleService1>().Named("service1"))
.Register(Component.For<SampleService2>().Named("service2"))
.Register(Component.For<SampleService3>().Named("service3"));
}
private readonly IWindsorContainer container;
[Fact]
public void CanResolveDependencyWithServiceId()
{
var s1 = container.Resolve<SampleService1>();
var s2 = container.Resolve<SampleService2>();
Assert.Equal("service1", s1.ServiceId);
Assert.Equal("service2", s2.ServiceId);
Assert.Throws<HandlerException>(() => container.Resolve<SampleService3>());
}
}
Now comes to the implementation.
ISubDependencyResolver has 2 simple methods, namely CanResolve and Resolve. CanResolve tells the container that this resolver may handle the operation of resolving and resolve does the real work.
We should define a convention here, about when to resolve a dependency with the service’s id. My convention is if the parameter name is “serviceid”, then this means that the service wants to know about his id.
public class ServiceIdResolver : ISubDependencyResolver
{
#region ISubDependencyResolver Members
public bool CanResolve(CreationContext context, ISubDependencyResolver parentResolver,
ComponentModel model, DependencyModel dependency)
{
return dependency.DependencyKey.ToLowerInvariant().Equals("serviceid") &&
dependency.TargetType == typeof(string);
}
public object Resolve(CreationContext context, ISubDependencyResolver parentResolver,
ComponentModel model, DependencyModel dependency)
{
return model.Name;
}
#endregion
}
In CanResolve method, we tell the container that we are able to resolve a dependency only when it is service id, and in the Resolve method we return that dependency to be the service id.
There are still missing points in the code, we should also check if this parameter is defined somewhere in configuration. If that is the case, we shouldn’t resolve it on our own, but leave the container handles this.
Very easy isn’t it?
6f445791-efad-48f7-b7d4-604e2eb2830c|0|.0
castle, windsor, microkernel, extensibility