Autofac: зарегистрировать открытые универсальные типы как экземпляры неуниверсальных абстрактных родительских типов? - PullRequest
0 голосов
/ 22 февраля 2019

Я поддерживаю устаревший код с динамической и гибкой системой плагинов, которая реализована в виде очень мелкого дерева классов, где все конкретные плагины расширяют абстрактный класс Plugin.Большинство этих плагинов являются конкретными классами (например, LoadSound, HighPassFilter), но некоторые из них имеют один параметр универсального типа (например, CreateCopy<T>).

Последовательность инициализации для этой системы в основном загружает всесборки, перечисляет все их типы, выбирает те, которые являются потомками Plugin, а затем выполняет итерацию по ним и вставляет их в своего рода контейнер IoC для доморощенного пивоварения.

Для повышения удобства обслуживания этой системы я хочузаменить самодельный контейнер IoC на Autofac, который используется в другом месте кодовой базы.

Мне удалось зарегистрировать все типы полезным способом, например так:

Autofac.ContainerBuilder builder;

// Find all classes derived from Plugin, and split them
// up based on whether or not they are generic
ILookup<bool, Type> isGeneric = types.Where(IsPlugin)
    .ToLookup(t => t.IsGenericTypeDefinition);

// Register the generic types as themselves
foreach (Type t in isGeneric[true])
    builder.RegisterGeneric(t)
        .AsSelf()
        .SingleInstance();

// Register all the non-generic types as themselves and
// as instances of Plugin
builder.RegisterTypes(isGeneric[false].ToArray())
    .AsSelf()
    .As<Plugin>()
    .SingleInstance();

С этим,Я могу успешно разрешить любой плагин из контейнера, позже созданного из builder.Проблема в том, что система также ожидает метод GetAvailablePlugins.Моим первым инстинктом было реализовать это как

public IReadOnlyCollection<PluginMetaData> GetAvailablePlugins()
{
    return Components.Resolve<IEnumerable<Plugin>>()
        .Select(plugin => new PluginMetaData(plugin.GetType()))
        .ToList<PluginMetaData>();
}

(я знаю, что разрешение вручную из контейнера IoC - плохая идея, но исправление - это следующий шаг, а не проблема, с которой я сталкиваюсь сейчас).

Однако, как вы уже догадались, это приведет только к коллекции не универсальных плагинов, а не к общим.Я наивно пытался зарегистрировать универсальные типы с помощью

builder.RegisterGeneric(t)
    .AsSelf()
    .As<Plugin>()
    .SingleInstance();

, но Autofac не хочет ничего из этого:

The service 'Vendor.Common.Plugin' is not an open generic type definition.

, что, конечно, является точным наблюдением.Это не так.И я не ожидаю, что попытка разрешения для Plugin также приведет к конкретному экземпляру универсального плагина - я просто хочу получить хороший способ доступа ко всем типам, зарегистрированным как Plugin s, без необходимости дублирования этой информации в другом месте!

Есть идеи?

Ответы [ 2 ]

0 голосов
/ 22 февраля 2019

Другой подход - запросить container.ComponentRegistry и просто найти все зарегистрированные плагины таким образом.

0 голосов
/ 22 февраля 2019

После написания вопроса прошло совсем немного времени, пока я сам не понял, как это сделать: я изменил логику регистрации на

// Find all classes derived from Plugin
IEnumerable<Type> plugins = types.Where(IsPlugin);

// Register all plugins (generic and non-generic) as children of Plugin
builder.RegisterTypes(plugins.ToArray())
    .AsSelf().As<Plugin>().SingleInstance();

// Register generic plugins in a resolvable way
foreach (Type t in modules.Where(t => t.IsGenericTypeDefinition))
    builder.RegisterGeneric(t).AsSelf().SingleInstance();

Я не совсем уверен, как это работает, но похоже, что он регистрирует универсальные плагины, как если бы они были не универсальными компонентами, так или иначе.Эти неуниверсальные компоненты не могут быть разрешены в системе, но они возвращаются при запросе у Autofac всех компонентов, реализующих службу Plugin.

Затем в дополнение к этому Я также регистрируюуниверсальные плагины как универсальные компоненты, которые позволяют системе разрешать их с помощью конкретных аргументов типа.


Обновление : Однако теперь я обнаружил, что список всех плагинов с

Components.Resolve<IEnumerable<Plugin>>()
        .Select(plugin => new PluginMetaData(plugin.GetType()))
        .ToList<PluginMetaData>();

не является приемлемым маршрутом - некоторые плагины имеют зависимости, которые невозможно создать при запуске тестов, которые проверяют некоторые свойства этого списка плагинов.Поэтому я согласился с комбинацией моего ответа и ответа DR :

Теперь я получаю список плагинов с

Components.ComponentRegistry
    .RegistrationsFor(new TypedService(typeof(BaseModule)))
    .Select(reg => new PluginMetaData(reg.Activator.LimitType))
    .ToList<PluginMetaData>();
...