Table of contents
- Introduction
- Initializing Prism Applications
- Manage dependencies between components
- Modular Application Development
- Implementation of the MVVM pattern
- Advanced MVVM scripts
- Creating user interface
- User Interface Design Recommendations
- Navigation
- View-Based Navigation (View-Based Navigation)
- The interaction between loosely coupled components
Applications created with the Prism library are typically composite applications, potentially consisting of loosely coupled services and components. They must interact with each other so as to provide content to the user interface and receive notifications about user actions. Since they are loosely coupled, they need a way to interact, without which the necessary functionality cannot be obtained.
To link all the pieces together, Prism applications rely on the DI container. DI containers reduce dependencies between objects by providing a way to instantiate classes and control their lifetime depending on the configuration of the container. When creating objects using a container, it injects the necessary dependencies into them. If the dependencies have not yet been created, the container first creates them and resolves their own dependencies. In some cases, the container itself is implemented as a dependency. For example, when using Unity, a container is inserted into modules so that they can register their views and services in it.
There are several advantages to using a container:
- The container eliminates the need for the component to locate its dependencies or control their lifetime.
- The container allows you to replace the implementation without affecting the components.
- The container facilitates testability, allowing you to embed fake dependencies on objects.
- The container simplifies maintenance by allowing new components to be easily added to the system.
In the context of an application based on the Prism library, there are certain advantages to using a container:
- The container resolves dependencies of the module when it is loaded.
- The container is used to register and create view models and views.
- The container can create view models and embed views.
- The container implements composite application services, such as a region manager, or event aggregator.
- A container is used to register module-specific services with module-specific functionality.
The note
Some examples in the Prism manual use the Unity Application Block (Unity) container. Others, such as Modularity QuickStarts , use the Managed Extensibility Framework (MEF). The Prism library itself is independent of the container used, and you can use its services and patterns with other containers, such as Castle Windsor, Autofac, Structuremap, Spring.NET, or with any other.
Key solution: select dependency deployment container
The Prism library provides two default DI containers: Unity and MEF. Prism is extensible, so you can use other containers by writing a small amount of code to adapt them. Both Unity and MEF provide the same basic functionality needed to implement dependencies, even considering that they work very differently. Some of the features provided by both containers are:
- Both allow you to register types in a container.
- Both allow you to register instances in a container.
- Both allow you to force instantiation of registered types.
- Both embed instances of registered types in constructors.
- Both embed instances of registered types in properties.
- They both have declarative attributes for managing types and dependencies.
- They both resolve dependencies in the object graph.
Unity provides several features not found in MEF:
- Permits specific types without registration.
- Allows open generalizations ( Generics ).
- Can use method call hooking to add additional functionality to the target object ( Interception ).
MEF provides several features not found in Unity:
- Independently detects assemblies in the file system directory.
- Loads XAP files and looks for assemblies in them.
- Conducts a recomposition of properties and collections when new types are detected.
- Automatically exports derived types.
- Comes with the .NET Framework, starting with the fourth version.
Containers vary in capacity and work differently, but Prism can work with any container, providing the same functionality. When considering which container to use, keep in mind your previous experience and determine which container is best for your application scenarios.
')
Container Considerations
What should be considered before using containers:
- Consider whether it is appropriate to register and resolve components using a container:
- Consider whether the performance impact of registering with the container and allowing instances is acceptable for your case. For example, if you have to create 10,000 polygons to draw something inside a drawing method, creating all the polygons through a container can lead to a significant loss of performance.
The note.
Some containers are able to resolve instances of objects almost as quickly as creating them through the new keyword. But, in any case, the resolution through the container of a large number of objects in the cycle should be seriously justified.
- If there are a lot of deep dependencies, then the time spent on their resolution can increase significantly.
- If the component has no dependencies or is not itself a dependency for other types, it may not make sense to use the container when creating it, or to place it in a container, respectively.
- If a component has a single set of dependencies that are an integral part of it and will never change, it may not make sense to use the container when creating it. Although, in this case, testing it can be complicated.
- Consider whether the component should be registered as a singleton, or as an instance:
- If a component is a global service that acts as a manager of a single resource, for example, a logging service, then you can register it as a singleton.
- If the component gives access to the general state to numerous consumers, then it can be registered as a singleton.
- If an object needs to create a new instance every time it is deployed, then it cannot be registered as a singleton. For example, each view probably needs a new instance of the view model.
- Consider whether you want to configure the container in code or through the configuration file:
- If you want to centrally manage all services, use the configuration file.
- If you want to register different services depending on any circumstances, configure the container in code.
- If you have module-level services, configure the container via code so that they are registered only when the module is loaded.
The note
Some containers, such as MEF, cannot be configured through the configuration file and must be configured in code.
Basic scripts
Containers are used for two main purposes, namely, registration and authorization.
check in
Before you can embed dependencies in an object, the types of dependencies must be registered in the container. Type registration usually involves passing an interface to a container and a specific type that implements this interface. First of all, there are two ways of registering types and objects: in code, or through a configuration file. Implementation details may vary by container.
As a rule, there are two ways to register types and objects in a container in code:
- You can register a type, or a display of one type to another. At the appropriate time, the container will create an instance of the type you specified.
- You can register an existing instance of an object as a singleton. The container will return a reference to an existing object.
Type Registration in UnityContainer
During initialization, a type can register other types, such as views and services. Registration allows you to resolve their dependencies container and become available to other types. To do this, you need to embed the container in the module's constructor. The following code shows how
OrderModule
from
Commanding QuickStart registers a repository type during initialization as a singleton.
public class OrderModule : IModule { public void Initialize() { this.container.RegisterType<IOrdersRepository, OrdersRepository>(new ContainerControlledLifetimeManager()); ... } ... }
Depending on which container you are using, registration can also be performed out of code through the configuration file. For an example, see "Registering Modules using a Configuration File" in Chapter 4, "
Modular Application Development ."
Type Registration with MEF Container
For registering types in a container, MEF uses an attribute-based system. As a result, it is quite easy to add a type registration to a container: this requires adding the
[Export]
attribute to the type that you want to register in the container, as shown in the following example.
[Export(typeof(ILoggerFacade))] public class CallbackLogger: ILoggerFacade { ... }
Another use case for MEF could be to create an instance of a class and register that particular instance in a container.
QuickStartBootstrapper
in
Modularity for Silverlight with MEF QuickStart shows an example of this in the
ConfigureContainer
method.
protected override void ConfigureContainer() { base.ConfigureContainer();
The note
When using MEF as a container, it is recommended that attributes be used to register types.
Resolution
After the type is registered, it can be enabled or implemented as a dependency. When a type is resolved, and the container must create a new instance of this type, it injects dependencies into that instance.
In general, when a type is allowed, one of three things happens:
- If the type has not been registered, the container throws an exception.
The note
Some containers, including Unity, allow you to resolve a specific type that was not registered.
- If the type was registered as a singleton, the container returns an instance of the singleton. If this is the first call, the container can create an instance and save it for future calls.
- If the type was not registered as a singleton, the container returns a new instance.
The note
By default, types registered in MEF are singletones, and the container stores references to objects. In Unity, by default, new instances of objects are returned, and the container does not save references to them.
Resolving instances in unity
The following code sample from
Commanding QuickStart shows how the
OrdersEditorView
and
OrdersToolBar
resolved from the container to bind them to their respective regions.
public class OrderModule : IModule { public void Initialize() { this.container.RegisterType<IOrdersRepository, OrdersRepository>(new ContainerControlledLifetimeManager());
The
OrdersEditorPresentationModel
constructor contains the following dependencies (order repository and order command proxy), which are entered when it is resolved.
public OrdersEditorPresentationModel(IOrdersRepository ordersRepository, OrdersCommandProxy commandProxy) { this.ordersRepository = ordersRepository; this.commandProxy = commandProxy;
In addition to embedding in the constructor, as shown in the previous example, Unity can also embed dependencies in properties. Any properties to which the
[Dependency]
attribute is applied are automatically resolved and implemented when the object is resolved. If the property is marked with the
OptionalDependency
attribute, then if it is impossible to resolve the dependency, the property is assigned a
null
and no exception is thrown.
Resolving instances in MEF
The following code example shows how
Bootstrapper
in
Modularity for Silverlight with MEF QuickStart gets a shell instance. Instead of requesting a specific type, the code might request an instance of the interface.
protected override DependencyObject CreateShell() { return this.Container.GetExportedValue<Shell>(); }
In any class that MEF resolves, you can also use injection into the constructor, as shown in the following code example from
ModuleA
in
Modularity for Silverlight with MEF QuickStart , in which
ILoggerFacade
and
IModuleTracker
.
[ImportingConstructor] public ModuleA(ILoggerFacade logger, IModuleTracker moduleTracker) { if (logger == null) { throw new ArgumentNullException("logger"); } if (moduleTracker == null) { throw new ArgumentNullException("moduleTracker"); } this.logger = logger; this.moduleTracker = moduleTracker; this.moduleTracker.RecordModuleConstructed(WellKnownModuleNames.ModuleA); }
On the other hand, you can use property injection, as shown in the
ModuleTracker
class from
Modularity for Silverlight with MEF QuickStart , which has an instance of
ILoggerFacade
being implemented.
[Export(typeof(IModuleTracker))] public class ModuleTracker : IModuleTracker {
The note
In Silverlight, the properties and fields being imported should be publicly available.
Using dependency injection containers and services in Prism
Dependency injection containers are used to satisfy dependencies between components. Satisfying these dependencies usually includes registration and resolution. Prism provides support for Unity and MEF containers, but does not depend on them. Since the library has access to the container through the
IServiceLocator
interface, the container can be easily replaced. To do this, you must implement the
IServiceLocator
interface. Usually, if you replace a container, you will also need to write your own container-specific loader. The
IServiceLocator
interface
IServiceLocator
defined in the
Common Service Locator Library . This is an open source project to provide an abstraction of IoC containers (Inversion of Control), such as dependency injection containers, and service locators. The purpose of using this library is to use IoC and Service Location, without providing a specific container implementation.
Prism library provides
UnityServiceLocatorAdapter
and
MefServiceLocatorAdapter
. Both adapters implement the
ISeviceLocator
interface, extending the type of
ServiceLocatorImplBase
. The following illustration shows the class hierarchy.

Although Prism does not reference or rely on a specific container, it is typical for an application to use a very specific DI container. This means that it is reasonable for an application to refer to a specific container, but the Prism library does not reference the container directly. For example, the
Stock Trader RI app and several of QuickStarts use Unity as the container. Other examples and QuickStarts use MEF.
IServiceLocator
The following code shows the
IServiceLocator
interface and its methods.
public interface IServiceLocator : IServiceProvider { object GetInstance(Type serviceType); object GetInstance(Type serviceType, string key); IEnumerable<object> GetAllInstances(Type serviceType); TService GetInstance<TService>(); TService GetInstance<TService>(string key); IEnumerable<TService> GetAllInstances<TService>(); }
The Service Locator extends the Prism library with extension methods, shown in the following code. You can see that
IServiceLocator
used only for permission, not for registration.
public static class ServiceLocatorExtensions { public static object TryResolve(this IServiceLocator locator, Type type) { try { return locator.GetInstance(type); } catch (ActivationException) { return null; } } public static T TryResolve<T>(this IServiceLocator locator) where T: class { return locator.TryResolve(typeof(T)) as T; } }
The
TryResolve
extension
TryResolve
, which the Unity container does not support, returns an instance of the type that must be enabled if it was registered, otherwise it returns
null
.
ModuleInitializer
uses the
IServiceLocator
to resolve module dependencies during its loading, as shown in the following code examples.
IModule moduleInstance = null; try { moduleInstance = this.CreateModule(moduleInfo); moduleInstance.Initialize(); } ...
protected virtual IModule CreateModule(string typeName) { Type moduleType = Type.GetType(typeName); if (moduleType == null) { throw new ModuleInitializeException(string.Format(CultureInfo.CurrentCulture, Properties.Resources.FailedToGetType, typeName)); } return (IModule)this.serviceLocator.GetInstance(moduleType); }
Considerations for using IServiceLocator
IServiceLocator
not intended to be used as a general purpose container. Containers may have different usage semantics, which often affect the choice of container. Taking this into account,
Stock Trader RI uses the dependency injection container directly instead of using the
IServiceLocator
. This is the recommended approach when developing applications.
In the following situations, the use of
IServiceLocator
is appropriate:
- You are an independent software provider (ISV) that develops a third-party service that must support various containers.
- You are developing a service that will be used in an organization that uses different containers.
Additional Information
For information related to DI containers, see: