Converting the Composite Web Application Block to Unity - Adding an ICompositionContainer

This is the second post in a series. The initial post is Converting the Composite Web Application Block to Unity - Intro. If you want background, go read the first post.

EDIT: Based upon feedback, I am making the source code available at CWAB and Unity.

Let's get started with some coding. First, I backed up my WCSF and WCSF-AppBlock code. Then, I opened up Visual Studio 2008, opened the Composite Web Application Block solution (with VSTS Tests), compiled, and ran all the tests. All Green.

Now for the tough part, thinking about what we want to do.  Basically, we need to be able to replace the CompositionContainer with an IUnityContainer, and remove a lot of dead code.  How can we do this?  We need to have a simple interface that would work as a facade on both containers (and any others).  So, let's create an interface for this facade from what we have, and morph it into what we need.

I opened CompositionContainer.cs, and using a refactoring tool ( which happens to be a VS plugin) I extracted a new public interface from the class. I made all the public methods (Except IDisposable methods) part of the interface, and let the refactoring tool do a search/replace of CompositionContainer with ICompositionContainer where it thought appropriate.

Compile -> Failed.

Ooops, It missed a few spots.   Global search, careful replace. It compiles, but seven tests fail. After updating ModuleThrowingException.TestModuleInitializer and TestModule.TestModuleInitializer to both use the new interface, the failing unit tests pass.

Re-run all the tests-> Success. We are green again. :-)

So, we have done a very minor refactoring, and arrived at a good state again. 

Before we do anything else, let's compare the two interfaces we want to get to eventually line up. Here is ICompositionContainer

 public interface ICompositionContainer
{
    [Dependency(NotPresentBehavior = NotPresentBehavior.ReturnNull)]
    ICompositionContainer Parent { get; set; }

    ICompositionContainer RootContainer { get; }
    IBuilder<WCSFBuilderStage> Builder { get; }
    IReadWriteLocator Locator { get; }
    IManagedObjectCollection<ICompositionContainer> Containers { get; }
    IServiceCollection Services { get; }
    event EventHandler<DataEventArgs<object>> ObjectAdded;
    event EventHandler<DataEventArgs<object>> ObjectRemoved;
    void RegisterTypeMapping<TRequested, TReturned>() where TReturned : TRequested;
    void RegisterTypeMapping(Type requested, Type returned);
    Type GetMappedType<TRequested>();
    Type GetMappedType(Type requested);
    object BuildNewItem(Type typeOfItem);
    void InitializeRootContainer(IBuilder<WCSFBuilderStage> builder);

    [InjectionMethod]
    void InitializeContainer();
}

And here is IUnityContainer:

 public interface IUnityContainer : IDisposable
{
    IUnityContainer RegisterType<TFrom, TTo>() where TTo : TFrom;
    IUnityContainer RegisterType... //about 10 overloads

    IUnityContainer RegisterInstance<TInterface>(TInterface instance);
    IUnityContainer RegisterInstance... //about 10 overloads

    T Resolve<T>();
    T Resolve<T>(string name);
    object Resolve(Type t);
    object Resolve(Type t, string name);

    IEnumerable<T> ResolveAll<T>();
    IEnumerable<object> ResolveAll(Type t);
    
    T BuildUp<T>(T existing);
    T BuildUp<T>(T existing, string name);
    object BuildUp(Type t, object existing);
    object BuildUp(Type t, object existing, string name);
    void Teardown(object o);
    
    IUnityContainer AddExtension(UnityContainerExtension extension);
    IUnityContainer AddNewExtension<TExtension>() where TExtension : UnityContainerExtension, new();
    
    TConfigurator Configure<TConfigurator>() where TConfigurator : IUnityContainerExtensionConfigurator;
    object Configure(Type configurationInterface);

    IUnityContainer RemoveAllExtensions();
    IUnityContainer CreateChildContainer();
    IUnityContainer Parent { get; }
}

 

It looks like I have a little bit of work ahead.  I think I will start with the simple stuff. Parent stays around.  RegisterTypeMapping becomes RegisterType.  BuildNewItem becomes Resolve, based on the semantics.  Also, InitializeContainer and InitializeRootContainer need to go away, as does GetMappedType the Containers collection, and the public events. I won't bore you with the details, but I will show you the new version of the ICompositionContainer interface when I am done and highlight any big difficulties.

...

<clicking of keyboard> 

...

<swearing> 

....

<clicking of keyboard>  

....

<sigh>

Here is the new version of the interface:

 public interface ICompositionContainer
{
    /// <summary>
    /// Gets or sets the parent container for this container instance.
    /// </summary>
    [Dependency(NotPresentBehavior = NotPresentBehavior.ReturnNull)]
    ICompositionContainer Parent { get; set; }
    IBuilder<WCSFBuilderStage> Builder { get; }
    IReadWriteLocator Locator { get; }
    IManagedObjectCollection<ICompositionContainer> Containers { get; }
    IServiceCollection Services { get; }
    void RegisterType<TRequested, TReturned>() where TReturned : TRequested;
    void RegisterType(Type requested, Type returned);
    object Resolve(Type typeOfItem);
}

In the process, I did remove code that I knew I would not need (since Unity will handle it) namely these files (and any test fixtures):

  • IContainerAwareTypeMappingPolicy.cs
  • ContainerAwareTypeMappingPolicy.cs
  • ContainerAwareTypeMappingStrategy.cs

I also commented out a few calls and marked them with TODO's ( // TODO: Replace this call with a new one from the UnityContainer)  I also commented out unit tests around features I know I removed (like type mapping support) with notes to remember them later.

We are again at a good state, having done some preliminary refactoring to make room for a new feature (or at least a better replacement for an old one).

Now, this is getting closer to my eventual goal, but we still need to get rid of the Builder, Locator, Containers, and Services.  Builder and Locator are handled by Unity.  Containers will just be registered in the root container by module name.  Services will probably just go away.  Global Services are types that are Registered on the Root Container as singletons, and module services are types registered on the appropriate Container as singletons.  This will be really simple once we get Unity in here.  In the mean time, things will probably get ugly.  We will tackle all of this in the next installment, though.