CslaContrib Mef Error "currently composing another batch..."

CslaContrib Mef Error "currently composing another batch..."

Old forum URL: forums.lhotka.net/forums/t/11706.aspx


c_manboy posted on Friday, November 16, 2012

I'm using csla 4.3,  cslacontrib.mef and cslacontrib.caliburn.micro.  One of my viewmodels contains a property:

    Private _managerList As IEnumerable(Of Library.Main.ManagerInfo)
    Public ReadOnly Property ManagerList As IEnumerable(Of Library.Main.ManagerInfo)
        Get
            Return Library.Main.ManagerInfoList.GetAll
        End Get
    End Property

But I receive the following error:

Currently composing another batch in this ComposablePartExportProvider. Only one batch can be composed at a time.

When I remove the GetAll call, I do not receive the error.

Edit: I'm basing on the MefRepository sample and have the following in all of my business classes (substituting the correct interface name, of course):

        <NonSerialized(), NotUndoable()>
        Private _dataAccess As IManagerDal
 
        <Import(GetType(IManagerDal))>
        Private Property DataAccess As IManagerDal
            Get
                Return _dataAccess
            End Get
            Set(value As IManagerDal)
                _dataAccess = value
            End Set
        End Property

c_manboy replied on Friday, November 16, 2012

In cslacontrib.Mef, I changed all of the inject methods from Ioc.Container.ComposeParts(this) to Ioc.Container.SatisfyImportsOnce(this). 

It works, but I dont' know why and what the ramifications are.  I'm going to continue researching this, but if someone could fill me in, that would be very appreciated.

Thanks.

 

JonnyBee replied on Saturday, November 17, 2012

Obviosly the Mef Container is not thread safe.

So you may also update the Ioc.cs Container property getter to:

public static CompositionContainer Container
    {
      get
      {
        //create and configure container if one does not yet exist
        if (_container == null)
        {
          lock (_syncRoot)
          {
            if (_container == null)
            {
              Debug.Write("Start configuring MEF Container");

              //create container
              var catalog = new AggregateCatalog();
              catalog.Catalogs.Add(new DirectoryCatalog("."));
              catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
              _container = new CompositionContainer(catalog, true);  // necessary to make container threadsafe
              _container.ComposeParts();

              Debug.Write("End configuring MEF Container");
            }
          }
        }
        return _container;
      }
    }

c_manboy replied on Saturday, November 17, 2012

Thanks for the reply, but I have already tried that and that change alone does not resolve the issue.  I am still required to change each class Inject method.  Interestingly (to me), if I make no changes to the code and put a break point in the Container method and F8 (step into) through the all of the calls, I do not receive the error.  Not quite sure what that means, but thought it was interesting.

Reading up on the difference between composeparts and satisfyimportsonce, it seems that the only disadvantage of using satisfyimports once is that I will not be able to recompose my business classes.  I do not see that as an issue since the only thing it's supposed to do is import my data access object.  And that doesn't change during execution.

JonnyBee replied on Sunday, November 18, 2012

You're likely hitting a thread race condition, which is why this works when you add a break point.

The other part to remember when using MEF is that most IOC containers default to create "new instance" of an object. MEF does not - it defaults to create a "singelton" - shared object. Hence you can use PartCreationPolicy on either the publisher or consumer to specify creation policy.

http://mef.codeplex.com/wikipage?title=Parts%20Lifetime

"The “shareability” of a part is defined through the CreationPolicy set (class level) using the PartCreationPolicyAttribute. The following values are supported:

This is a useful for scenarios where the “shareability” of a part is relevant for the importer. By default, the RequiredCreationPolicy is set to Any, so Shared and NonShared parts can supply the values.

The following table summarizes the behavior:

- Part.Any Part.Shared Part.NonShared
Import.Any Shared Shared Non Shared
Import.Shared Shared Shared No Match
Import.NonShared Non Shared No Match Non Shared

Note that when both sides define “Any” the result will be a shared part."

Good answer on the difference between ComposeParts and satisfyImportsOnce can be found here:
http://stackoverflow.com/questions/6419319/mef-satisfyimportsonce-vs-composeparts

c_manboy replied on Sunday, November 18, 2012

So how would you approach this scenario?  Should I avoid changing the inject methods to SatisfyImportsOnce and try to address the thread race issue?  I'm not even sure where I would begin with that...unless there's a way to put an "IsBusy" on the container so that methods could "wait" for it, which seems kind of hacky.

Or should I revisit my viewmodel and avoid properties of namevalue lists and readonly cache lists?

I am concerned about using the mef business objects if there is a posibility of future "thread races". 

I'll spend some more time reading the references you provided to see if it sheds some light on this.

sergeyb replied on Monday, November 19, 2012

The unfortunate part about MEF is that it is not thread safe.  The second parameter you specify still does not make it completely thread safe either, and if I remember correctly, documentation indeed says that  You have to wrap all calls to create a container and compose parts inside lock{}.  I ran into this issue about a year ago.  I believe the main problem is that MEF was designed initially to support specific set of use cases on the client, thus thread safety was an after thought IMHO.  

c_manboy replied on Monday, November 19, 2012

Then, is cslacontrib.MEF more of a proof of concept than a recommended way of injecting the data access?  I ask that because the container fails to compose parts when the viewmodel contains a list property which fetches data on initialization (resulting in what I assume to be two simulatneous fetches: the viewmodel's model business object, and the list property's fetch).

Or, is my "fix" (see pervious post about changing the inject methods) an acceptable way of getting around this issue?  Or will that "fix" give me issues later on in the objects lifecycle?

Or, thirdly, should I reconsider adding the list property on my view model and find another way to load the list?

JonnyBee replied on Tuesday, November 20, 2012

We highly recommend to use "use case controller objects" to retrieve all the data needed for a use case / "screen".

The reason behind this is that typically your data access is async and you MUST make sure to have all the drop down lists available at the time that the business objects is databound. So a typical "use case controller" should then be readonly root object that has properties for both business objects and drop down lists and does an async fetch. So when the fetch is completed you are in control of the sequence of when objects gets databound.

See also http://forums.lhotka.net/forums/p/1749/9149.aspx#9149

I will also look into fixing the tread race conditions in CslaContrib.

Copyright (c) Marimer LLC