Print Page | Close Window

Composition issue on Shared instance

Printed From: IdeaBlade
Category: Cocktail
Forum Name: Community Forum
Forum Discription: A professional application framework using Caliburn.Micro and DevForce
URL: http://www.ideablade.com/forum/forum_posts.asp?TID=3625
Printed Date: 25-Apr-2024 at 7:42am


Topic: Composition issue on Shared instance
Posted By: Walid
Subject: Composition issue on Shared instance
Date Posted: 12-Sep-2012 at 3:58pm
Hi,

I have an issue with a service exported as Shared :

[Export(typeof(IContextRequirementSelection))]
[ExportMetadata("ContextName""Company")]
[PartCreationPolicy(CreationPolicy.Shared)]

This service is located  in a XAP which is dynamically loaded once the user login.


In the main application, I have a usercontrol which has an import on all the service implementing IContextRequirementSelection

[ImportMany(AllowRecomposition = true)] 
public IEnumerable<Lazy<IContextRequirementSelectionIContextRequirementMetadata>> ContextSelections;
Once the user login, the collection gets populated and when I access to the element with the metadata "Company", the instance is created.
Now, an action in my application load another XAP which also need to use this shared service.
I use the same syntaxe as the one above and MEF gives me a NEW instance of the service with the metadata "Company"

After reading a little bit on this subject, the only possibility seems to be if a new MEF container is created by the application.  
To try to confirm it, I changed for the test purpose the references in my application to add a reference to the assembly hosting the exported service.
Then, when I run the application, both usercontrols share the same instance as expected !

Is this a problem in cocktail or devforce ?

regards,





Replies:
Posted By: mgood
Date Posted: 12-Sep-2012 at 10:45pm
Neither. I believe this is a problem in your application. I recreated the scenario you are describing here and I can't reproduce this issue. The only thing that would explain this that I can think of is if your dynamically loaded XAPs instantiate additional bootstrappers. The bootstrapper creates the MEF container, so if you have more than one bootstrapper, each one creates a new container. You should have only one bootstrapper, otherwise bad things will happen if each XAP attempts to bootstrap the appliction all over again.


Posted By: Walid
Date Posted: 13-Sep-2012 at 12:17am
I have only one bootstrapper in the application.
I checked if the Harness assembly was by mistake referenced by some modules and it isn't.

Would you mind sharing your test project so I can see what is different with my application (maybe I wasn't clear in my description) and I could modify it to try to reproduce the issue ?



Posted By: mgood
Date Posted: 13-Sep-2012 at 12:49am
Here you go: http://sdrv.ms/QVuo6S - http://sdrv.ms/QVuo6S
When you start it you should get "Service not available" and two buttons. Click the "Load Service" button. This loads the second XAP which contains the Service and you should see the service creation date/time above the button. Wait a few seconds and then click "Load Content". That loads the third assembly which contains a ViewModel that gets composed into the first ViewModel and that also gets the Service injected and then displays the service creation date/time in the View. You should see the exact same date/time now in both places indicating that the second VM got the same service injected as the first VM.


Posted By: Walid
Date Posted: 13-Sep-2012 at 1:42am
I was able to reproduce it with some code change.




My bootstrapper is using MefContrib as you do in Temphire for "auto registering" any new instance to the event aggregator
So if you add those lines to the bootstrapper the issue is there !

I guess I will have to get rid of this librairy and manually register to the event aggregator in my screens.

        protected override ComposablePartCatalog PrepareCompositionCatalog()
        {
            InterceptionConfiguration cfg = new InterceptionConfiguration().AddInterceptor(this);
            return new InterceptingCatalog(Composition.Catalog, cfg);
        }
 
        private static void SubscribeToEventAggregator(object instance)
        {
            if (!(instance is IHandle)) return;
 
            LogFns.DebugWriteLine(string.Format("Automatically subscribing instance of {0} to EventAggregator.", instance.GetType().Name));
            EventFns.Subscribe(instance);
        }
 
        object IExportedValueInterceptor.Intercept(object value)
        {
            SubscribeToEventAggregator(value);
            return value;
        }


Posted By: mgood
Date Posted: 13-Sep-2012 at 1:53am
Ah! Looks like you found a bug in MefContrib. Good to know.


Posted By: Walid
Date Posted: 13-Sep-2012 at 2:30am
yes and unfortunatly the project seems dead.


Posted By: stevef
Date Posted: 22-Sep-2012 at 5:16pm
Marcel,
I have a related issue, so I thought I'd continue on this thread.
I have a console app that makes use of DF, Cocktail, and MEF.

I have an interface called IStatusUpdater defined in an external DLL.
In order to resolve references to this (and other) external references, I have a Compose() method called from the main:

       private void Compose()
        {
            var execAssembly = System.Reflection.Assembly.GetExecutingAssembly();
            var catalog = new AssemblyCatalog(execAssembly);
            var curPath = System.IO.Path.GetDirectoryName(execAssembly.Location);
            var dirCatalog = new DirectoryCatalog(curPath,"*.dll");
            var aggCatalog = new AggregateCatalog();
            aggCatalog.Catalogs.Add(catalog);
            aggCatalog.Catalogs.Add(dirCatalog);
            var container = new CompositionContainer(aggCatalog);
           
            container.ComposeParts(this);
        }


I have some other classes that have an importing constructor, for example:

  [ImportingConstructor]
        public ProjectCreator(IEntityManagerProvider<POEntities> emp, IStatusUpdater statusUpdater, IOptimizer optimizer)
        {
            _emp = emp;
            _statusUpdater = statusUpdater;
            _optimizer = optimizer;
        }


In my main class, I also have a property:

        [Import]
        public IStatusUpdater StatusUpdater { get; set; }


I have one class that implements and exports IStatusUpdater, having the following declaration, and constructor:

    [Export(typeof(IStatusUpdater))]
    public class StatusUpdater : IStatusUpdater
    {
        private readonly IEntityManagerProvider<POEntities> _emp;
        private List<MyTextListener> _myListeners;

        [ImportingConstructor]
        public StatusUpdater([Import(RequiredCreationPolicy = CreationPolicy.NonShared)]
                                IEntityManagerProvider<POEntities> emp)
        {
            _emp = emp;
            em.AuthorizedThreadId = null;
            _myListeners = new List<MyTextListener>();
        }



Everything works just fine this way.

However, I needed to change the way StatusUpdater was disposed of in the main class, so I changed it from a property with an [Import], to:

using (var StatusUpdater = Composition.GetInstance<IStatusUpdater>(CreationPolicy.Shared)) {...


Once I did that, however, I started getting 2 instantiations of my StatusUpdater class, the first for the importing constructor of the other classes, and the second from my call to Composition.GetInstance.

Why is GetInstance not sharing the already instantiated instance??


Posted By: mgood
Date Posted: 22-Sep-2012 at 10:23pm
First of all, Cocktail is not designed for console applications, so use it at your own risk.
The problem you are having here is that Cocktail creates and owns it's own CompositionContainer. It knows nothing about the CompositionContainer you are creating in your Compose method, so when you call Composition.GetInstance.... the instance comes from the container that Cocktail created and not the container you created in Compose.  
 
If anything you should use Composition.Container throughout your application and not create your own container.


Posted By: stevef
Date Posted: 23-Sep-2012 at 8:33am
Ah, wasn't aware of that.  Ok, will do.
BTW, have had no issues with Cocktail in this app, though it doesn't do much other than some compute-intensive calculations, with just a smattering of console output.


Posted By: cefernan
Date Posted: 29-Oct-2012 at 12:38pm
Marcel,

How can I use Composition.Container? I've tried to do that but Container doesn't appear in this context.


Posted By: cefernan
Date Posted: 29-Oct-2012 at 12:51pm
I found it.

IdeaBlade.Core.Composition.CompositionHost.Instance.Container

Thanks


Posted By: mgood
Date Posted: 29-Oct-2012 at 1:34pm
Originally posted by cefernan

I found it.

IdeaBlade.Core.Composition.CompositionHost.Instance.Container

Thanks

That's the container DevForce uses internally. Cocktail doesn't use this container. It uses it's own container. 

Are you using Cocktail 2012 by any chance? In Cocktail 2012 the container is no longer publicly accessible. This is due to the fact that the Composition API is now implementation independent. The container is an implementation detail specific to the underlying IoC technology. 

By implementing ICompositionProvider and setting it with Compositon.SetProvider you can supply your own container to Cocktail. Your Bootstrapper should extend CocktailBootstrapper instead of CocktailMefBootstrapper so you can configure the composition yourself. You can follow what CocktailMefBootstrapper does. As you can see from the source code it configures composition with the MefCompositionProvider, the Cocktail internal implementation of ICompositionProvider.


Posted By: cefernan
Date Posted: 30-Oct-2012 at 9:53am
Yes, we are using Cocktail 2012 in the Silverlight side.

We are developing an application in Silverlight and we intend to expose services through OData. We will use the same features in different technologies. So, we decided to put our main business logic in the DomainModel project. The better approach that we found was that, because we don't want to duplicate code. We are using MEF in the DomainModel logic, because of this we need to start a MEF container in Silverlight/OData and pass them to the DomainModel.

OData:
        private void Compose()
        {
            var catalog = new AggregateCatalog();
            catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
            catalog.Catalogs.Add(new AssemblyCatalog(Assembly.Load("DomainModel")));
            var container = new CompositionContainer(catalog);
            container.ComposeParts(this);

            StaticContainer.DefaultContainer = container;
        }

Silverlight:
    public class AppBootstrapper : BootstrapperBase<ShellViewModel>
    {
        protected override void PrepareCompositionContainer(CompositionBatch batch)
        {
            base.PrepareCompositionContainer(batch);

            StaticContainer.DefaultContainer = IdeaBlade.Core.Composition.CompositionHost.Instance.Container;
        }
    }

Do you have any suggestion? Do you see any problem in the strategy? Is there any problem in use DevForce container?

Regards.


Posted By: mgood
Date Posted: 30-Oct-2012 at 5:18pm
I wouldn't use MEF in the domain model. Using IoC in the domain model is a pretty frowned upon practice. To do proper dependency injection, the entities would have to be owned by the container, but that doesn't work, because they are owned by the EntityManager or OData, so what you are doing is pulling dependencies from a container, which is quite a bit of an anti-pattern.
 
This non-standard use of Cocktail goes beyond the free support provided on this forum, but our Professional Services team will be more than happy to advise you on a proper approach and architecture for your situation. I'll have my sales team follow up.


Posted By: cefernan
Date Posted: 31-Oct-2012 at 3:41am
Great, I'll talk with them.

Thank you so much.


Posted By: KitKat
Date Posted: 22-Mar-2013 at 6:32am
Good morning Marcel,
Any chance to get the test program you posted a link to above.  As we are having a problem where the exports in our external modules are not being added to the composition.

Thank you,
Katerina


Posted By: mgood
Date Posted: 22-Mar-2013 at 9:18am
Honestly, I don't know where to look for it anymore. Looks like I cleaned up my SkyDrive. I probably deleted the whole sample.


Posted By: KitKat
Date Posted: 22-Mar-2013 at 9:34am
Any advice then, we have classes that are Exported in an external module, that are not getting imported to classes in the main xap.  We are using Composition.AddXapAsync.

Thank you,
Kat


Posted By: mgood
Date Posted: 22-Mar-2013 at 9:49am
This is most often the case if one of their direct or indirect dependencies cannot be resolved. I would make sure first that the dependencies are actually in the catalog. Are you using .NET 4.5? If so you should see quite detailed diagnostic messages from MEF in your output window pointing you towards the issue.



Print Page | Close Window