Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MEF composition issues, multithreading

I have a following code:

public class Temp<T, TMetadata>
{
    [ImportMany]
    private IEnumerable<Lazy<T, TMetadata>> plugins;

    public Temp(string path)
    {
        AggregateCatalog aggregateCatalog = new AggregateCatalog();
        aggregateCatalog.Catalogs.Add(new DirectoryCatalog(path));
        CompositionContainer container = new CompositionContainer(aggregateCatalog);
        container.ComposeParts(this);
    }

    public T GetPlugin(Predicate<TMetadata> predicate)
    {
        Lazy<T, TMetadata> pluginInfo;

        try
        {
            pluginInfo = plugins.SingleOrDefault(p => predicate(p.Metadata));
        }
        catch
        {
            // throw some exception
        }

        if (pluginInfo == null)
        {
            // throw some exception
        }

        return Clone(pluginInfo.Value); // -> this produces errors
    }
}

I have a single object of Temp and I call GetPlugin() from multiple threads. Sometimes I face strange composition errors, which I didn't find a way to reproduce. For example:

"System.InvalidOperationException: Stack empty.
    at System.Collections.Generic.Stack`1.Pop()
    at System.ComponentModel.Composition.Hosting.ImportEngine.TrySatisfyImports(PartManager partManager, ComposablePart part, Boolean shouldTrackImports)
    at System.ComponentModel.Composition.Hosting.ImportEngine.SatisfyImports(ComposablePart part)
    at System.ComponentModel.Composition.Hosting.CompositionServices.GetExportedValueFromComposedPart(ImportEngine engine, ComposablePart part, ExportDefinition definition)
    at System.ComponentModel.Composition.Hosting.CatalogExportProvider.GetExportedValue(CatalogPart part, ExportDefinition export, Boolean isSharedPart)
    at System.ComponentModel.Composition.ExportServices.GetCastedExportedValue[T](Export export)
    at System.Lazy`1.CreateValue()
    at System.Lazy`1.LazyInitValue()
    at Temp`2.GetPlugin(Predicate`1 predicate)..."

What could be a reason and how to cure this code?

like image 317
Qué Padre Avatar asked Jan 16 '14 07:01

Qué Padre


1 Answers

The CompositionContainer class has a little-known constructor which accepts an isThreadSafe parameter (which defaults to false for performance reasons). If you'll create your container with this value set to true, I believe your problem will be solved:

CompositionContainer container = new CompositionContainer(aggregateCatalog, true);

On a side note, unrelated to the original question, instead of calling Clone() on the plugin, you can use an export factory instead - this way you don't have to implement your own clone method, as MEF will create a new instance for you.

like image 181
Adi Lester Avatar answered Oct 19 '22 04:10

Adi Lester