Fork me on GitHub

Introduction

To get started with the bootstrapper you need the following three things:

  1. Extension interface
  2. Strategy
  3. Bootstrapper setup

Extension interface

The extension interface defines the extension points which are called by the bootstrapper. The custom extension interface must inherit from IExtension and can only declare methods which return void as extension points. A very basic extension interface could look like the following:

The extension interface above would indicate that the applications extension points are:

That's basically it for the extension interface. Although we generally advice to build a base class which defines virtual members for the extension points. For example:

This allows inheritors to override only the extension points that they are interested in.

Note: IExtension inherits from IDescribable which defines a name and a description. The name and the description are consumed by the reporting capability. The more expressive your names and descriptions are the more meaningful your report will be!

Strategy

The strategy defines the order of execution for extension points. The custom strategy must inherit from IStrategy. For convenience there is a abstract base class AbstractStrategy{TExtension} which simplifies defining a custom strategy. A very basic strategy could look like the following:

The strategy above would configure the applications startup and shutdown phase to:

Now let's look how the custom extension interface and the strategy get combined together in the bootstrapper.

Bootstrapper setup

The bootstrapper setup is simply and straight forward. You can always follow the following steps:

  1. Instantiate a new DefaultBoostrapper
  2. Instantiate a new strategy
  3. Initialize the bootstrapper with the strategy
  4. Add the required extensions
  5. Call Run when the application starts
  6. Call Shutdown and then Dispose when the application stops.

Note:DefaultBootstrapper{TExtension} and AbstractStrategy{TExtension} implement IDisposable. The bootstrapper takes care of disposing the strategy and the extensions if the correct behavior is attached. If you are using FxCop to check your compiled code it will produces warnings that you should dispose the strategy and the extensions which implement IDisposable before the references move out of scope. You can safely suppress these warnings.

Advanced

The bootstrapper can do more! Let us look into a more complex scenario. Often it is required to collect context information during the bootstrapping process and pass this information to the extension points.

Imagine you are using an inversion of control container which intakes IModule implementations to register dependencies during the bootstrapping process. All IModule implementations need to be passed into the IContainer implementation upon construction. For backward compatibility the bootstrapper must automatically check whether an extension implements IModuleProvider and register the provided modules on the IContainer implementation.

The application we are designing allocates both managed and unmanaged resources which need to be removed when the application is shutting down. To get this behavior some extension occasionally implement IDisposable which needs to be called when shutting down.

Furthermore the application uses configuration sections to be able to overwrite some default configurations without recompiling the application. These configuration entries must be parsed and passed to the extensions which rely on their existence.

Extension interface

A custom extension interface for this example could look like the following:

The extension interface above would indicate that the applications extension points are:

An abstract base class for this use case:

This allows inheritors to override only the extension points that they are interested in. The extension interface does not include:

How can we extend the bootstrapping mechanism without modifying the custom extension interface? We need behaviors!

Behaviors

Behaviors allow extending the bootstrapping process in an aspect oriented style. Behaviors gain access to extensions which are participating in the bootstrapper process and can therefore influence them for example by injecting additional runtime information into an extension. Behaviors must implement IBehavior. They automatically gain access to all extensions participating the bootstrapping process. Behaviors are executed before the corresponding extension point is called.

Let's us look into an example. Imagine you have the following module provider interface below:

The behavior below demonstrates how extensions can be scanned whether they are module providers. If an extension implements IModuleProvider all provided modules are automatically collected in the module collection the behavior receives upon construction.

Below is a sample IModule implementation which uses a made up fluent syntax to register dependencies.

Below an extension which implements IModuleProvider and yield returns a custom module. Note:Of course the custom module could also have been added by overriding ContainerInitializing and adding the module to the collection. But the sample should demonstrate the power of behaviors.

The DependencyUsingBehavior is a special behavior which does not consume extensions but execute an operation on a dependency. The dependency is resolved by using constructor injection. To enable constructor injection when constructing the DependencyUsingBehavior the IContainer implementation must already be built up and the required dependencies registered.

The DisposeExtensionBehavior calls Dispose on all extensions which implement IDisposable.

Now all these pieces must be sticked together in the strategy. Let's look how to define it.

Strategy

The strategy defines the order of execution for extension points and behaviors. The custom strategy must inherit from IStrategy. For convenience, use the provided abstract base class AbstractStrategy{TExtension} which simplifies defining a custom strategy. The strategy could look like the following:

The strategy above would configure the applications startup and shutdown phase to: