Friday 12 February 2010

Unity 2 and InjectionFactory

Unity 2 has now reached Beta 1 stage, which is available for download. One nice new feature is the InjectionFactory, which replaces the old and rather cumbersome StaticFactory.StaticFactoryExtension. David Hayden wrote a nice article explaining the details of it. I wanted to tweak that example in order to get Unity to resolve CSLA classes by calling their factory methods.

Setup

Lets create a quick and simple CSLA Customer class that implements a simple ICustomer interface:



If we want Unity to resolve a new Customer then we can simply do the following:



And the hard coded name "New Andreas" should appear in your console/output window.

Passing parameters to the static factory methods

This is fairly simple if your factories do not accept any parameters but is a bit trickier otherwise. Customer has also got a GetCustomer method which accepts an Id integer. One way of getting around this is to create a dedicated factory which we could call directly or inject into our container. In general though this adds an extra class (and potentially interface), which would be nice to avoid. So instead, we will add a delegate factory to our Customer class:



The delegate will serve a dual purpose here. First, it will be the factory that will be injected into the container and secondly, it will be responsible for either creating a new or fetching an existing customer, depending on weather the id parameter is empty or not. Back to our container setup:



And yep, you guessed it right. The output is:

Id:-1, Name:New Andreas
Id:3, Name:Old Andreas

Can I have a more real world example please?

That would be a bit too far fetched, but lets see how we could use this in conjunction with a ViewModelBase derived class. In this case, we will be injecting the customer factory to the ViewModel and let it update its Model when required.

To start off with, we add the constructor and properties:



Instead of calling the DoRefresh method on the ViewModel base class, we will assume control of loading the Model. This is done in the OnContextChanged handler (which is very similar to the implementation of BeginRefresh in the base class):



So now, when resolving a ViewModel:



And you can see the result on the console is what we expected it to be.

There are some parts of this solution which are not elegant. The DynamicInvoke call is a bit fragile and the fact we do not use DoRefresh or BeginRefresh is a shame. But this solution does provide some benefits as far as testability is concerned.

5 comments:

  1. Thanks for doing the research on this. Have you taken a look at the ParameterOverride and ParameterOverrides implementation yet? I was interested in seeing if they could be used in conjunction with the name to handle Csla.

    Example:
    var businessObject = container.Resolve("Get", new ParameterOverrides { { "container", container }, { "id", 2312 } });

    It seems to be a slightly more manageable solution, but I can't seem to get the parameter overrides to work with InjectionFactory.

    Cheers

    ReplyDelete
  2. Sorry... forgot:

    container.RegisterType("New", new InjectionFactory(c => TestBusinessObject.New(c)));
    container.RegisterType("Get", new InjectionFactory(c => TestBusinessObject.Get(c)));

    Then:
    var businessObject = container.Resolve("Get", new ParameterOverrides { { "container", container }, { "id", 2312 } });

    ReplyDelete
  3. I just took a quick look and indeed I cannot seem to get it to work. Saying that I did spot a slight improvement that removes the need for DynamicInvoke.

    Instead of:
    var oldCustomer = container.Resolve>().DynamicInvoke(new object[] { 3 }) as ICustomer;

    we can write:
    var oldCustomer = container.Resolve>().Invoke(3) as ICustomer;

    Not ideal but 1 step closer to... improvement :-)

    Also, you might want to take a look at the last post on this thread:

    http://unity.codeplex.com/Thread/View.aspx?ThreadId=68584

    Of course this is not usable with CSLA (since we want to type-safe the static factory method signatures) but I will take a look if this can be used.

    HTH

    ReplyDelete
  4. Hey, were you able to develop anything new beyond your last post? I am still trying to get the multiple factory methods to work, and still about in the same place.

    ReplyDelete
  5. Hi, unfortunately I have moved on and am not working on a CSLA project at the moment, hence my blog inactiveness :-(

    I think this still remains an unsolved issue and do not have high hopes of tackling it the way I would like to (same issue with MEF - see other blog post).

    Hopefully new posts will arrive soon!

    ReplyDelete