2016-09-06 9 views
0

У меня есть базовый класс для классов, которые могут выступать в качестве плагинов для центральной системы. Я хочу, чтобы эти плагины были определены в сторонних сборках. Центральная система создает экземпляры плагинов на основе некоторых аргументов командной строки.Используя ninject, могу ли я создать привязку к классу, который имеет атрибут и удовлетворяет предикату?

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

[ArgName("some-module")] 

И в момент их инициализации, я использую кусок кода, который отражается на все загруженные типы, пытаясь найти тот, у которого есть атрибут и правильный параметр:

AppDomain.CurrentDomain.GetAssemblies() 
     .SelectMany(x => x.GetTypes()) 
     .SingleOrDefault(x => /* check for attribute and predicate */); 

Это прекрасно работает.

Но я чувствую, что я заново изобретаю то, что может иметь каркас DI самостоятельно. Итак, поскольку я уже использую Ninject для некоторых других зависимостей в своем проекте, мне было интересно: есть ли способ передать эту ответственность Ninject, а не писать код пользовательского отражения?

Другими словами, это Ninject уже есть что-то я пропустил, что делает примерно следующее:

kernel.Bind<ModuleBase>() 
     .ToTypesThatHaveThisAttribute<ArgName>() 
     .With(x => x.Name == userProvidedCommandlineArgument); 

Конечно, я знаю, что могу создать вышеупомянутые методы расширения для BindingToSyntax<T> и сделать синтаксис лучше, где он используется. Но я хотел бы изгнать код отражения вообще, если Ninject имеет встроенную функциональность.

+0

Посмотрите на библиотеку Ninject.Extensions.Conventions, у нее есть много дополнительных функций, особенно для таких сценариев привязки «пакетной». https://github.com/ninject/Ninject.Extensions.Conventions –

ответ

2

Используя Ninject.Extensions.Conventions, вы можете достичь чего-то подобного (не тестировалось).

string arg = null; 
Kernel.Bind(x => 
{ 
    x.FromThisAssembly() 
     .Select(t => 
     { 
      var attributes = t.GetCustomAttributes(typeof(ArgNameAttribute)); 
      if (attributes.Length == 0) return false; 
      var attribute = attributes[0]; 
      return attribute.ArgName == arg; 
     }) 
     .BindSelection((type, interfaces) => new[] {typeof(PluginBase)}); 
}); 

Но, честно говоря, это почти то же самое количество кода по сравнению с ручным выбором типа и связывание с отражением, подобно тому, что вы написали. Я имею в виду что-то вроде этого.

AppDomain.CurrentDomain.GetAssemblies() 
    .SelectMany(x => x.GetTypes()) 
    .Where(x => { 
     var attributes = t.GetCustomAttributes(typeof(ArgNameAttribute)); 
     if (attributes.Length == 0) return false; 
     var attribute = attributes[0]; 
     return attribute.ArgName == arg; 
    }) 
    .Select(x => Kernel.Bind<PluginBase>().To(x))... 

я бы, вероятно, сделать это без расширения Conventions, если вы не найдете его полезным и в других отношениях.

Обратите внимание, что ни один из этих кодов не проверен, и я использую их только, например.

+0

Это, по сути, то, что у меня уже есть. Но я бы предпочел не иметь механизм поиска и изучения атрибутов в моей базе кода вообще, если у Ninject есть что-то встроенное. Если это не так, я могу жить с тем, что у меня уже есть. –

+0

Я не знаю никаких встроенных возможностей. IMO эти несколько строк кода полностью в порядке, и для меня это не похоже на шаблон. Он описательный, легко читаемый (особенно способ отражения). Я бы реорганизовал логику локации в функцию типа GetArgNameFromType, которая обрабатывала бы атрибут, так что это стало бы еще более чистым. –

+0

@ TheodorosChatzigiannakis нет ничего заранее подготовленного, чтобы делать именно то, что вы ищете. – BatteryBackupUnit