New Posts New Posts RSS Feed: Composition issue on Shared instance
  FAQ FAQ  Forum Search   Calendar   Register Register  Login Login

Composition issue on Shared instance

 Post Reply Post Reply Page  12>
Author
Walid View Drop Down
Senior Member
Senior Member
Avatar

Joined: 14-Nov-2010
Posts: 161
Post Options Post Options   Quote Walid Quote  Post ReplyReply Direct Link To This Post Topic: Composition issue on Shared instance
    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,


Back to Top
mgood View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 18-Nov-2010
Location: Emeryville, CA
Posts: 583
Post Options Post Options   Quote mgood Quote  Post ReplyReply Direct Link To This Post 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.
Back to Top
Walid View Drop Down
Senior Member
Senior Member
Avatar

Joined: 14-Nov-2010
Posts: 161
Post Options Post Options   Quote Walid Quote  Post ReplyReply Direct Link To This Post 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 ?

Back to Top
mgood View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 18-Nov-2010
Location: Emeryville, CA
Posts: 583
Post Options Post Options   Quote mgood Quote  Post ReplyReply Direct Link To This Post Posted: 13-Sep-2012 at 12:49am
Here you go: 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.
Back to Top
Walid View Drop Down
Senior Member
Senior Member
Avatar

Joined: 14-Nov-2010
Posts: 161
Post Options Post Options   Quote Walid Quote  Post ReplyReply Direct Link To This Post 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;
        }
Back to Top
mgood View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 18-Nov-2010
Location: Emeryville, CA
Posts: 583
Post Options Post Options   Quote mgood Quote  Post ReplyReply Direct Link To This Post Posted: 13-Sep-2012 at 1:53am
Ah! Looks like you found a bug in MefContrib. Good to know.
Back to Top
Walid View Drop Down
Senior Member
Senior Member
Avatar

Joined: 14-Nov-2010
Posts: 161
Post Options Post Options   Quote Walid Quote  Post ReplyReply Direct Link To This Post Posted: 13-Sep-2012 at 2:30am
yes and unfortunatly the project seems dead.
Back to Top
stevef View Drop Down
Newbie
Newbie
Avatar

Joined: 15-Jul-2011
Location: NY
Posts: 9
Post Options Post Options   Quote stevef Quote  Post ReplyReply Direct Link To This Post 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??
Back to Top
mgood View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 18-Nov-2010
Location: Emeryville, CA
Posts: 583
Post Options Post Options   Quote mgood Quote  Post ReplyReply Direct Link To This Post 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.
Back to Top
stevef View Drop Down
Newbie
Newbie
Avatar

Joined: 15-Jul-2011
Location: NY
Posts: 9
Post Options Post Options   Quote stevef Quote  Post ReplyReply Direct Link To This Post 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.
Back to Top
cefernan View Drop Down
Groupie
Groupie


Joined: 13-Jul-2012
Posts: 70
Post Options Post Options   Quote cefernan Quote  Post ReplyReply Direct Link To This Post 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.
Back to Top
cefernan View Drop Down
Groupie
Groupie


Joined: 13-Jul-2012
Posts: 70
Post Options Post Options   Quote cefernan Quote  Post ReplyReply Direct Link To This Post Posted: 29-Oct-2012 at 12:51pm
I found it.

IdeaBlade.Core.Composition.CompositionHost.Instance.Container

Thanks
Back to Top
mgood View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 18-Nov-2010
Location: Emeryville, CA
Posts: 583
Post Options Post Options   Quote mgood Quote  Post ReplyReply Direct Link To This Post 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.
Back to Top
cefernan View Drop Down
Groupie
Groupie


Joined: 13-Jul-2012
Posts: 70
Post Options Post Options   Quote cefernan Quote  Post ReplyReply Direct Link To This Post 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.


Edited by cefernan - 30-Oct-2012 at 9:54am
Back to Top
mgood View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 18-Nov-2010
Location: Emeryville, CA
Posts: 583
Post Options Post Options   Quote mgood Quote  Post ReplyReply Direct Link To This Post 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.
Back to Top
cefernan View Drop Down
Groupie
Groupie


Joined: 13-Jul-2012
Posts: 70
Post Options Post Options   Quote cefernan Quote  Post ReplyReply Direct Link To This Post Posted: 31-Oct-2012 at 3:41am
Great, I'll talk with them.

Thank you so much.
Back to Top
 Post Reply Post Reply Page  12>

Forum Jump Forum Permissions View Drop Down