Home >

Castle ServiceIdResolver

19. November 2008

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?

kick it on DotNetKicks.com

, , ,

Comments

11/20/2008 4:46:30 AM #
Tuna, great info!

As usual all of this might seem easy, when you know your way around and understand what everything means. I did not have a lot of material to look things up in and not the time to really dive deep into windsor source code to find out, what is supposed to be going on.
I did not know what 'dependency' meant in the context, much less what the 'dependency key' was, (dependency/component)'model' - no idea, ...! I intellisensed around, but did not reach a point, where I could come up something bearing real meaning. Even reading your code now, I do not understand why the resolve method should return the model's name.

So - little code, yes, but easy, I wouldn't bet on it.

In the end I passed in a serviceid (==id) as a parameter and resolved by that, no subresolvers involved, just a service with a IKernel reference. Suboptimal for sure, but easy to understand one year from now.

And I am unsure, if your solution actually can do, what I was asking for at the list - to get something like
IDictionary<string, IOrderRule> ResolveAllWithKey()

But thanks a lot for clearing this up a bit, I wish I would have found something like your example a little while back. It would have helped me a great deal!
11/20/2008 8:28:06 AM #
Hello Jan,

You are right in saying that without diving deep, this solution wasn't apperant and there isn't many info about it. This post's aim was to show how easy things can be once you get famiiar with the tool you are using, not saying "this is really easy, how didn't you find it".

This post was an answer to your first question, I missed the second one. If i can find time and how to do that in castle way, I promise I'll send another post telling about it.
11/21/2008 2:52:42 AM #
Trackback from DotNetKicks.com

Castle ServiceIdResolver
Comments are closed