Bootstrapper
Retired Content |
---|
This content is outdated and is no longer being maintained. It is provided as a courtesy for individuals who are still using these technologies. This page may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. |
The bootstrapper is responsible for the initialization of an application built using the Composite Application Library. Having a bootstrapper gives you more control of how the Composite Application Library components are wired up to your application. For example, if you have an existing application that you are adding the Composite Application Library to, you can initialize the bootstrapping process after the application is already running.
In a traditional Windows Presentation Foundation (WPF) application, a startup Uniform Resource Identifier (URI) is specified in the App.xaml that launches the main window. In an application created with the Composite Application Library, it is the bootstrapper's responsibility. This is because the shell relies on services, such as the Region Manager, that need to be registered before the shell can be displayed. Additionally, the shell may rely on other services that are injected into its constructor. For more information about the shell, see the Shell and View technical concept.
The Composite Application Library includes a default abstract UnityBootstrapper class that handles this initialization using the Unity container. Many of the methods on the UnityBootstrapper class are virtual methods. You should override these methods as appropriate in your own custom bootstrapper implementation. If you are using a container other than Unity, you should write your own container-specific bootstrapper.
Figure 1 illustrates the stages of the bootstrapping process.
Figure 1
Bootstrapping process
Configuring the Container
Containers play a key role in an application created with the Composite Application Library. Both the Composite Application Library and the applications built on top of it depend on a container for injecting required dependencies. During the container configuration phase, the container is created and several core services are registered, as shown in the following code from the UnityBootstrapper. Notice that the container itself is registered with itself. This allows modules to get direct access to the container to explicitly register and resolve dependencies.
Note
An example of this is when a module registers module-level services in its Initialize method.
protected virtual void ConfigureContainer()
{
…
Container.RegisterInstance<IUnityContainer>(Container);
…
if (_useDefaultConfiguration)
{
RegisterTypeIfMissing(typeof(IContainerFacade), typeof(UnityContainerAdapter), true);
RegisterTypeIfMissing(typeof(IEventAggregator), typeof(EventAggregator), true);
RegisterTypeIfMissing(typeof(RegionAdapterMappings), typeof(RegionAdapterMappings), true);
RegisterTypeIfMissing(typeof(IRegionManager), typeof(RegionManager), true);
RegisterTypeIfMissing(typeof(IModuleLoader), typeof(ModuleLoader), true);
}
}
The bootstrapper will determine whether a service has already been registered—it will not register it twice. This allows you to override the default registration through configuration. You can also turn off registering any services by default and disable services that you do not want to use, such as the event aggregator.
Note
If you turn off the default registration, you will need to manually register required services.
Configuring the Region Mappings
During this phase, the default region adapter mappings are registered. These mappings are used by the region manager to associate the correct adapters for XAML-defined regions. By default, an ItemsControlRegionAdapter, a ContentControlRegionAdapter and a SelectorRegionAdapter are registered. For more information about these adapters, see the Region technical concept.
You can also override the ConfigureRegionAdapterMappings method to add your own custom region adapter mappings, as shown in the following code from the UnityBootstrapper.
protected virtual RegionAdapterMappings ConfigureRegionAdapterMappings()
{
RegionAdapterMappings regionAdapterMappings = Container.TryResolve<RegionAdapterMappings>();
if (regionAdapterMappings != null)
{
regionAdapterMappings.RegisterMapping(typeof(Selector), new SelectorRegionAdapter());
regionAdapterMappings.RegisterMapping(typeof(ItemsControl), new ItemsControlRegionAdapter());
regionAdapterMappings.RegisterMapping(typeof(ContentControl), new ContentControlRegionAdapter());
}
return regionAdapterMappings;
}
Creating the Shell
During this phase, the shell will be displayed if it exists. Having the creation of the shell in the bootstrapper allows greater testability of the application because the shell can be mocked in a unit test.
If you are adding the Composite Application Library to an existing application, there may not be a shell. In these instances, you should override the CreateShell method to return null. For more information about the shell, see the Shell and View technical concept. The following code from the file StockTraderRIBootstrapper.cs shows the CreateShell method used in the Stock Trader Reference Implementation (Stock Trader RI).
protected override DependencyObject CreateShell()
{
ShellPresenter presenter = Container.Resolve<ShellPresenter>();
IShellView view = presenter.View;
view.ShowView();
return view as DependencyObject;
}
Initializing the Modules
During this phase, module loading occurs. First, the module enumerator and module loader services are resolved from the container. After that, the modules that have been specified to load on startup are initialized, as shown in the following code from the UnityBootstrapper.
protected virtual void InitializeModules()
{
IModuleEnumerator moduleEnumerator = Container.TryResolve<IModuleEnumerator>();
if (moduleEnumerator == null)
{
throw new InvalidOperationException(Resources.NullModuleEnumeratorException);
}
IModuleLoader moduleLoader = Container.TryResolve<IModuleLoader>();
if (moduleLoader == null)
{
throw new InvalidOperationException(Resources.NullModuleLoaderException);
}
ModuleInfo[] moduleInfo = moduleEnumerator.GetStartupLoadedModules();
moduleLoader.Initialize(moduleInfo);
}
If you are loading modules statically, you should override this method and use the StaticModuleEnumerator, as shown in the following code from the Stock Trader RI.
protected override IModuleEnumerator GetModuleEnumerator()
{
return new StaticModuleEnumerator()
.AddModule(typeof(NewsModule))
.AddModule(typeof(MarketModule))
.AddModule(typeof(WatchModule), "MarketModule")
.AddModule(typeof(PositionModule), "MarketModule", "NewsModule");
}
For more information about modules, see the Module technical concept.
Retired Content |
---|
This content is outdated and is no longer being maintained. It is provided as a courtesy for individuals who are still using these technologies. This page may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. |