When one of my websites or service hosts starts up it executes a series of start-up tasks to prime the application for run time. I discovered this great bootstrapping library on codeplex, cleverly named bootstrapper written by luisbocaletti. Using this framework you can create several classes that implement IStartupTask which will execute on start-up, as long as you include the bootstrapper initialisation code.
You can also write dependency injection container extensions for it to install your dependencies into a container of your choice on start-up. I based my Windsor extension on the one I found on the Bootstrapper website. However I customised it to my needs. I have customised it to take an environment setting which will then set the context of whether the bootstrap operation is occurring for a web based system or a bus based system. It also takes the class I wrote to store assemblies that are not stored in the bin directory (this will be explained in a later article).
Upon execution of the first part of the bootstrapping initialisation:
1 | Bootstrapper.With.Windsor(AssemblyResolver, BootstrapEnvironment.WEB) |
The code registers every class that implements an IRegister interface into the container. Then depending on the context it runs every class that registers dependencies into Windsor. In the case of the Web it executes every Register(IWindsorContainer container) for every class that implements IWebWindsorRegistration, in the case of the service host it executes every IBusWindsorRegistration.
This allows one to specify different lifestyles for different dependencies. For example in a web context you are likely to want to manage your data access classes on a per web request basis. However in a service host context you may want to specify a per thread lifestyle.
1 2 3 4 5 6 | Bootstrapper.With.Windsor(AssemblyResolver, BootstrapEnvironment.WEB).And.StartupTasks() .UsingThisExecutionOrder(s => s .First() .Then() .Then().TheRest()) .Start(); |
Once registering everything into the container the bootstrapper begins the start-up tasks. Beginning with adding a method to the app domain assembly resolve method which points to the GetAssembly method in the class I have to manage assemblies which are loaded from outside the bin folder. Now when an assembly is required it will also execute this method and return the assembly if it is located within the dictionary where the external libraries are loaded.
1 2 3 4 | public void Run() { AppDomain.CurrentDomain.AssemblyResolve += (a, args) => MvcApplication.AssemblyResolver.GetAssembly(args.Name); } |
The bootstrapper then executes the start-up task to configure nServiceBus allowing messages to be sent from the website to a configured MSMQ queue and picked up by the service host (Will be covered by a later article).
There are then a series of other start-up tasks. I have used Fluent Validation on this project as I found the Data Annotations framework quite limiting. There are going to be regular situations where you want more than simple validation and you need to validate against a database. For example when a register attempts to create an account, does a user with their details already exists? As Data Annotations use filters it is not possible to inject a repository or nHibernate session into the filter and validate against a database. Fluent Validation however does allow you to and I believe it still creates unobtrusive javascript for the simple validation rules. Anyway one of the start up tasks for the web context adds a new Validation Provider to the ModelValdationProviders class. All validations are also installed into my Windsor Container using my Windsor Installers allowing them to be injected into any class.
There is also a Logging Start-up Task which registers a global filter I wrote to handle errors. If an exception is unhanded it sends a message to the logging queue for the logging system to process.
There is the standard overriding of the controller factory to resolve controllers from the Windsor container. This works nicely with the plug-in approach I will explain in later articles as with my IWebWindsorInstallers you can register the controllers you create in the plug-ins into the container which are loaded from outside the bin directory.
There is a task to register routes into the routing table. Routes can be registered by the plug-in libraries by implementing an IRegisterRoutes interface I have created and installing them into the container using the previously mentioned IWebWindsorInstaller. The start-up task resolves all of the classes which implement IRegisterRoutes and registers all the defined routes into the route table.
There are two more tasks for the process of starting the Web Application. The penultimate one is adding a view engine to the MVC Framework. The view engine allows the system to return razor views which have been registered into the Windsor Container. Using David Ebbo’s Razor Generator each plug-in compiles their views which are then registered into the container during the install tasks. Then upon each request the view is searched for in the container before looking in the standard file disk locations.
The final task creates a navigation structure. Each plug-in can register a navigation structure in the form of a tree structure. They can define things such as the roles required to access that component. Upon start-up the container is passed to a class which upon each requests outputs a navigation structure using the navigation information provided by each plug-in. Obviously it does not include paths which the current user does not have access rights for. It does however merge paths of the same structure until they differ, for example on the final leaf node.
Now some of you maybe here thinking, he’s talked a lot about the Web Context but what about the Service Host. Where did that go? The service host also uses the bootstrapper and installs all the classes that implement IBusWindsorInstaller. However it only really has the start-up task to resolve assemblies from outside of the bin directory.
Apologies for quickly brushing over the tasks. I have done so as they pretty much each deserve their own article, hence the part one. These will be coming in the forthcoming weeks however for now I just wanted to make a start and where better than how it starts up. Hopefully how things work will become clearer as I begin to write about how all of these pieces slot together.
It‘s quite in here! Why not leave a response?