Thread Safety Of CompositionContainer

Oct 24, 2010 at 6:40 AM

I am new to MEF and I have a question regarding the CompositionContainer. I am playing with an ASP.NET app where I would like to use MEF to resolve the dependancies. The question is around how to store the container itself? The documentation for the CompositionContainer (http://msdn.microsoft.com/en-us/library/dd833553.aspx) says that instance members of the container are not thread safe. Does this mean that for each and every ASP.NET web app request, we have to create the catalogs etc and then store the container in the HttpContext to make the container thread safe?  If not is there any way to store a static reference of the container and call the ComposeParts on it.

My initial attempt at creating a static reference of the container and then calling ComposeParts failed due to a System.InvalidOperationException with an error message: "Currently composing another batch in this ComposablePartExportProvider. Only one batch can be composed at a time." (The error message is a .NET error message) On tracing the issue I found that two different threads were trying to compose objects simultaneously. Is there any way to get around this?

Thanks

Oct 24, 2010 at 8:42 AM
Edited Oct 24, 2010 at 8:43 AM

From the documentation you linked:

A CompositionContainer object that can be accessed from multiple threads must be constructed with the isThreadSafe parameter set to true. Performance will be slightly slower when isThreadSafe is true, so false is preferred in single-threaded scenarios. The default is false.

The mentioned isThreadSafe parameter can be set with one of the constructors.

Oct 24, 2010 at 2:55 PM

THanks for the reply wcoenen. I tried setting the 'isThreadSafe' parameter to true. But I still get the "System.InvalidOperationException : Currently composing another batch in this ComposablePartExportProvider. Only one batch can be composed at a time." error.

Oct 26, 2010 at 1:06 AM

Could you share a bit of how your code looks like?

Oct 27, 2010 at 2:47 AM
Edited Oct 27, 2010 at 2:51 AM
The code below is run in the static contructor of my wrapper class for the container.

######## Private variables #####################

 

private static AggregateCatalog _AggregateCatalog = new AggregateCatalog ();

 

 

 

private static CompositionContainer _Container = null ;

 

######## Static Constructor ################################################ 
	 AssemblyCatalog defaultCatalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
            _AggregateCatalog.Catalogs.Add(defaultCatalog);
            CatalogExportProvider defaultCatalogProvider = new CatalogExportProvider(_AggregateCatalog);

            string overridePath = AppDomain.CurrentDomain.BaseDirectory + "Extensions";

            //add override only if the directory exists
            if (Directory.Exists(overridePath))
            {
                //Create the override catalog and compose the container.
                DirectoryCatalog overrideCatalog = new DirectoryCatalog(AppDomain.CurrentDomain.BaseDirectory + "Extensions");
                CatalogExportProvider overExpProvider = new CatalogExportProvider(overrideCatalog);
                _Container = new CompositionContainer(overrideCatalog, true, defaultCatalogProvider);
            }
            else
                _Container = new CompositionContainer(_AggregateCatalog, true);

            //Set the source for the default catalog provider.
            defaultCatalogProvider.SourceProvider = _Container;
#############################################################################################
This wrapper class then exposes the ComposeParts of the _Container variable.
I apologize for the formatting mess. 
Oct 27, 2010 at 8:47 AM
You create two CatalogExportProvider instances. Use the constructor which takes a isThreadSafe parameter.

(Though you don't actually seem to use the overExpProvider instance; probably because the CompositionContainer constructor
which takes a isThreadSafe parameter forced you to pass a ComposablePartCatalog as the first argument. You can just pass an empty TypeCatalog there.)