Обрабатывать ReflectionTypeLoadException во время создания MEF - PullRequest
22 голосов
/ 10 ноября 2010

Я использую DirectoryCatalog в MEF для удовлетворения импорта в моем приложении.Однако в каталоге иногда присутствуют запутанные сборки, которые вызывают ReflectionTypeLoadException, когда я пытаюсь составить каталог.

Я знаю, что могу обойти его, используя отдельный каталог или используя фильтр поиска наDirectoryCatalog но я хочу более общий способ решения проблемы.Есть ли способ обработать исключение и разрешить продолжение композиции?Или есть другое, более общее решение?

Ответы [ 3 ]

44 голосов
/ 18 декабря 2010

Чтобы спасти других от написания собственной реализации SafeDirectoryCatalog, вот что я придумал на основе предложений Вима Коенена:

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; }
    }
}
25 голосов
/ 10 ноября 2010

DirectoryCatalog уже имеет код для перехвата ReflectionTypeLoadException и игнорирования этих сборок.К сожалению, как я сообщил , простое создание AssemblyCatalog еще не вызовет исключение, поэтому код не будет работать.

Исключение фактически вызывается первым вызовом AssemblyCatalog.Parts.

Вместо использования DirectoryCatalog из MEF вам придется сделать это самостоятельно:

  • сканирование каталога на наличие сборок
  • загрузка каждой сборкии создает AssemblyCatalog для него
  • , вызывает AssemblyCatalog.Parts.ToArray(), чтобы вызвать исключение, и ловит его
  • , объединяет все хорошие каталоги с AggregateCatalog
2 голосов
/ 27 мая 2011

Я делал это из 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);
        }
    }  
...