Tigraine

Daniel Hoelbling-Inzko talks about programming

Component Lifestyles and Fluent Interface for Pandora

Posted by Daniel Hölbling on June 5, 2009

I just finished refactoring Pandora to have a much cleaner configuration and also to enable lifestyles for certain components.

Until now, Pandora was not able to save one service after it’s initial activation. Every call to container.Resolve() would instantiate a new service with new dependencies etc. It may be of interest to some of you that this is the exact opposite of the default lifestyle Windsor sets for it’s components. So I obviously wanted to change that.

[Fact]
public void ActivationHappensOnlyOnceForSingletonComponents()
{
    var store = new ComponentStore();
    var registration = store.Add<IService, ClassWithNoDependencies>("test");
    registration.Lifestyle = ComponentLifestyles.Singleton;

    var container = new PandoraContainer(store);

    var service = container.Resolve<IService>("test");     var service2 = container.Resolve<IService>("test");

    Assert.Same(service, service2); }

All “logic” concerning a lifestyle is completely enclosed in the Lifestyle classes so creating a new lifestyle for Pandora should be rather simple in the future.

But the real big news (and the real big change) is the changed configuration.
At first I tried to bake a fluent interface into the Registration (the class Pandora uses to represent one registered service) to allow nice parameter syntax.

The idea was good, but it led to some problems with the interface and also made it much harder to consume those registrations inside the container.

I then decided to rip everything out and revert the registration to a dumb value type that only holds information that can be easily serialized/deserialized if needed.
So that the fluent interface would generate a value type, instead of trying to be one with all it’s faults. Doing so opened up a whole lot of new possibilities in terms of API for me, and so I ended up with a quite pleasant interface like this:

store.Register(p => p.Service<IService>("db.service")
                        .Implementor<ClassWithNoDependencies>()
                        .Parameters("hello").Set("world")
                        .Lifestyle.Transient()
                        .Parameters("foo").Set("bar"));

As before, this looks intentionally familiar to Windsor users, since I believe Windsor’s interface is really good and makes a lot of sense when reading.
What I improved upon (at least, I believe so) was to get away from the static Component class Windsor uses to bootstrap the Fluent registration, but to use a closure that provides Intellisense right from the beginning.

Meaning, in Windsor there is no Intellisense when you Write: container.Register() .. From the signature you can only see that you need an array of IRegistration while nobody tells you that you need to use that static class that’s buried down in Castle.MicroKernel.Registration, while writing p. instantly brings up all registration options in Pandora.

Next in line is improving the fluent interface even further to allow for auto-configuration (eg. take Assembly A and register all Types in there).

Another challenge I want to tackle with Pandora is externalizing the configuration. Fluent interfaces are awesome for developers (since they allow easy refactoring), but the real power of a DI container also comes from the ability to change the configuration without recompiling the app. Usually all containers solve this through XML, so I’d like to try a new approach here and am currently thinking about making Pandora read the configuration from a IronPython script. That would allow me to consume the Fluent Interface without recompiling the application and paying the XML bracket tax while retaining the flexibility of just opening up a text file to change my configuration.

As usually, you can find the source to Pandora at the project website on Bitbucket.

comments powered by Disqus

My Photography business

Projects

dynamic css for .NET

Archives

more