Я изучаю DI и недавно сделал свой первый проект.
В этом проекте я реализовал шаблон репозитория. У меня есть интерфейсы и конкретные реализации. Интересно, можно ли реализовать реализацию моих интерфейсов в виде «плагинов», dll, которые моя программа будет загружать динамически.
Таким образом, программа может быть улучшена с течением времени без необходимости ее перекомпоновки, вы просто помещаете dll в папку «plugins», меняете настройки и вуаля!
Это возможно? Может ли Ninject помочь с этим?





вы можете легко сделать это с помощью обычного отражения 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);
а потом вы просто используете это.
Это может быть потому, что он не хочет знать, как создавать подключаемый модуль, но как использовать DI и изменять его без компиляции ... поэтому он предложил подключаемый модуль ...
Я не голосовал за вас: P почему вы голосуете за меня ... в любом случае смешно
Проблема в том, что вам может потребоваться перекомпиляция, если объект, который вы устанавливаете при загрузке вашего модуля, используется внутри программы. Причина в том, что в вашей программе может не быть последней версии сборки вашего класса. Например, если вы создаете новый конкретный класс для одного из своих интерфейсов, допустим, вы меняете 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());
}
}
Я использую это для сценариев, когда мне не нужна прямая ссылка на сборку на что-то, что будет очень часто меняться, и я могу поменять сборку, чтобы предоставить другую модель приложению (при условии, что у меня есть соответствующие тесты)
Мне интересно, есть ли вероятность того, что плагин сможет вмешиваться в ваше приложение.
Как это работает? Если моя сборка включает в себя множество классов, будет ли она искать какие-либо классы, которые расширяются от NinjectModule, или моя сборка должна содержать только NinjectModules?
Все это выполняет поиск любых производных классов NinjectModule в вашей сборке и вызывает метод Load () для каждого, таким образом инициализируя ваши привязки.
Согласно вики Ninject на GitHub, вы можете автоматически загружать плагины, назвав сборку Ninject.Extensions. *. Dll. Однако объем документации по этой функции меньше, чем тот абзац, который я набираю в середине.
Думаю, рамки не нужны. В этом туториале решена ваша проблема 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>().
Преимущества этого метода:
Я делаю именно это в одном из моих проектов на найдено здесь. Я обнаружил, что оставление Ninject ответственным за фактическую загрузку сборок приведет к сбою вашего приложения, если одна из ваших dll плагинов не загрузится. Это нехорошо, поэтому я сначала загружаю сборки в домен приложения, завернутые в try / catch для лучшего поведения.
Как вы могли сделать то же самое, используя последнюю версию Ninject, Scan больше нет?
Расширение хорошего ответа @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 и не могу загружать модули автоматически.
Не знаю, почему это было отклонено. Это технически точный и верный ответ.