Я поддерживаю устаревший код с динамической и гибкой системой плагинов, которая реализована в виде очень мелкого дерева классов, где все конкретные плагины расширяют абстрактный класс 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, без необходимости дублирования этой информации в другом месте!
Есть идеи?