Radical.Windows.Presentation: bootstrap conventions
I’ve released a new version of my Presentation and UI Composition toolkit: Radical.Windows.Presentation, the new feature is the first drop of the Castle Windsor integration package (Radical.Windows.Presentation.CastleWindsor), this first drop targets only WPF 4.0 (but it is a first drop, next to come will be WinRT, Silverlight 5.0 and Windows Phone 7.5).
The integration package simply adds support for the famous Invention of Control toolkit introducing the WindsorApplicationBootstrapper<TShellView> that is the concrete implementation of the abstract ApplicationBootstrapper base class. The role of bootstrapper is:
- attach the relevant application events;
- wire up stuff to configure the I.o.C. container;
- show the shell view (the application main window);
Frictionless: convention based
As I have already said my first aim is to remove friction, it is not always easy and cannot be done every single time, but one thing that can give a lot of benefits in this area is to move from a configuration based toolkit to a convention based toolkit, I suppose that this concept is widely accepted and is nothing new.
What happens when this lines of code are executed:
namespace Radical.Presentation.Samples { public partial class App : Application { public App() { var bootstrapper = new WindsorApplicationBootstrapper<MainView>(); } } }
A lot of things:
- The application Startup event is wired;
- When the Startup event is fired:
- The Inversion of Control container is created;
- The MEF composition container is created;
- The composition container is composed against the bootstrapper itself (here is why, in Italian unfortunately);
- The Inversion of Control container is configured using the bootstrap conventions;
- The main window (the one identified by the TShellView generic parameter) is resolved and shown;
Bootstrap conventions
Bootstrap conventions are mainly related to the way components are registered into the container:
- every class the is defined in a namespace ending with “Services” (*.Services) will be considered a service and will be registered as singleton using as the service contract the first interface, if any, otherwise using the class type;
- every class the is defined in a namespace ending with “Presentation” (*.Presentation) and whose type name ends with “ViewModel” (*ViewModel) will be considered as a view model and registered as transient;
- following the same logic every type in the same namespace whose name ends with “View” (*View) will be considered a view, a transient view;
- if a view or a view model are a shell, type name beginning with Shell* or Main*, they will be registered as singleton
- be default views and view models will be registered using as service contract the class type and no interface is searched along the way;
- every type defined in the “Messaging.Handler” namespace (*.Messaging.Handler) will be considered a message broker message handler and will be registered as singleton and automatically attached, as an handler, to the broker pipeline;
These are the main conventions used at boot time, there are a few more but less important. Obviously all these behaviors can be replaced or extended to accomplish the end user needs:
public App() { var bootstrapper = new WindsorApplicationBootstrapper<MainView>(); var conventions = bootstrapper.GetService<BootstrapConventions>(); var original = conventions.IsService; conventions.IsService = t => { if( /* your logic to determine if “t” is a service */ ) { return true; } return original( t ); }; }
All the conventions are Func(s) or Predicate(s) and can be easily replaced as shown in the above sample, here is the list of the currently defined and used conventions:
- IsService: determines if a type should be considered a service or not;
- SelectServiceContracts: given the service type returns the list of contracts that should be used to register the given service;
- IsMessageHandler:determines if a type should be considered a message handler or not;
- SelectMessageHandlerContracts: given the message handler type returns the list of contracts that should be used to register the given message handler;
- IsView: determines if a type should be considered a view or not;
- IsViewModel: determines if a type should be considered a view model or not;
- IsShellView: determines if a type should be considered a shell view or not;
- IsShellViewModel: determines if a type should be considered a shell view or not;
- SelectViewContracts: given the view type returns the list of contracts that should be used to register the given view;
- SelectViewModelContracts: given the view model type returns the list of contracts that should be used to register the given view model;
In the next post will deal with runtime conventions.
.m