WCSF and SCSF now extended by the community!
May 30th, 2007
2 new open source projects were just lanched on Codeplex for all of us who want to extend both WCSF or SCSF and share it with the community: WCSFContrib and SCSFContrib.
Make sure you add bookmarks on them, as we expect a lot of contributions and extensions from the users. If you want to contribute to any of this projects, don’t hesitate, as you will be very welcomed.
Want to know more? Go and check out the contrib sites.
You can also find more information on Bil Simser’s post or Glenn Block’s post.
Cheers!
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