Back to Home

Silverlight application migration from Prism 2.2 to Prism 4 MEF edition

Silverlight · Prism · MEF · Unity · container · mvvm · ioc · inversion of control · migration

Silverlight application migration from Prism 2.2 to Prism 4 MEF edition

    The time is right when the development of the Prism 4 library , designed to create modular and flexible Silverlight and WPF applications , will be announced . The new version has a large number of changes, improvements and innovations. One of the main innovations is the addition of MEF support as a container (in the previous version only Unity container was supported).

    In this article, I would like to address the issue of migration from Prism 2.2 to Prism 4, taking into account the transition to using the MEF container instead of Unity.

    Changes in naming and composition of assemblies


    The development team decided to change the assembly names and namespace names:
    • Microsoft.Practices.Composite changes to Microsoft.Practices.Prism;
    • The Microsoft.Practices.Composite.Presentation assembly becomes part of the Microsoft.Practices.Prism assembly;
    • Namespace Microsoft.Practices.Composite.Presentation is replaced by Microsoft.Practices.Prism;
    • Microsoft.Practices.Composite.UnityExtensions changes to Microsoft.Practices.Prism.UnityExtensions;
    • The assembly Microsoft.Practices.Prism.MefExtensions and the corresponding namespace are added;
    • The assembly Microsoft.Practices.Prism.Interactivity is added;

    Changing the assembly list


    During migration, the following changes should be made to the assembly list of each project:
    • - Microsoft.Practices.Unity.dll
    • - Microsoft.Practices.Composite.dll
    • - Microsoft.Practices.Composite.Presentation.dll (if present)
    • + Microsoft.Practices.Prism.dll
    • + System.ComponentModel.Composition.dll
    • + System.ComponentModel.Composition.Initialization.dll, if one of the classes of this library is used in the project
    • + Microsoft.Practices.Prism.MefExtensions.dll if the project contains a module

    Replacing the assembly, you should change the namespace. This can be done using the standard Find and Replace dialog (Ctrl-H) by entering Microsoft.Practices.Composition and Microsoft.Practices.Prism in the corresponding fields. Given that the assembly Microsoft.Practices.Composite.Presentation.dll no longer exists separately, after the previous replacement, you should replace Microsoft.Practices.Prism.Presentation with Microsoft.Practices.Prism

    Note:
    Do not forget to change namespace names not only in .cs files, but also in .xaml and .config (if required).

    Replacing Unity Container


    MEF container has certain advantages and disadvantages. They should be known before deciding to migrate to the MEF container.

    Disadvantages:
    • Cannot instantiate a class without registering in a container
    • Cannot work with open generic classes

    Benefits:
    • Can import parts from assembly folders
    • Native support for XAP files
    • Reposition Support
    • Attribute Inheritance Support [Export]
    • The library is an integral part of the .Net framework and is included in the client part of the framework, which means that assemblies of this library will not be included in the XAP file.


    Getting rid of the Unity container should begin by removing all occurrences IUnityContaineras a class constructor or public property. If other parameters from the container (constructor injection) are passed to the constructor, then we leave them, and we mark the constructor with an attribute [ImportingConstructor].

    Example:
    //Prism 2.2
    public class SampleClass
    {
      private readonly IRegionManager regionManager;
      private readonly IUnityContainer container;

      public SampleClass(IUnityContainer container, IRegionManager regionManager)
      {
        this.container = container;
        this.regionManager = regionManager;
      }
    }

    //Prism 4
    [Export(typeof(SampleClass))]
    public class SampleClass
    {
      private readonly IRegionManager regionManager;

      [ImportingConstructor]
      public SampleClass(IRegionManager regionManager)
      {
        this.regionManager = regionManager;
      }
    }

    * This source code was highlighted with Source Code Highlighter.


    Further, all classes that were somehow created through the container should be marked with an attribute [Export](can be used [InheritedExport]for interfaces), and at the same time the registration code of the classes in the container should be deleted.

    The next step is to change the syntax of the modules. Each module must be marked with an attribute (the definition of this attribute is in the Microsoft.Practices.Prism.MefExtensions.dll assembly). Example:[ModuleExport(typeof())]


    //Prism 2.2
    public class SampleModule : IModule
    {
      public void Initialize()
      {
        Register();
      }

      public void Register()
      {
        container.RegisterType();
      }
    }

    //Prism 4
    [ModuleExport(typeof(SampleClass))]
    public class SampleModule : IModule
    {
      public void Initialize()
      {
        ...
      }
    }

    [InheritedExport]
    public interface ISampleViewModel
    {
      ...
    }

    * This source code was highlighted with Source Code Highlighter.

    The next step is to modify Bootstarpper.cs.
    • Class Bootstarappershould be inherited fromMefBootstrapper
    • Change the method CreateShell()and add the methodInitializeShell()
    • The method GetModuleCatalog()should be renamed toCreateModuleCatalog()
    • The method ConfigureAggregateCatalog()should be overridden to include all assemblies that are available from the current assembly.

    Example:
    //Prism 4
    using System.ComponentModel.Composition;
    using System.ComponentModel.Composition.Hosting;

    using Microsoft.Practices.Prism.MefExtensions;
    using Microsoft.Practices.Prism.Regions;
    using Modularity = Microsoft.Practices.Prism.Modularity;

    namespace Sample.Shell
    {
      public class Bootstrapper : MefBootstrapper
      {
        protected override DependencyObject CreateShell()
        {
          return Container.GetExportedValue();
        }

        protected override void InitializeShell()
        {
          base.InitializeShell();
          Application.Current.RootVisual = (ShellView)this.Shell;
        }

        protected override Modularity.IModuleCatalog CreateModuleCatalog()
        {
          return Modularity.ModuleCatalog.CreateFromXaml(new Uri("/Sample.Shell;component/ModulesCatalog.xaml", UriKind.Relative));
        }

        protected override void ConfigureAggregateCatalog()
        {
          base.ConfigureAggregateCatalog();
          AggregateCatalog.Catalogs.Add(new AssemblyCatalog(this.GetType().Assembly));
        }
      }
    }

    * This source code was highlighted with Source Code Highlighter.

    A few more features of the transition to the MEF container.
    • If the parameter is CreationPolicynot specified during import or export, the class will be used as Shared (one instance for all calls)
    • If an instance of a class is registered in the container or registration is performed using ContainerControlledLifetimeManager(), then in this case such a class must be marked as Shared. Otherwise, this class can be instantiated multiple times by specifying an attribute.[Import(RequiredCreationPolicy=CreationPolicy.NonShared)]
    • If, in accordance with the program logic, repeated class instantiation is required, then this should be done using ExportFactory

    Example 1:
    //Prism 2.2
    private void RegisterTypes()
    {
      SampleType sampleType = new SampleType(...);
      Container.RegisterInstance(sampleType);

      Container.RegisterType(new ContainerControlledLifetimeManager());
    }

    //Prism 4
    [Export]
    [PartCreationPolicy(CreationPolicy.Shared)]
    public class SampleType : ISampleType
    {
    ...
    }

    [Export]
    [PartCreationPolicy(CreationPolicy.Shared)]
    public class SingletonSampleType : ISingletonSampleType
    {
    ...
    }

    * This source code was highlighted with Source Code Highlighter.

    Example 2:
    //Prism 2.2
    private void Foo()
    {
      List samples = new List();
      for(int i = 0; i == 10; i++)
      {
        ISampleType sampleTypeInstance = Container.ResolveInstance();
        samples.Add(sampleTypeInstance);
      }
    }

    //Prism 4
    [Import]
    public ExportFactory SampleTypeFactory
    {
      get;
      set;
    }

    private void Foo()
    {
      List samples = new List();
      for(int i = 0; i == 10; i++)
      {
        ISampleType sampleTypeInstance = SampleTypeFactory.CreatExport().Value;
        samples.Add(sampleTypeInstance);
      }
    }

    * This source code was highlighted with Source Code Highlighter.

    Changes to ViewModel


    All ViewModel (or their common ancestor) should be inherited from the class NotificationObject. This class implements the interface INotifyPropertyChangedand implements yet another variant of the method RaisePropertyChanged(), which receives not the string name of the property, but the property itself. It is highly advisable to abandon the old implementation RaisePropertyChanged()and start using the new one.

    Example:
    //Prism 2.2
    public class SampleViewModel : ISampleViewModel
    {
      private SamplePropertyType sampleProperty;
      public SamplePropertyType SampleProperty
      {
        get
        {
          return sampleProperty;
        }
        set
        {
          sampleProperty = value;
          this.RaisePropertyChanged("SampleProperty");
        }
      }
    }

    //Prism 4
    public class SampleViewModel : NotificationObject, ISampleViewModel
    {
      private SamplePropertyType sampleProperty;
      public SamplePropertyType SampleProperty
      {
        get
        {
          return sampleProperty;
        }
        set
        {
          sampleProperty = value;
          this.RaisePropertyChanged(() => SampleProperty);
        }
      }
    }

    * This source code was highlighted with Source Code Highlighter.

    Pitfalls of migration


    Duplicate Assemblies

    It should be noted that if Prism assemblies fall into several XAP files, an exception will be generated when composing the composition.

    Message: Unhandled Error in Silverlight Application Code: 4004 Category: ManagedRuntimeError Message: Microsoft.Practices.Prism.Modularity.ModuleTypeLoadingException: Failed to load type for module . Error was: The composition remains unchanged. The changes were rejected because of the following error (s): The composition produced multiple composition errors, with 13 root causes. The root causes are provided below. Review the CompositionException.Errors property for more detailed information.

    1) Change in exports prevented by non-recomposable import 'Microsoft.Practices.Prism.MefExtensions.Modularity.MefModuleManager.MefXapModuleTypeLoader (ContractName = "Microsoft.Practices.Prism.MefExtensions.Modularity.MefXapModuleTypeLoader. Prism.MefExtensions.Modularity.MefModuleManager '.
    ...

    In order to avoid this situation, you should set the property of all duplicate assemblies CopyLocal=Falseor use the plugin for VisualStudio2010 .

    Features of the module catalog

    When using the module catalog, the fully qualified name of the assembly to which you are referring should be indicated.

    Example:

            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:Modularity="clr-namespace:Microsoft.Practices.Composite.Modularity; assembly=Microsoft.Practices.Composite">
          Ref="SampleModule.xap"
        ModuleName="SampleModule"
        ModuleType="SampleModule.SampleModule, SampleModule, Version=1.0.0.0" />



            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:Modularity="clr-namespace:Microsoft.Practices.Prism.Modularity; assembly=Microsoft.Practices.Prism">
          Ref="SampleModule.xap"
        ModuleName="SampleModule"
        ModuleType="SampleModule.SampleModule, SampleModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />


    * This source code was highlighted with Source Code Highlighter.

    Conclusion


    With the release of the library release, each developer will make their own decision about the possibility of switching to a new version and about changing the container from Unity to MEF. My experience shows that migration does not take much time, is feasible and will allow you to take advantage of the MEF container.

    I wish everyone success in this matter and I believe that my article will be a good help.

    Read Next