How to set up the Prism bootstrapper - solved

Apr 24, 2010 at 8:18 PM
Edited Apr 27, 2010 at 5:45 PM

[ The original title of this tread was "Instances registered with AddExportedValue are recreated when pulled via Unity" ]

I tried the "Full Unity + MEF Integration scenario" and noticed that singletons registered with AddExportedValue are not retrieved as singletons but recreated when accessed via Unity.

I.e. in the following example resolution of the type "MyStuff" will create a new instance of "MyStuff, although it was registered with AddExportedValue (of CompositionBatch).

Am I doing something wrong, or is this a bug? If a bug, does a workaround/fix exist?

var stuff = new MyStuff(); // stuff.Name = "Name no. 1";
batch.AddExportedValue(stuff);
...

If the "MyStuff" is not annotated with [Export], then every resolution will create a new instance:
var stuff = Container.Resolve<MyStuff>(); // stuff.Name == "Name no. 2";
stuff = Container.Resolve<MyStuff>(); // stuff.Name == "Name no. 3"; NOTE the 3 here

 If I annotate the type "MyStuff" with [Export], the recreation happens, but only for the first resolution:

var stuff = Container.Resolve<MyStuff>(); // stuff.Name == "Name no. 2";
stuff = Container.Resolve<MyStuff>(); // stuff.Name == "Name no. 2"; NOTE the 2 here

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Practices.Composite.UnityExtensions;
using Microsoft.Practices.Unity;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition;
using MefContrib.Integration.Unity;
using Microsoft.Practices.Composite.Modularity;

namespace UnityIntegration01
{
    public partial class App : Application
    {
        public App()
        {
            this.Startup += this.Application_Startup;     
            InitializeComponent();
        }

        private void Application_Startup(object sender, StartupEventArgs e)
        {
            var bootstrapper = new Bootstrapper();
            bootstrapper.Run();

            var stuff = bootstrapper.Container.Resolve<MyStuff>(); // stuff.Name == "Name no. 2";
            stuff = bootstrapper.Container.Resolve<MyStuff>(); // stuff.Name == "Name no. 3";
            stuff = bootstrapper.Container.Resolve<MyStuff>(); // stuff.Name == "Name no. 4";

            this.RootVisual = new MainPage();
        }     
    }

    public class Bootstrapper : UnityBootstrapper
    {
        public Bootstrapper()
        {
            RootCatalog = new AggregateCatalog();
        }

        AggregateCatalog RootCatalog { get; set; }

        protected override IUnityContainer CreateContainer()
        {
            var container = new UnityContainer();

            // MEF - Unity integration
            // See http://mefcontrib.codeplex.com/wikipage?title=Unity%20Integration&referringTitle=Documentation%20%26%20Features
            //
            this.RootCatalog.Catalogs.Add(new DeploymentCatalog());
            container.RegisterCatalog(this.RootCatalog);

            return container;
        }

        protected override void ConfigureContainer()
        {
            base.ConfigureContainer();

            // EDITED: Piotr solved my issue: Acquire the container via the UnityContainer in order to make this scenario work correctly. 
            // var container = Container.Configure<CompositionIntegration>().CompositionContainer;           

            // EDITED: Don't create a new MEF-container.

            var container = new CompositionContainer(this.RootCatalog);

            try
            {
                CompositionBatch batch = new CompositionBatch();

                var stuff = new MyStuff(); // stuff.Name = "Name no. 1";
                batch.AddExportedValue(stuff);

                container.Compose(batch);

                CompositionHost.Initialize(container);
            }
            catch (CompositionException compositionException)
            {
                throw;
            }
        }

        protected override IModuleCatalog GetModuleCatalog()
        {
            return new ModuleCatalog();
        }

        protected override DependencyObject CreateShell()
        {
            return null;
        }
    }

    //[Export]
    //[PartCreationPolicy(CreationPolicy.Shared)]
    public class MyStuff
    {
        static int counter;
        public MyStuff()
        {
            counter++;
            this.Name = "Item no. " + counter.ToString(); ;
        }
        public string Name { get; set; }
    }
}


Regards,
Kasimier

 

 

Apr 24, 2010 at 8:54 PM

I  think I must be doing something wrong, because the batch.AddExportedValue(stuff) doesn't seem to have any effect.

The added instance is not visible to the integration mechanism this way, right?

How to register a singleton instance correctly?

Regards,

Kasimier

Coordinator
Apr 25, 2010 at 1:15 PM

Yes, to register a manually created singleton, you can use AddExportedValue and CompositionBatch. However, there is a small bug in your code. In the ConfigureContainer method, you create NEW instance of the CompositionContainer and you further use THIS instance with the batch. Note, however, that the Unity knows NOTHING about your own CompositionContainer instance. Hence, when you pull MyStuff from Unity, it cannot be resolved by MEF since the CompositionContainer used internally by Unity is NOT the same instance your batch was composed with. Hopefuly, your code can be easily fixed =) Instead of creating your own CompositionContainer in the ConfigureContainer method, pull it from the integration layer like this:

 

var container = Container.Configure<CompositionIntegration>().CompositionContainer;

Hope this helps.

Piotr

 

Apr 26, 2010 at 3:16 PM

Thanks Piotr. This solved my issue. Works fine now :-)

I've added some comments to my original post in order to make the readers aware of the solution.

Regards,

Kasimier