Tigraine

Daniel Hoelbling-Inzko talks about programming

Pandora used in dotless and moved to GitHub

Pandora, my personal IoC Container (mostly written for educational purposes) has recently been integrated into dotless. This has helped us improve the design of dotless without having to take on a big dependency like Windsor or StructureMap.

I chose to implement Pandora through the Common Service Locator interface by Microsoft, so if we ever feel restricted by Pandora we can easily switch to a more potent container without touching the actual dotless code.

This step also made me bring the Pandora repository from mercurial to git with some help from Horst. He was kind enough to run hg-fast-import for me.

Pandora can now be found on GitHub with a similar build process as dotless and elms-connector.

Pandora on GitHub

Pandora now likes concrete classes

While getting into WPF and the MVVM pattern (you should check out this Webcast by Jason Dolinger if you need a tutorial to get started on MVVM) I found myself somehow dealing more with concrete classes then I was used to. Usually I hide everything behind some interface and try to think really hard before violating that habit. Nonetheless, ViewModel classes are just that, ViewModel classes very special to their View and you really can’t hide them behind a meaningful Interface (without looking too silly).

So, after writing a fairly small application with only 3 ViewModel classes I found myself writing this utterly meaningless block of code:

container.Register(p =>
{
    p.Service<PersonViewModel>()
        .Implementor<PersonViewModel>();
    p.Service<AddressesViewModel>()
        .Implementor<AddressesViewModel>();
    p.Service<ProductViewModel>()
        .Implementor<ProductViewModel>();
});

It’s not completely meaningless after all, everything that keeps me from writing new PersonViewModel() somewhere in my code is just awesome. Only, it’s a bit too much of ceremony for something that simple. By asking for a concrete type you already tell Pandora everything it needs to know.

So, now Pandora can resolve concrete types directly if there is no registration that says otherwise. If you ask for a concrete class A, you’ll get a concrete class A even if the container is totally blank.
Now, if a class A is registered with the container it will use that one, if not Pandora will try to figure it out.

That also leaves open one way of extending your system. You can start off with an implicitly registered concrete class A, and if you want to subclass it later to modify behavior you only register the new subclass B as implementor for service A with Pandora and you’re set.

So, quick recap on how this works:

When a concrete type is requested and can’t be found in the configuration it will be instantiated if:

  1. It’s a concrete type
  2. You are not resolving to a name
  3. The dependencies of the requested type can be satisfied.

Also keep in mind that the default lifestyle for Pandora is singleton, so once you request A, you’ll always get the same instance back as long as you don’t change that through an explicit registration.

As always you can grab the source from the BitBucket site. There is no new binary release (I only moved up a minor version), but compiling Pandora is a pretty straightforward process as long as you have Visual Studio installed.

Ps: I was amazed how easy it was to implement this. Basically all I did was write a wrapper around the ComponentLookup service to implement the new functionality. It’s all in one place :).

Pandora release 0.1

I finally decided it’s time to release Pandora. The current build is stable and meets all of my current requirements, so I feel the best way to advance Pandora is to dogfood it on another project.

Also, this gives me a bit of an opportunity to catch up on the documentation side of things and work out bugs that may be there.

Upcoming features

I still have a pretty interesting wish list (Generics, Factory Methods, AutoConfiguration), but those features are all rather non-trivial and I feel Pandora should stay a simple and minimalistic container right now.

Where to get

You can download the compiled Pandora release from Bitbucket or you can grab the source and compile it for yourself.

Compiling Pandora

Just run the build.bat script and NAnt (supplied within the repository) will fire up and compile everything into the build\ directory. By default the script will also compile and run the accompanying unit tests, leaving you with a Pandora.Test-Result.htm file you can then review if you please.

To compile without tests simply run “build compile”, and you’ll end up only with Pandora.dll/pdb and the common service locator library.

Using Pandora

If you didn’t run build compile, you end up with lots of stuff in your build directory:

Pandora result

If you plan on using Pandora you only need the following:

image 

If you are interested in the ServiceLocation.dll you can read about it here: Common Service Locator Adapter for Pandora

RFC: Is a supplied factory method useful for an IoC container?

