Ручка ReflectionTypeLoadException во время композиции MEF
Я использую DirectoryCatalog
в MEF для удовлетворения импорта в моем приложении. Тем не менее, иногда возникают обфускации сборок в каталоге, которые вызывают ReflectionTypeLoadException
, когда я пытаюсь составить каталог.
Я знаю, что могу обойти его, используя отдельный каталог или используя фильтр поиска на DirectoryCatalog
, но я хочу более общий способ решить проблему. Есть ли способ обработать исключение и позволить композиции продолжить? Или есть еще одно общее решение?
Ответы
Ответ 1
DirectoryCatalog
уже имеет код для catch ReflectionTypeLoadException
и игнорировать эти сборки. К сожалению, поскольку я сообщил, просто создание AssemblyCatalog
еще не вызывает исключение, так что код не работает.
Исключение фактически инициируется первым вызовом AssemblyCatalog.Parts
.
Вместо использования DirectoryCatalog
из MEF вам придется сделать это самостоятельно:
- сканировать каталог для сборок
- загрузите каждую сборку и создайте для нее
AssemblyCatalog
- invoke
AssemblyCatalog.Parts.ToArray()
, чтобы заставить исключение, и поймать его
- агрегировать все хорошие каталоги с помощью
AggregateCatalog
Ответ 2
Чтобы спасти других от написания собственной реализации 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; }
}
}
Ответ 3
Я делал это из 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);
}
}