Использование Ninject в плагине, подобном архитектуре

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

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

Таким образом, программа может быть улучшена с течением времени без необходимости ее перекомпоновки, вы просто помещаете dll в папку «plugins», меняете настройки и вуаля!

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

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
27
0
13 682
9

Ответы 9

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

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

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

ass = Assembly.Load(name);

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

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

а потом вы просто используете это.

Не знаю, почему это было отклонено. Это технически точный и верный ответ.

Joseph Ferris 01.12.2008 17:12

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

Patrick Desjardins 01.12.2008 17:13

Я не голосовал за вас: P почему вы голосуете за меня ... в любом случае смешно

Patrick Desjardins 01.12.2008 17:15

Проблема в том, что вам может потребоваться перекомпиляция, если объект, который вы устанавливаете при загрузке вашего модуля, используется внутри программы. Причина в том, что в вашей программе может не быть последней версии сборки вашего класса. Например, если вы создаете новый конкретный класс для одного из своих интерфейсов, допустим, вы меняете dll плагина. Теперь Injector загрузит его, хорошо, но когда он будет возвращен внутри вашей программы (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, ваша настоящая программа не будет иметь ссылки на ваш новый объект. Итак, как только вы загрузите из подключаемого модуля BaseModule.cs, у вас могут возникнуть проблемы со ссылкой. Чтобы иметь возможность это сделать, вам нужно будет распространить новую dll этой конкретной реализации с вашим плагином, который будет иметь модуль, который потребуется Injector для загрузки интерфейса в конкретный класс. Это можно сделать без проблем, но вы начинаете иметь целое приложение, которое постоянно загружается из подключаемого модуля, и в некоторых случаях это может быть проблематично. Знайте.

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

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

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

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

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

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

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

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

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

Взгляните на Managed Extensibility Framework. http://www.codeplex.com/MEF

Этот вопрос относится к тому же ответу, который я дал здесь: Может ли 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());
    }
}

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

Мне интересно, есть ли вероятность того, что плагин сможет вмешиваться в ваше приложение.

Piotr Owsiak 14.07.2010 16:48

Как это работает? Если моя сборка включает в себя множество классов, будет ли она искать какие-либо классы, которые расширяются от NinjectModule, или моя сборка должна содержать только NinjectModules?

Pandincus 22.03.2011 23:06

Все это выполняет поиск любых производных классов NinjectModule в вашей сборке и вызывает метод Load () для каждого, таким образом инициализируя ваши привязки.

Sean Chambers 25.04.2011 07:19

Согласно вики Ninject на GitHub, вы можете автоматически загружать плагины, назвав сборку Ninject.Extensions. *. Dll. Однако объем документации по этой функции меньше, чем тот абзац, который я набираю в середине.

devlord 04.12.2012 01:15

Думаю, рамки не нужны. В этом туториале решена ваша проблема http://www.codeproject.com/KB/cs/c__plugin_architecture.aspx

Хотя Решение Шона Чемберса работает в том случае, если вы контролируете плагины, он не работает в случае, когда плагины могут быть разработаны третьими сторонами, и вы не хотите, чтобы они зависели от написания модулей 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>().

Преимущества этого метода:

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

Я делаю именно это в одном из моих проектов на найдено здесь. Я обнаружил, что оставление Ninject ответственным за фактическую загрузку сборок приведет к сбою вашего приложения, если одна из ваших dll плагинов не загрузится. Это нехорошо, поэтому я сначала загружаю сборки в домен приложения, завернутые в try / catch для лучшего поведения.

ungood 24.03.2012 00:59

Как вы могли сделать то же самое, используя последнюю версию Ninject, Scan больше нет?

MoonKnight 31.10.2016 17:31

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

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

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

Как вы можете автоматически загружать модули с вашим решением? Я только что переключился с Ninject 2.2 на 3.0 и не могу загружать модули автоматически.

Charles Ouellet 19.04.2012 19:39

Другие вопросы по теме