I need your help.
Does it make sense to have something like this in a DI container?

[Fact]
public void CanSupplyFactoryMethod()
{
    store.Register(
        p => p.Service<IService>()
                 .Factory(s => 
                     {
                         Console.WriteLine("Creating type..");
                         if (SOME_STATIC_VAR == true)
                         {
                             return new ClassWithNoDependencies();
                         }
                         return new OtherClass();
                     })
        );
    var service = container.Resolve<IService>();
    Assert.IsType<ClassWithNoDependencies>(service);
}

The idea being, you can supply a function delegate (Func<IPandoraContainer, T>) to be executed when the service should be instantiated. This would make a rather interesting extensibility story, since I’d avoid having to build in all kinds of hard to find hooks to allow modification of the object creation.

Also, I’d like this delegate to be evaluated during runtime. This could enable me to resolve to another object if for example a network link is down etc.

Another obvious use for this would be to access classes that are present in the .NET BCL but can’t be instantiated and would otherwise be supplied to the container through .Instance like the HttpContext:

store.Register(
    p => p.Service<HttpContext>()
        .Factory(s => { return HttpContext.Current; }));

What do you think? Should this make it into Pandora? What do you want to see in a DI container? Please feel free to comment.

Generic Registrations for Pandora

Yesterday I finished expanding the Pandora wiki a bit on how to configure the container and while doing so I made one discovery:

I never tested if Pandora can handle generic types!!

Wow, why didn’t I think about that. One of the most important scenarios fully untested. Thank god, simple generic registration already worked:

[Fact]
public void CanResolveSpecificGenericClass()
{
    store.Register(p => 
        p.Service<GenericClass<string>>()
        .Implementor<GenericClass<string>>());

    Assert.DoesNotThrow(() => container.Resolve<GenericClass<string>>()); }

The moment you define what generic type you are using, a generic class is just a regular class that can be used like all the others.

Still, it sucks having to specify one class explicitly 5 times if I want 5 to use it with 5 types. So I set out to support the following scenario:

[Fact(Skip = "Not implemented yet")]
public void CanRegisterAndResolveRealGenericRequests()
{
    store.Register(p => 
        p.Generic(typeof(GenericClass<>))
        .Implementor(typeof(GenericClass<>))
        .ForAllTypes());

    Assert.DoesNotThrow(() => {         var resolve = container.Resolve<GenericClass<string>>();     }); }

I want the container to just know the generic and then figure out if the generic registration can satisfy my requested service. Since this would obviously require Reflection in the resolving part of Pandora, I shelved that feature for a maybe more useful one that at least eases some of the generic pain:

store.Register(
    p => p.Generic(typeof (GenericClass<>))
             .Implementor(typeof (GenericClass<>))
             .OnlyForTypes(typeof (string), typeof (int)));

This way I can specify in one registration what types I want the generic to serve and the Fluent interface will create a distinct registration for each type in .OnlyForTypes[]. This way I don’t need any reflection code in the resolving part of Pandora.
And: It’s already implemented.

You can find the source to Pandora at the project website on Bitbucket.

Injecting instances into Pandora

There are times when your services depend on an object you can’t construct yourself. One obvious example being the HttpContext in most ASP.NET applications.

So all DI containers have some way to inject an instance into the container to cover that. Pandora has joined them yesterday. It’s baked into the fluent interface and the ComponentStore:

Fluent:

var store = new ComponentStore();
            var instance = new ClassWithNoDependencies();
            store.Register(p => p.Service<IService>("test")
                                    .Instance(instance));

Conventional:

var store = new ComponentStore();
            var instance = new ClassWithNoDependencies();
            store.AddInstance<IService>(instance);

Component Lifestyles and Fluent Interface for Pandora

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.

Common Service Locator adapter for Pandora

In case you haven’t heard. Some time ago Microsoft sat with most authors of leading IoC containers and defined a common interface for Dependency Injection / Service Location. You can get all the relevant information about the Common Service Locator through reading Glenn Block, Ayende Rahien or Chris Travares and you can download it from it’s Codeplex site. There is a comprehensive API reference in their wiki too.

Ayende sums the purpose of the CSL up pretty well:

