Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handle ReflectionTypeLoadException during MEF composition

Tags:

c#

mef

I am using a DirectoryCatalog in MEF to satisfy imports in my application. However, there are sometimes obfuscated assemblies in the directory that cause a ReflectionTypeLoadException when I try to compose the catalog.

I know I can get round it by using a separate directory or by using a search filter on the DirectoryCatalog but I want a more general way to solve the problem. Is there some way I can handle the exception and allow composition to continue? Or is there another more general solution?

like image 359
Phil J Pearson Avatar asked Nov 10 '10 12:11

Phil J Pearson


3 Answers

To save others from writing their own implementation of the SafeDirectoryCatalog, here is the one I came up with based upon Wim Coenen's suggestions:

public class SafeDirectoryCatalog : ComposablePartCatalog
{
    private readonly AggregateCatalog _catalog;

    public SafeDirectoryCatalog(string directory)
    {
        var files = Directory.EnumerateFiles(directory, "*.dll", SearchOption.AllDirectories);

        _catalog = new AggregateCatalog();

        foreach (var file in files)
        {
            try
            {
                var asmCat = new AssemblyCatalog(file);

                //Force MEF to load the plugin and figure out if there are any exports
                // good assemblies will not throw the RTLE exception and can be added to the catalog
                if (asmCat.Parts.ToList().Count > 0)
                    _catalog.Catalogs.Add(asmCat);
            }
            catch (ReflectionTypeLoadException)
            {
            }
            catch (BadImageFormatException)
            {
            }
        }
    }
    public override IQueryable<ComposablePartDefinition> Parts
    {
        get { return _catalog.Parts; }
    }
}
like image 77
Dan Morphis Avatar answered Nov 05 '22 14:11

Dan Morphis


DirectoryCatalog already has code to catch ReflectionTypeLoadException and ignore those assemblies. Unfortunately, as I have reported, merely creating the AssemblyCatalog will not yet trigger the exception so that code doesn't work.

The exception is actually triggered by the first call to AssemblyCatalog.Parts.

Instead of using the DirectoryCatalog from MEF, you will have to do it yourself:

  • scan a directory for assemblies
  • load each assembly and creates a AssemblyCatalog for it
  • invoke AssemblyCatalog.Parts.ToArray() to force the exception, and catch it
  • aggregate all the good catalogs with a AggregateCatalog
like image 38
Wim Coenen Avatar answered Nov 05 '22 13:11

Wim Coenen


I was doing this from an API I was writing and the SafeDirectoryCatalog would not log multiple exports matching a single import from different assemblies. MEF debugging is typically done via debugger and TraceListener. I already used Log4Net and I didn't want someone to need to add another entry to the config file just to support logging. http://blogs.msdn.com/b/dsplaisted/archive/2010/07/13/how-to-debug-and-diagnose-mef-failures.aspx I came up with:

    // I don't want people to have to add configuration information to get this logging. 
    // I know this brittle, but don't judge... please. It makes consuing the api so much
    // easier.
    private static void EnsureLog4NetListener()
    {
        try
        {
            Assembly compositionAssembly = Assembly.GetAssembly(typeof (CompositionContainer));
            Type compSource = compositionAssembly.GetType("System.ComponentModel.Composition.Diagnostics.CompositionTraceSource");

            PropertyInfo canWriteErrorProp = compSource.GetProperty("CanWriteError");
            canWriteErrorProp.GetGetMethod().Invoke(null,
                                                    BindingFlags.Public | BindingFlags.NonPublic |
                                                    BindingFlags.Static, null, null,
                                                    null);

            Type traceSourceTraceWriterType =
                compositionAssembly.GetType(
                    "System.ComponentModel.Composition.Diagnostics.TraceSourceTraceWriter");

            TraceSource traceSource = (TraceSource)traceSourceTraceWriterType.GetField("Source",
                                                                          BindingFlags.Public |
                                                                          BindingFlags.NonPublic |
                                                                          BindingFlags.Static).GetValue(null);

            traceSource.Listeners.Add(new Log4NetTraceListener(logger));                
        }
        catch (Exception e)
        {
            logger.Value.Error("Cannot hook MEF compisition listener. Composition errors may be swallowed.", e);
        }
    }  
like image 2
Mike Barry Avatar answered Nov 05 '22 13:11

Mike Barry