2008-12-01 2 views
27

Я изучаю DI и недавно сделал свой первый проект.Использование Ninject в плагине, подобной архитектуре

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

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

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

ответ

3

вы можете легко сделать это с нормальным отражением 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); 

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

+1

Не знаю, почему это было отклонено. Это технически точный и действительный ответ. – 2008-12-01 14:12:07

+1

Возможно, это потому, что он не хочет знать, как создать плагин, но как использовать DI и изменить его DI без необходимости компилировать ... поэтому он предложил плагин ... – 2008-12-01 14:13:27

+0

Я вас не проголосовал: P почему ты проголосуешь за меня ... в любом случае смешно – 2008-12-01 14:15:09

-1

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

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

0

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

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

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

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

0

Я получил это как удар для 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

12

Этот вопрос относится к тому же ответ я представил здесь: Can NInject load modules/assemblies on demand?

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

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()); 
    } 
} 

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

+1

Я обожаю, если есть потенциал, который плагин сможет повесить с вашим приложением. – 2010-07-14 12:48:39

25

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

Это довольно легко сделать с Conventions Extension для 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, поэтому они могут иметь конструкторы для ввода типов, которые хост-плагин знает, как построить.
11

Продолжение на @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 (поместить интерфейс здесь) в том же пути приложения.

Смежные вопросы