With the Web Client Softare Factory you can easily use dependency injection for object creation and obtaining required resources. For example, the factory demonstrates how to use view/presenter pairs with an application (module) controller. As the user interacts with a Web page, the presenter notifies the controller, where the business logic and flow decisions reside. This means a presenter requires a reference to a controller instance. With the WCSF, the presenter can use the CreateNewAttribute to have the controller object injected into the presenter (the presenter does not create the controller):

public ApproveCustomerViewPresenter([CreateNew] CustomersController controller)
{ 
  controller = controller;
}

However, the CreateNewAttribute requires that you specify a concrete type. Wouldn’t it be great if we could request an implementation of an interface, and make WCSF behave like a simple factory?

For example, imagine that we want to tell the ObjectBuilder to inject a concrete implementation of the interface IMyInterface, but we don’t really care which implementation to use. In fact, we want to let some other part of the code (e.g. in another module, or in the configuration file) determine the concrete implementation to use. In essence, we want the developer experience to be similar to the code required when you request a service:

public CustomersController([ServiceDependency] INavigationService navigationService)
{
  navigationService = navigationService;
}

While we want the developer experience to be similar, we want the behavior to be different. We want the ability to have a new object created and obtain a reference to that object.

First Attempt

Those with experience in CAB or SCSF might consider implementing this with a  TypeMappingPolicy. Actually, that was the first thing I intended to do, as I was inspired by this forum thread. Unfortunately, it turns out we can’t use the TypeMappingPolicy without modifying the way WCSF builds objects. When objects are created by WCSF, it assigns a random GUID as a temporary id for the object being built. It does not let you specify a custom id, and therefore the builder cannot find the appropriate TypeMappingPolicy required to provide a new concrete type of the object being requested.

Proposed Solution

Introduce a new attribute, InterfaceDependencyAttribute, to provide this functionality. With this attribute, your code requests an object instance that implements a particular interface:

public ModuleController([InterfaceDependency] IMyInterface myInterface)
{
    myInterface.Foo();
}

We want this to attribute to behave in the same way as the [CreateNew] attribute, but instead obtain a concrete implementation of an interface.

What if there are multiple implementations of an interface, and a developer wants to request a particular one? The attribute allows you to specify the key of a particular implementation:

public ModuleController([InterfaceDependency] IMyInterface myInterface, [InterfaceDependency(Key="Other")] IMyInterface myOtherInterface)
{
    myInterface.Foo();
    myOtherInterface.Foo();
}

To support this, you must add a mapping when you register particular types. This way ObjectBuilder will know which concrete type to inject. For example in the ShellModuleInitializer you could add the following method:

private void AddRegisteredTypes(IInterfaceMappingService interfaceMappingService)
{ 
    interfaceMappingService.RegisterType(typeof(MyConcreteObject), typeof(IMyInterface)); 
    interfaceMappingService.RegisterType(typeof(MyOtherConcreteObject), typeof(IMyInterface), “Other”);
}

And in the Load method, add the InterfaceMappingService to the root WorkItem.

AddRegisteredTypes(moduleContainer.Parent.Services.Get<IInterfaceMappingService>());

Extending WCSF

To make this even easier (and not require developers to write this code in each application) we could extend WCSF a little. All we need to do is add a few classes to our solution, and register IInterfaceMappingService as a global service before trying to build any object using the InterfaceDependency attribute. This could be done in a custom application class, and make global.asax inherit from this class:

public class CustomWebClientApplication : WebClientApplication
{ 
    protected override void AddRequiredServices() 
    { 
        RootContainer.Services.AddNew<InterfaceMappingService, IInterfaceMappingService>(); 
        base.AddRequiredServices(); 
    }
}

Possible Enhancements

You could even add another attribute to decorate your concrete classes, and let ObjectBuilder register them automatically to the IInterfaceMappingService. (This is not done in the provided source code.) Another improvement could be to register the concrete types in the web.config file, and implement a class that reads them and configures them automatically.

Comparison between CreateNew and InterfaceDependency attributes

Don’t confuse this InterfaceDependency with ServiceDependency. The ServiceDependency attribute is specific to CWAB services (which are stored in the services container), and not for the creation of objects. The InterfaceDependency attribute is closer to (but a little more complex than) the CreateNew attribute. The difference is that with the InterfaceDependency attribute you do not specify which concrete class to instantiate.

The following table compares both attributes:

CreateNew InterfaceDependency
Allows creation of a concrete type (replacement of new operator) Allows creation of a concrete type that implements a specific interface
Very simple use, simply add the attribute above a concrete type Two step use: register the type in a catalog (InterfaceMappingService), decorate a parameter/property with [InterfaceDependency]
The object is built with ObjectBuilder and all the strategies will run over it. The object is built with ObjectBuilder and all the strategies will run over it.
The object won’t be added to any container The object won’t be added to any container
The lifetime of the object is the page request (not singleton) The lifetime of the object is the page request (not singleton)
 The specified concrete type will be injected Allows multiple implementation of the same interface and the injection of a particular using a string identifier

   

Source code

  • You can get the source code of the CompositeWeb.Extensions project by downloading the CompositeWeb.Extensions.zip file below. The solution contains the source code of the required services and parameter, plus a sample application that uses this approach
  • If you face problems building the solution, make sure the references to the Composite Web Application Block and ObjectBuilder assemblies are correct.
  • Important: The code is provided “as is” without warranty of any kind.

Attachment: CompositeWeb-InterfaceDependency.zip

   

8 Responses to “How To: Extend Object Creation in the Web Client Software Factory”

  1. David Hayden Says:

    Great post, Julian.

  2. 窃听器 Says:

    Good article, the author thanks!

  3. Nikola Malovic Says:

    Great stuff!

  4. http:// Says:

    Great Extension!! Solved a challenge we were having.

    I had to make the following two changes in order to support embedded resources:

    1) Modified the RequestEventBroker static Instance property (set method) to verify the session is not null before trying to assign the broker to it.

    2) Modified the WebClientApplication PostPageExecute to verify that the RequestEventBroker exists before attempting to Dispose it.

  5. Ram Says:

    Hi,

    I am not able to access the code base provided as zip file.

    Could you please share the same.

  6. jdominguez Says:

    Hi Ram, I’ve updated the link, so it should work now.
    Anyways, if you are using the latest version of WCSF, there is a new feature in the container that allows you to register a type mapping to do just this. You can read about it here: http://msdn.microsoft.com/en-us/library/cc304886.aspx

  7. Ram Says:

    Hi Julian,

    Thanks for the quick response and the source.

    I am using WCSF latest. I have a specific challenge and I was looking out for customizing WCSF.

    I am trying to build AOP based injection within WCSF framework by tweaking ObjectBuilder itself.

    I added a strategy to intercept objects which are inherited from Marshall by Reference. That workded well for all the services with [ServiceDependency] attribute.

    However the same is not working for [CreateNew] where a register type mapping is already present.

    Please help by throwing some light on what could be going wrong.

    Thanks in advance.

  8. Fer Antivero’s Blog » How CWAB takes advantage of ObjectBuilder Says:

    [...] If you want continue reading more about it, I recommend to visit the following blog post How To: about Extend Object Creation in the Web Client Software Factory [...]

Leave a Reply