Подтвердить что ты не робот

Использование Ninject в плагиновой архитектуре

Я изучаю DI и недавно сделал свой первый проект.

В этом проекте я реализую шаблон репозитория. У меня есть интерфейсы и конкретные реализации. Интересно, возможно ли реализовать реализацию моих интерфейсов как "плагинов", dll, которые моя программа будет загружать динамически.

Таким образом, программа может быть улучшена с течением времени, не перестраивая ее, вы просто поместите DLL в папку "plugins", измените настройки и измените настройки!

Возможно ли это? Может ли Ninject помочь с этим?

4b9b3361

Ответ 1

В то время как решение Sean Chambers работает в том случае, если вы контролируете плагины, оно не работает в случае, когда плагины могут разрабатываться третьими лицами, и вы не 't хотят, чтобы они зависели от написания модулей ninject.

Это довольно легко сделать с Расширение условных обозначений для Ninject:

public static IKernel CreateKernel()
{
    var kernel = new StandardKernel();

    kernel.Scan(scanner => {
        scanner.FromAssembliesInPath(@"Path\To\Plugins");
        scanner.AutoLoadModules();
        scanner.WhereTypeInheritsFrom<IPlugin>();
        scanner.BindWith<PluginBindingGenerator<IPlugin>>();
    });

    return kernel;
}

private class PluginBindingGenerator<TPluginInterface> : IBindingGenerator
{
    private readonly Type pluginInterfaceType = typeof (TPluginInterface);

    public void Process(Type type, Func<IContext, object> scopeCallback, IKernel kernel)
    {
        if(!pluginInterfaceType.IsAssignableFrom(type))
            return;
        if (type.IsAbstract || type.IsInterface)
            return;
        kernel.Bind(pluginInterfaceType).To(type);
    }
}

Затем вы можете загрузить все загруженные плагины с помощью kernel.GetAll<IPlugin>().

Преимущества этого метода заключаются в следующем:

  • Ваши плагины DLL не должны знать, что они загружаются с помощью ninject
  • Конкретные экземпляры плагинов будут разрешены ninject, поэтому у них могут быть конструкторы для ввода типов, которые узел плагина знает, как их построить.

Ответ 2

Этот вопрос относится к тому же самому ответу, который я предоставил здесь: Может ли NInject загружать модули/сборки по требованию?

Я уверен, что это то, что вы ищете:

var kernel = new StandardKernel();
kernel.Load( Assembly.Load("yourpath_to_assembly.dll");

Если вы посмотрите на KernelBase с отражателем в Ninject.dll, вы увидите, что этот вызов будет рекурсивно загружать все модули в загруженные сборки (метод Load принимает IEnumerable)

public void Load(IEnumerable<Assembly> assemblies)
{
    foreach (Assembly assembly in assemblies)
    {
        this.Load(assembly.GetNinjectModules());
    }
}

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

Ответ 3

Расширение на @ungood хорошего ответа, основанного на v.2, с v.3 Ninject (в настоящее время на RC3), можно сделать еще проще. Вам больше не нужен IPluginGenerator, просто напишите:

var kernel = new StandardKernel();
kernel.Bind(scanner => scanner.FromAssembliesInPath(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location))
                                   .SelectAllClasses()
                                   .InheritedFrom<IPlugin>()
                                   .BindToAllInterfaces());

Обратите внимание, что я ищу плагины, реализующие IPlugin (поместите ваш интерфейс здесь) в один и тот же путь приложения.

Ответ 4

вы можете легко сделать это с обычным отражением С#, вам не нужны никакие дополнительные технологии.

В Интернете довольно много примеров, например. http://www.codeproject.com/KB/cs/c__plugin_architecture.aspx

Как правило, в основном приложении вам необходимо загрузить сборку, реализующую плагин, например:

ass = Assembly.Load(name);

а затем вам нужно создать экземпляр вашего плагина. Если вы знаете имя класса, оно будет выглядеть так:

ObjType = ass.GetType(typename);
IPlugin plugin = (IPlugin)Activator.CreateInstance(ObjType);

а затем просто используйте его.

Ответ 6

Существует несколько способов сделать это, и вы уже достигли главной цели, чтобы достичь этого, имея конкретные реализации через предопределенные интерфейсы. Реально, если ваши интерфейсы остаются стабильными, вы должны уметь строить свое основное приложение.

Я не уверен, каким образом реализация будет работать с Ninject. Вы можете сделать это с помощью Модель поставщика или с отражением - хотя я думаю, что рефлексия переполнена, если вам не нужно это делать абсолютно.

При использовании подхода модели поставщика вы помещаете файл в папку /bin или любую другую папку, которую вы исследуете, и настраиваете файл .config, чтобы отразить присутствие провайдера. Если у вас есть конкретная папка "плагин", вы можете создать метод, который вызывается при запуске приложения, и периодически, в противном случае, для сканирования новых или удаленных экземпляров и перезагрузки поставщиков.

Это будет работать в ASP.NET, под С# или VB. Однако, если вы делаете какое-то другое приложение, вам нужно будет рассмотреть другой подход. Поставщик действительно просто Microsoft работает над Шаблон стратегии.

Ответ 7

Я получил это как удар для Activator.CreateInstance + Ninject и просто хотел указать что-то в этой области - надеюсь, это вдохновит кого-то, кто придумает настоящий ответ убийцы на этот вопрос на SO.

Если вы еще не решили проблему автоматического сканирования модулей и классов и правильно зарегистрировали их с помощью Ninject и все еще создаете свой плагин через Activator.CreateInstance, вы можете post-CreateInstance вводить зависимости через

IKernel k = ...
var o = Activator.CreateInstance(...);
k.Inject( o );

Конечно, это было бы лишь временным решением на пути к чему-то вроде http://groups.google.com/group/ninject/browse_thread/thread/880ae2d14660b33c

Ответ 9

Проблема в том, что вам может потребоваться перекомпилировать, если объект, который вы устанавливаете при загрузке вашего модуля, используется внутри программы. Причина в том, что у вашей программы может быть не последняя версия сборки вашего класса. Например, если вы создаете новый конкретный класс для одного из ваших интерфейсов, скажем, вы меняете плагин dll. Теперь инжектор будет загружать его, прекрасно, но когда он будет возвращен внутри вашей программы (kernel.get(...)), ваша программа может не иметь сборки и будет вызывать ошибку.

Пример того, о чем я говорю:

BaseAuto auto = kernel.Get<BaseAuto>();//Get from the NInjector kernel your object. You get your concrete objet and the object "auto" will be filled up (interface inside him) with the kernel.

//Somewhere else:

public class BaseModule : StandardModule
{
        public override void Load(){
            Bind<BaseAuto>().ToSelf();
            Bind<IEngine>().To<FourCylinder>();//Bind the interface
        }     
 }

Если вы создали новый FourCylinder под названием SixCylinder, ваша реальная программа не будет ссылаться на ваш новый объект. Итак, как только вы загрузите из PlugIn BaseModule.cs, вы можете столкнуться с проблемой. Чтобы это сделать, вам нужно будет распространять новую DLL этой конкретной реализации с вашим плагином, у которого будет модуль, который Инжектор потребует загрузить класс Interface to Concrete. Это можно сделать без проблем, но вы начинаете иметь целое приложение, которое находится при загрузке из плагина, и в некоторых случаях это может быть проблематично. Имейте в виду.

НО, если вам нужна информация о подключаемом модуле, вы можете получить учебник из CodeProject.