Multi-threading issue when using MEF with WCF

Nov 25, 2010 at 5:19 PM

Hi,

I'm trying to get MEF and WCF to play nicely together so that I can use MEF for dependency injection in my WCF services. It works really well most of the time but once in a while the following exception is thrown:

System.InvalidOperationException: Currently composing another batch in this ComposablePartExportProvider. Only one batch can be composed at a time.  
at System.ComponentModel.Composition.Hosting.ComposablePartExportProvider.Compose(CompositionBatch batch) at System.ComponentModel.Composition.Hosting.CompositionContainer.Compose(CompositionBatch batch)   at System.ComponentModel.Composition.CompositionInitializer.SatisfyImports(ComposablePart part) in ...\ComponentModel.Composition.Initialization.Desktop\System\ComponentModel\Composition\CompositionInitializer.cs:line 90 at System.ComponentModel.Composition.CompositionInitializer.SatisfyImports(Object attributedPart) in ...\ComponentModel.Composition.Initialization.Desktop\System\ComponentModel\Composition\CompositionInitializer.cs:line 43 at MyService.MefInstanceProvider.GetInstance(InstanceContext instanceContext) in ...\MefInstanceProvider.cs:line 28   

I use the IInstanceProvider interface to hook up MEF with WCF as detailed here and the CompositionInitializer found here. My implementation of the instance provider:

public class MefInstanceProvider : IInstanceProvider, IContractBehavior
{
   #region IInstanceProvider Members

   public object GetInstance(InstanceContext instanceContext, Message message)
   {
      return this.GetInstance(instanceContext);
   }

   public object GetInstance(InstanceContext instanceContext)
   {
      var serviceType = instanceContext.Host.Description.ServiceType;
     
object service = Activator.CreateInstance(serviceType);
      CompositionInitializer.SatisfyImports(service);

      return service;
   }

   public void ReleaseInstance(InstanceContext instanceContext, object instance)
   {
      var disposable = instance as IDisposable;

      if (disposable != null)
      {
         disposable.Dispose();
       }
   }

   #endregion

   #region IContractBehavior Members

   public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
   {
   }
   
public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
}
   public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
   {
      dispatchRuntime.InstanceProvider = this;
   }

   public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
   {
   }
   
#endregion }

After researching this a bit I found out that MEF isn't thread safe unless you construct the container with the threadSafe constructor parameter set to true. Unfortunately it doesn't seem to help. :-( Another strange thing is that when I'm able to catch the exception in the debugger and then look into the thread window, there is really only one thread that is composing a batch.

My MEF initialization code:

_catalog = new AggregateCatalog();

_catalog.Catalogs.Add(new AutoRefreshingDirectoryCatalog(".\\Addins", "*.dll"));
_catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));

_container = new CompositionContainer(_catalog, true);  // threadSafe = true

CompositionHost.Initialize(_container);

Any ideas anyone?
/Martin
Coordinator
Nov 29, 2010 at 11:07 PM

Even with isThreadSafe=true, not all operations are safe to use on a container from multiple threads.  I don't think SatisfyImports is safe to call.  SatisfyImportsOnce is more likely to be safe but I'm not absolutely sure about it.  Any of the GetExport methods are safe, if you can change your code to use those to get the parts out of the container that would be best.

 

Thanks,

Daniel

Dec 10, 2010 at 1:35 PM

Thanks for the reply. I (hopefully) solved the problem by adding some synchronization logic of my own.

/Martin

Mar 17, 2015 at 3:52 PM
Can you leave the answer for this problem Martin? I have the same problem too, isThreadSafe=true can't solve this problem.