The idea is based off a post that Jeremy Miller had about a month ago, having a common, shared interface across several IoC implementation. That would allow library authors to make use of the benefits of a container without taking a dependency on a particular implementation.

The alternative for that is to each library to create its own abstraction layer. A good example of that is NServiceBus' IBuilder interface, or ASP.NET MVC's IControllerFactory. That is just annoying, especially if you are integrating more than a single such framework.

In Pandora I aimed for CSL compliance from the beginning, and the CSLAdapter class has been there for quite some time now. But since Pandora only recently got the named lookups feature I never finished it.

Until now. Today I finally implemented the CommonServiceLocatorAdapter that (for me) somehow “verifies” that Pandora supports most mandatory lookup features needed from a DI container.

On the implementation side: I first tried to implement IServiceLocator directly, until I discovered the abstract ServiceLocatorImplBase class that wraps all of the overloads and generic/nongeneric stuff, saving you a bunch of boilerplate code.

Coming up next in Pandora will be component lifestyles, forwards and then maybe DLR configuration.

Named dependency lookup &ndash; Pandora

One rather essential feature for a DI container is to be able to lookup components by some key. For example retrieving a specific IController class based on the controller name specified by the request.

So the usual use case for the above looks like this:

var store = new ComponentStore();
store.Add<IRepository, MemoryRepository>("memory.repository");
store.Add<IRepository, SqlRepository>("db.repository");
var container = new PandoraContainer(store);

var service = container.Resolve<IRepository>("db.repository");

Now, there is another thing too: What if I want one controller to use one special repository out of the registered ones:

var store = new ComponentStore();
store.Add<IRepository, SqlRepository>("db.repository");
store.Add<IRepository, MemoryRepository>("memory.repository");
store.Add<Controller, Controller>()
    .Parameters("repository").Eq("memory.repository");
var container = new PandoraContainer(store);

var controller = container.Resolve<Controller>();

Well, yes Pandora can do those kinds of things too.

If you know Windsor’s Fluent interface .Parameters().Eq() may sound familiar to you. That’s intentionally. I like the Windsor Fluent syntax.

What I really underestimated was how to do the fluent interface. It’s not terribly hard, but it’s takes some tinkering ;).

You can find the source to Pandora at the project website on Bitbucket.

Dependency chain lookups with Pandora

Almost a week ago I introduced Pandora my own take on Inversion of Control. So I’m back to report some news.

New features:

  1. Multiple registrations of one service type
  2. Graph splitting
  3. Dependency chains of own service

Before only one service registration was possible, preventing you from doing all sorts of cool things.
For example it prevented you from registering a class like this one:

public class ClassWithDependencyOnItsOwnService : IService
{
    public ClassWithDependencyOnItsOwnService(IService service)
    {
    }
}

This class would typically be some sort of decorator that covers the IService interface with a thin layer of concerns (like logging/errorhandling/caching). Now, you can just register more than one IService and it will walk the graph for you:

[Fact]
public void CanResolveDependencyChainOfSameService()
{
    var store = new ComponentStore();
    store.Add<IService, ClassWithDependencyOnItsOwnService>();
    store.Add<IService, ClassWithNoDependencies>();

    var container = new PandoraContainer(store);

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

    Assert.IsType(typeof (ClassWithDependencyOnItsOwnService), service);     var ownService = (ClassWithDependencyOnItsOwnService)service;     Assert.IsType(typeof (ClassWithNoDependencies), ownService.SubService); }

Also now there is the possibility to split the graph at some point like this:

image

You don’t need three registrations for this, only one since every subresolving of IService will happen on it’s own. Something that isn’t really practical actually since we still lack the ability to influence the resolve process. Both IServices will be populated by the same registered type, it just won’t be the parent IService again.

I also spent almost the whole day refactoring the resolver code since I felt that it became quickly unreadable.

Next:

I still feel like the resolver needs some more refactoring, and I also want to improve the error messages. After that, I’d like to return my focus back to much needed features like lookup strategies and after that auto-configuration.

You can check out the code on the project site on bitbucket.

My Photography business

Projects

dynamic css for .NET

Archives

more