2008-12-01 22 views
27

Estoy aprendiendo DI, e hice mi primer proyecto recientemente.Usando Ninject en un plugin como arquitectura

En este proyecto he implementado el patrón de repositorio. Tengo las interfaces y las implementaciones concretas. Me pregunto si es posible construir la implementación de mis interfaces como "complementos", dlls que mi programa cargará dinámicamente.

Así que el programa podría mejorarse con el tiempo sin tener que reconstruirlo, simplemente coloque el dll en la carpeta "complementos", cambie la configuración y ¡listo!

¿Esto es posible? ¿Puede Ninject ayudar con esto?

Respuesta

3

puede hacerlo fácilmente con la reflexión C# normal, no necesita ninguna tecnología adicional.

Existen bastantes ejemplos en la web, p. http://www.codeproject.com/KB/cs/c__plugin_architecture.aspx

En general en su aplicación principal, es necesario cargar el ensamblado de aplicación del complemento, por ejemplo .:

ass = Assembly.Load(name); 

y luego es necesario crear una instancia de su complemento. Si conoce el nombre de la clase, se vería así:

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

y luego simplemente la usa.

+1

No sé por qué esto fue rechazado. Es técnicamente preciso y una respuesta válida. –

+1

Puede ser porque no quiere saber cómo crear un complemento, sino cómo usar DI y cambiar su DI sin tener que compilar ... así que sugirió el complemento ... –

+0

No te he votado por error: P ¿Por qué me votas? De todos modos, gracioso –

-1

El problema es que es posible que deba volver a compilar si el objeto que configura en la carga de su módulo se utiliza dentro del programa. La razón es que su programa podría no tener la última versión del ensamblaje de su clase. Ejemplo, si crea una nueva clase concreta para una de sus interfaces, supongamos que cambia el plugin dll. Ahora, Injector lo cargará, bien, pero cuando se devolverá dentro de tu programa (kernel.get (...)) tu programa podría no tener el ensamblado y lanzará un error.

Ejemplo de lo que estoy hablando:

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 
     }  
} 

Si tiene crear un nuevo cuatro cilindros llamada de seis cilindros, el programa real no tendrá ninguna referencia a su nuevo objeto. Por lo tanto, una vez que cargues desde el PlugIn the BaseModule.cs, es posible que tengas problemas con la referencia. Para poder hacerlo, tendrá que distribuir el nuevo dll de esta implementación concreta con su complemento que tendrá el Módulo que el Injector necesitará para cargar la clase Interface to Concrete. Esto se puede hacer sin problemas, pero comienza a tener una aplicación completa que reside en la carga desde el complemento y puede ser problemático en algunos puntos. Ten cuidado

PERO, si desea obtener información sobre el PlugIn, puede obtener tutorial from CodeProject.

0

Existen varias maneras de hacerlo y ya ha logrado el objetivo principal para lograr esto al tener implementaciones concretas a través de interfaces predefinidas. De manera realista, si sus interfaces permanecen estables, debería poder construir desde su aplicación principal.

No estoy seguro de cómo funcionaría la implementación con Ninject, sin embargo. Puede hacer esto con el Provider Model o con la reflexión, aunque creo que la reflexión es excesiva, si no es absolutamente necesario hacerlo.

Con el enfoque de modelo de proveedor, coloca el archivo en la carpeta/bin, o en cualquier otra carpeta que esté sondeando, y ajuste la.archivo de configuración para reflejar la presencia del proveedor. Si tiene una carpeta específica de "complementos", puede crear un método llamado al inicio de la aplicación y periódicamente, de lo contrario, para buscar instancias nuevas o eliminadas y volver a cargar los proveedores.

Esto funcionaría en ASP.NET, en C# o VB. Sin embargo, si está haciendo algún tipo de otra aplicación, debería considerar otro enfoque. El proveedor es realmente solo el giro de Microsoft en el Strategy Pattern.

0

Entendí esto como un golpe para Activator.CreateInstance + Ninject y solo quería señalar algo en esta área, espero que inspire a alguien a encontrar una respuesta realmente acertada a esta pregunta en SO.

Si todavía no has tomado la molestia de los módulos de auto-exploración y clases y registrarlas con Ninject correctamente, y todavía está creando su plug-in a través de Activator.CreateInstance, a continuación, puede publicar-CreateInstance inyectar a través de las dependencias en

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

por supuesto, esto sólo sería una solución temporal en el camino a algo así como http://groups.google.com/group/ninject/browse_thread/thread/880ae2d14660b33c

12

Esta pregunta se aplica a la misma respuesta que he dado aquí: Can NInject load modules/assemblies on demand?

Estoy bastante seguro de que esto es lo que está buscando:

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

Si nos fijamos en KernelBase con reflector en Ninject.dll verá que esta llamada cargará recursivamente todos los módulos en los ensamblados cargados (el método de carga toma un IEnumerable)

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

Estoy usando esto para escenarios en los que no quiero una referencia de ensamblaje directo a algo que cambiará con mucha frecuencia y puedo cambiar el ensamblaje para proporcionar un modelo diferente a la aplicación (a condición de que tenga las pruebas adecuadas en su lugar)

+1

Me sorprende que exista la posibilidad de que un complemento pueda perder el tiempo con tu aplicación. –

+1

¿Cómo funciona esto? Si mi conjunto incluye una variedad de clases, ¿buscará alguna clase que se extienda desde NinjectModule o mi ensamblaje solo debe contener NinjectModules? – Pandincus

+1

Todo lo que hace es buscar cualquier clase derivada de NinjectModule en su ensamblado y llama al método Load() en cada una de ellas para inicializar sus enlaces. –

25

Mientras que Sean Chambers' solution funciona en el caso de que controle los complementos, no funciona en el caso en que los complementos puedan ser desarrollados por terceros y usted no quiere que tengan que ser dependientes en la escritura de módulos ninject.

Esto es bastante fácil de hacer con el Conventions Extension para 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); 
    } 
} 

entonces se puede obtener todos los plugins cargados con kernel.GetAll<IPlugin>().

Las ventajas de este método son:

  1. Sus dlls plugins no necesitan saber que están siendo cargados con ninject
  2. Los casos concretos de plugin será resuelta por ninject, para que puedan tener constructores para inyectar tipos que el servidor de complementos sabe cómo construir.
+3

Hago exactamente esto en uno de mis proyectos en [aquí] (https://github.com/ungood/BadaBingBot/blob/fc326bc37169dfdf506c16115eb6b200f8fc1afd/BadaBingBot/Program.cs). Descubrí que al dejar a Ninject a cargo de cargar los ensamblajes se bloqueará su aplicación si uno de sus archivos DLL no se carga. Esto no es bueno, así que cargué los ensambles en el dominio de la aplicación primero, envuelto en try/catch para un mejor comportamiento. – ungood

+0

¿Cómo podría hacer lo mismo con la última versión de Ninject, Scan ya no está? – MoonKnight

11

Extendiendo en @ungood buena respuesta, que se basa en v.2, con v.3 de Ninject (actualmente en RC3) podría hacerse aún más fácil. No se necesita ningún IPluginGenerator más, acaba de escribir:

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

Tenga en cuenta que estoy buscando plugins de aplicación IPlugin (ponga su interfaz de aquí) en la misma ruta de la aplicación.

+0

¿Cómo puede cargar automáticamente los módulos con su solución? Acabo de cambiar de Ninject 2.2 a 3.0 y no puedo cargar automáticamente los módulos. –

Cuestiones relacionadas