在MEF合成期间处理ReflectionTypeLoadException

时间:2010-11-10 12:56:58

标签: c# mef

我在MEF中使用DirectoryCatalog来满足我的应用程序中的导入。但是,当我尝试编写目录时,目录中有时会出现模糊的程序集导致ReflectionTypeLoadException

我知道我可以通过使用单独的目录或使用DirectoryCatalog上的搜索过滤器来绕过它,但我想要一种更通用的方法来解决问题。有什么方法可以处理异常并允许组合继续吗?还是有另一种更通用的解决方案吗?

3 个答案:

答案 0 :(得分:44)

为了避免其他人编写他们自己的SafeDirectoryCatalog实现,这是我根据Wim Coenen的建议提出的:

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

答案 1 :(得分:25)

DirectoryCatalog已经有代码来捕获ReflectionTypeLoadException并忽略这些程序集。不幸的是,正如我reported一样,仅仅创建AssemblyCatalog将不会触发异常,因此代码不起作用。

实际上,第一次调用AssemblyCatalog.Parts会触发异常。

您不必使用MEF的DirectoryCatalog,而是自己动手:

  • 扫描程序集目录
  • 加载每个程序集并为其创建AssemblyCatalog
  • 调用AssemblyCatalog.Parts.ToArray()来强制执行异常并抓住它
  • 使用AggregateCatalog
  • 汇总所有好的目录

答案 2 :(得分:2)

我是从我正在编写的API执行此操作,而SafeDirectoryCatalog不会记录与来自不同程序集的单个导入匹配的多个导出。 MEF调试通常通过调试器和TraceListener完成。我已经使用过Log4Net,我不希望有人需要在配置文件中添加另一个条目以支持日志记录。 http://blogs.msdn.com/b/dsplaisted/archive/2010/07/13/how-to-debug-and-diagnose-mef-failures.aspx我提出了:

    // 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);
        }
    }