Prism Developer's Guide - Part 2, Initializing Prism Applications
- Transfer
- Tutorial
Table of contents
This chapter talks about what you need to do to download the Prism application. The Prism application requires registration and configuration of components at startup - this process is known as bootstrapping.
What is a bootstrapper?
The loader is the class responsible for initializing an application created using the Prism library. By using the bootloader, you get more control over how the components of the Prism library are created and linked when your application starts. The Prism library includes an abstract base loader class that can be specialized for use with any container. Many of the methods in the loader classes are virtual. You can override these methods to provide your own implementation.
The Prism library provides some additional base classes inherited from the Bootstrapper classhaving default implementations that are suitable for most applications. All that remains is to add an implementation for creating and initializing the application shell.
Dependency Injection
Applications built using the Prism library rely on the dependency injection mechanism provided by the container. The library provides assemblies that work with the Unity Application Block (Unity) or the Managed Extensibility Framework (MEF), and allows the use of other DI containers. Part of the boot process is configuring the container and registering the necessary types in it.
The Prism library includes the UnityBootstrapper and MefBootstrapper classes that implement most of the functionality needed to use both Unity and MEF as a DI container. In addition to the steps shown in the previous illustration, each bootloader adds some steps specific to the container used.
Shell creation
In a traditional WPF application, the start URI is defined in the App.xaml file, which determines when the application loads which window is the root. In a Silverlight application, the application's RootVisual property is set to code behind the App.xaml file. In an application created using the Prism library, the loader must create the shell itself or the main window. This is because the shell relies on services, such as a region manager, that must be registered before the shell is created and displayed.
Key Decisions
After you decide to use the Prism library in your application, you will need to think about the following:
- You will need to decide whether you will use MEF, Unity, or another DI container in your application. This will determine which bootloader class you should use, or whether you will need to create your own bootloader for the selected container.
- You should consider the specialized services that you will use in your application. They must also be registered in the container.
- Determine if the built-in journaling service meets your needs, or if you need to create and register another service.
- Determine how the modules will be detected by the application: through explicit declarations in the code, through the attributes on the modules found after scanning the file system directory, through the configuration file, or through XAML.
Next, we consider these points in detail.
Base scenarios
Creating a boot sequence is an important part of creating your Prism application. This section describes how to create a bootloader and configure it to create a wrapper, configure a DI container and application-level services, and how to load and initialize modules.
Creating a bootloader for your application
If you choose Unity or MEF, creating a bootloader will be relatively simple. You must create a class inherited from MefBootstrapper or UnityBootstrapper and implement the CreateShell method . Additionally, you can override the InitializeShell method to specifically initialize the wrapper class.
CreateShell Method Implementation
The CreateShell method allows the developer to define a high-level window for the Prism application. The shell is usually MainWindow or MainPage , in the case of Silverlight. Implement this method by returning an instance of the shell class of your application. In a Prism application, you can either create a shell object or retrieve it from a container, depending on the requirements of your application.
An example of using ServiceLocator to get a shell object is given in the following code example.
protected override DependencyObject CreateShell()
{
return ServiceLocator.Current.GetInstance();
}
Note
You will often see that ServiceLocator is used instead of a specific dependency injection container to obtain type instances . ServiceLocator is implemented in such a way that redirects calls to the container, so its use is a good choice for writing code that does not depend on the selected container. You can also reference and use the container itself, not the ServiceLocator .
Implementing the InitializeShell Method
After you create the shell, you may need to complete the initialization steps to ensure that the shell is ready for display. Depending on whether you are writing a WPF or Silverlight application, implementations of the InitializeShell method may vary. For Silverlight applications, you must set the shell as the visual root of the application, as shown below.
protected override void InitializeShell()
{
Application.Current.RootVisual = Shell;
}
For WPF applications, you need to create an application shell object and set it as the main application window (taken from Modularity QuickStarts for WPF).
protected override void InitializeShell()
{
Application.Current.MainWindow = Shell;
Application.Current.MainWindow.Show();
}
The basic implementation of InitializeShell does nothing. It is safe not to invoke the base class implementation.
Creating and configuring a module catalog
If you are creating a modular application, you will need to create and configure a catalog of modules. Prism uses a specific instance of IModuleCatalog to track which modules are available to the application, which modules may need to be loaded, and where the modules are located.
The Bootstrapper class provides the protected ModuleCatalog property for directory reference, as well as the basic implementation of the CreateModuleCatalog virtual method . The base implementation returns a new instance of ModuleCatalog . However, this method can be overridden to provide another instance of the module catalog, as shown in the following code example from QuickStartBootstrapper in Modularity with MEF for Silverlight QuickStart.
protected override IModuleCatalog CreateModuleCatalog()
{
// When using MEF, the existing Prism ModuleCatalog is still
// the place to configure modules via configuration files.
return ModuleCatalog.CreateFromXaml(new Uri(
"/ModularityWithMef.Silverlight;component/ModulesCatalog.xaml",
UriKind.Relative));
}
In both UnityBootstrapper and MefBootstrapper classes, the Run method calls the CreateModuleCatalog method and then sets the ModuleCatalog property of the class using the returned value. If you override this method, there is no need to call the base class implementation, because you are replacing the provided functionality. For more information on modularity, see Chapter 4, “Modular Application Development”.
Create and configure a DI container
Containers play a key role in an application built using the Prism library. Both the Prism library and the applications created using it depend on the DI container needed to implement the required dependencies and services. During the container configuration phase, some basic services are registered in it. In addition to these services, you can have your own specialized services that provide additional functionality that affects the composition process.
Basic services
The following table shows the basic non-specialized services of the Prism library.
Interface. | Description. |
---|---|
IModuleManager , module manager | Defines an interface for a service that receives and initializes application modules. |
IModuleCatalog , modules catalog | Contains metadata about the modules in the application. The Prism library provides several different directories. |
IModuleInitializer , module initializer | Initializes modules. |
IRegionManager , region manager | Registers and receives regions that are visual layout containers. |
IEventAggregator , event aggregator | A collection of events loosely related between publisher and subscriber. |
ILoggerFacade | A wrapper for the journaling engine. Thus, you can choose your own logging mechanism. The Stock Trader Reference Implementation (Stock Trader RI) uses the Enterprise Library Logging Application Block, through the EnterpriseLibraryLoggerAdapter class , as an example of how you can use your own logger. The logging service is registered in the container in the Run method of the bootloader using the value returned by the CreateLogger method . Registering another logger in the container will not work; instead, override the CreateLogger method in the bootloader. |
IServiceLocator | Allows the Prism library to access the container. It may be useful if you want to customize or expand the library. |
Specialized services
The following table lists the specialized services used in Stock Trader RI. They can be used as an example to understand what services might be in your application.
Service at Stock Trader RI. | Description. |
---|---|
IMarketFeedService | Provides (false) market data in real time. PositionSummaryPresentationModel updates the position screen based on the notifications received from this service. |
IMarketHistoryService | Provides a history of market data used to display trendlines for a selected fund. |
IAccountPositionService | Provides a list of funds in a portfolio. |
IOrdersService | Contains sent purchase / sale orders. |
INewsFeedService | Provides a list of news posts for the selected foundation. |
IWatchListService | Determines when a new item is added to the list of monitored items. |
There are two bootstrapper derived bootloaders available in Prism, UnityBootstrapper and MefBootstrapper. Creating and configuring other containers involves similar steps, implemented a little differently.
Creating and Configuring a Container in UnityBootstrapper
The CreateContainer method of the UnityBootstrapper class simply creates and returns a new instance of UnityContainer . In most cases, this functionality does not need to be changed. However, the method is virtual, providing the necessary flexibility. After the container is created, it probably should be configured accordingly. Implementing a ConfigureContainer in UnityBootstrapper registers the default Prism base services, as shown below.
Note
In approximately the same way, the module registers its services in the Initialize method .
protected virtual void ConfigureContainer()
{
...
if (useDefaultConfiguration)
{
RegisterTypeIfMissing(typeof(IServiceLocator), typeof(UnityServiceLocatorAdapter), true);
RegisterTypeIfMissing(typeof(IModuleInitializer), typeof(ModuleInitializer), true);
RegisterTypeIfMissing(typeof(IModuleManager), typeof(ModuleManager), true);
RegisterTypeIfMissing(typeof(RegionAdapterMappings), typeof(RegionAdapterMappings), true);
RegisterTypeIfMissing(typeof(IRegionManager), typeof(RegionManager), true);
RegisterTypeIfMissing(typeof(IEventAggregator), typeof(EventAggregator), true);
RegisterTypeIfMissing(typeof(IRegionViewRegistry), typeof(RegionViewRegistry), true);
RegisterTypeIfMissing(typeof(IRegionBehaviorFactory), typeof(RegionBehaviorFactory), true);
}
}
The Loader's RegisterTypeIfMissing method determines whether the service has already been registered, which prevents them from being re-registered. This allows you to override the default registration through the configuration. You can also turn off the registration of any services by default, to do this, use the overloaded Bootstrapper.Run method , passing false to it . You can also override the ConfigureContainer method and disable services that you do not want to use, such as an event aggregator.
Note
If you turn off default registrations, you will need to manually register the necessary services.
To extend the default behavior of ConfigureContainer , simply add an override to your application loader and optionally call the base implementation, as shown in the following code from QuickStartBootstrapper from Modularity for WPF (with Unity) QuickStart. This implementation calls the base class implementation, registers the ModuleTracker type as the concrete IModuleTracker implementation , and registers the callbackLogger as the singleton of the CallbackLogger instance that comes with Unity.
protected override void ConfigureContainer()
{
base.ConfigureContainer();
this.RegisterTypeIfMissing(typeof(IModuleTracker), typeof(ModuleTracker), true);
this.Container.RegisterInstance(this.callbackLogger);
}
Create and configure a container in MefBootstrapper
The CreateContainer method of the MefBootstrapper class does several things. First, it creates AssemblyCatalog and CatalogExportProvider . CatalogExportProvider allows the MefExtensions assembly to provide default export for Prism types and also allows you to override the default type registration. Then CreateContainer creates and returns a new instance of CompositionContainer using CatalogExportProvider . In most cases, you will not need to change this functionality. However, the method is virtual, providing the necessary flexibility.
Note
In Silverlight, due to security restrictions, you cannot get an assembly using a type. Instead, Prism uses another method that uses the Assembly.GetCallingAssembly method .
After the container is created, it must be configured for your application. The implementation of ConfigureContainer in MefBootstrapper registers many basic Prism services by default, as shown in the following code example. If you override this method, consider whether you should call the base class implementation to register the Prism base services, or you will provide them in your implementation.
protected virtual void ConfigureContainer()
{
this.RegisterBootstrapperProvidedTypes();
}
protected virtual void RegisterBootstrapperProvidedTypes()
{
this.Container.ComposeExportedValue(this.Logger);
this.Container.ComposeExportedValue(this.ModuleCatalog);
this.Container.ComposeExportedValue(new MefServiceLocatorAdapter(this.Container));
this.Container.ComposeExportedValue(this.AggregateCatalog);
}
Note
In MefBootstrapper, the basic Prism services are added to the container as singleton, so they can be received through the container throughout the application.
In addition to providing the CreateContainer and ConfigureContainer methods , MefBootstrapper also provides two methods for creating and configuring the AggregateCatalog used by MEF. The CreateAggregateCatalog method simply creates and returns an AggregateCatalog object . Like other methods in MefBootstrapper , CreateAggregateCatalog is virtual and can be overridden if necessary. The ConfigureAggregateCatalog method allows you to add type registration to AggregateCatalog forcibly. For example, QuickStartBootstrapperfrom the Modularity with MEF for Silverlight, QuickStart explicitly adds ModuleA and ModuleC to the AggregateCatalog , as shown below.
protected override void ConfigureAggregateCatalog()
{
base.ConfigureAggregateCatalog();
// Add this assembly to export ModuleTracker
this.AggregateCatalog.Catalogs.Add(
new AssemblyCatalog(typeof(QuickStartBootstrapper).Assembly));
// Module A is referenced in in the project and directly in code.
this.AggregateCatalog.Catalogs.Add(
new AssemblyCatalog(typeof(ModuleA.ModuleA).Assembly));
// Module C is referenced in in the project and directly in code.
this.AggregateCatalog.Catalogs.Add(
new AssemblyCatalog(typeof(ModuleC.ModuleC).Assembly));
}
Additional Information
For more information about MEF, AggregateCatalog , and AssemblyCatalog , see the "Managed Extensibility Framework Overview" on MSDN: http://msdn.microsoft.com/en-us/library/dd460648.aspx .