2010-09-28 17 views
5

Estoy trabajando en una aplicación de monitoreo del sistema similar a Nagios en C#. Tengo una interfaz de complemento definida como:C# plugin arquitectura pregunta

public interface IPlugin 
{ 
    PluginResult Execute(); 
} 

Cada complemento, dependiendo de su funcionalidad, tendrá un número variable de argumentos. Como ejemplo, un complemento ping puede tomar un nombre de host, # de paquetes, valor de tiempo de espera, etc. Quiero que el usuario pueda definir estos argumentos por servicio en mi interfaz de usuario, pero obviamente estos argumentos no se conocerán hasta que el la aplicación descubre qué complementos están disponibles. Tengo curiosidad sobre cómo otros podrían diseñar un complemento de modo que la aplicación pueda descubrir estos argumentos variables.

En este momento, como un ejemplo, tengo un plugin de ping:

public class PingPlugin : IPlugin 
{ 
    private const string RESULT_MESSAGE = "Average ms: {0}; Packet loss: {1}"; 

    private string _hostname; 
    private int _packets; 
    private int _timeout; 
    private int _warningTimeThreshold; 
    private int _warningLossThreshold; 
    private int _errorTimeThreshold; 
    private int _errorLossThreshold; 

    public PingPlugin(
     string hostname, 
     int packets, 
     int timeout, 
     int warningTimeThreshold, 
     int warningLossThreshold, 
     int errorTimeThreshold, 
     int errorLossThreshold) 
    { 
     _hostname = hostname; 
     _packets = packets; 
     _timeout = timeout; 
     _warningTimeThreshold = warningTimeThreshold; 
     _warningLossThreshold = warningLossThreshold; 
     _errorTimeThreshold = errorTimeThreshold; 
     _errorLossThreshold = errorLossThreshold; 
    } 

    public PluginResult Execute() 
    { 
     // execute the plugin 
    } 
} 

pensé que podría ser capaz de descubrir los parámetros del constructor mediante la reflexión y presentar al usuario una cuadrícula de propiedades para permitir la configuración del complemento, pero no estoy seguro de la mejor manera de proporcionar un conjunto de valores predeterminados con este diseño. ¿Cuáles podrían ser algunas alternativas?

Respuesta

18

¿Ha considerado mirar el Managed Extensibility Framework?

+3

Voto para esto, MEF es increíble. No hay necesidad de reinventar la rueda aquí, en serio. –

+0

¡No puedo soportar esto lo suficiente! ¿Por qué todos insisten en inventar el sistema ** TODO OTRO PLUG-IN **, incluso después de MEF?

+3

MEF permite el descubrimiento y la inyección, pero no responde a la pregunta fundamental, que es cómo exponer y cablear los parámetros dinámicamente al objeto del complemento. MEF no es, en sí mismo, un sistema de complementos. Todavía hay trabajo de arquitectura para decidir qué servicios deberían estar expuestos y cómo el usuario interactuará con los complementos. –

1

Puede aplicar el atributo [DefaultValue] a los parámetros.

En C#, usted puede utilizar la nueva sintaxis para esto: int warningLossThreshold = 30,

0

he votado 1 para la respuesta MEF también, se va a resolver muchos de sus problemas.

Sin embargo, si quiere hacerlo sin MEF, me parece que se echa en falta alguna manera de tener los plugins informe a su aplicación a través de metadatos, acerca de los parámetros que requieren.

Un posible diseño podría ser este: Tener una interfaz IPluginProvider, que su aplicación pueda descubrir. Esto debería tener un constructor sin parámetros, por lo que fácilmente puede new una instancia. Debería tener métodos que devuelvan los metadatos necesarios (como "nombres bonitos" para los parámetros, que son necesarios, algunos valores predeterminados razonables, etc.). Debería incluir el método CreateInstance, que toma los parámetros reales como IDictionary<string,object> y devuelve la instancia real IPlugin.

3

En lugar de tener un constructor Programas determinar los parámetros, es posible considerar algo como esto:

public interface IPlugin 
{ 
    PluginResult Execute(Object parameters); 
} 

public class PingParameters 
{ 
    //Various parameters here, including [Description] and [DisplayName] attributes if you wish 
} 

public class ParametersTypeAttribute : Attribute 
{ 
    public Type Type { get; private set; } 

    public ParametersTypeAttribute(Type type) 
    { 
     Type = type; 
    } 
} 

[ParametersType(typeof(PingParameters))] 
public class PingPlugin : IPlugin 
{ 
    public PluginResult Execute(Object parameters) 
    { 
     return Execute((PingParameters) parameters); 
    } 

    private PluginResult Execute(PingParameters parameters) 
    { 
     //Your execution code here 
    } 
} 

Esto le da más flexibilidad para los parámetros, como se puede añadir atributos, proporcionar una validación de la moda e incluso especificar diseñador/integración de convertidor para la cuadrícula de propiedad. La cuadrícula de propiedad se conecta directamente al objeto de parámetros.

+0

Me gusta mucho esta idea y la exploraré más. – Chris

+0

Esta es una idea muy simple, me gusta eso. Miré a MEF una vez. Era demasiado para una simple arquitectura de complementos. – NotMe

+0

@Chris, MEF definitivamente vale la pena examinarlo, ya que hace un muy buen trabajo al manejar el descubrimiento y la creación de instancias/inyecciones. También existe una buena posibilidad de que se convierta en la forma estándar de hacerlo, ya que ahora está incluida en el marco. Si los requisitos de su complemento se vuelven más complicados, con el complemento que desea interactuar con el entorno host a través de las interfaces de servicio, MEF es un buen camino a seguir. Por supuesto, incluso si usa MEF para hacer el cableado, igual necesitará algo como esto para encapsular sus parámetros. –

0

No he mirado el MEF (lo haré ahora).

Tuve un problema casi idéntico al tuyo, lo resolví con Attributes.
Tengo una IU que (llama BL que) usa la reflexión para mostrar todos los "servicios" disponibles (nada más que clases apropiadamente decoradas).

Cuando el usuario selecciona un "servicio", atributos adicionales conducen la interfaz de usuario. El atributo "esquema" es bastante directo y permite cualquier cantidad de parámetros con cualquier nombre.Al introducir constantes (con la definición de atributo) puede estandarizar elementos comunes como "nombre" para que sus servicios sean consistentes.

Todos los datos se almacenan en una tabla de pares clave-valor.

Lo bueno de esto es que puede simplemente volcar ensambles "de servicio" nuevos/modificados en el directorio de bin - no se requiere trabajo adicional. La única dependencia es el ensamblaje de definiciones de atributos, así que mantenga esta inclinación.

El código fuente está en CodePlex si quiere "robar" algún :)