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
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.
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;
}
}
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.
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
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.
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.
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?
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