2010-09-28 4 views
5

Я работаю над приложением для мониторинга системы, аналогичным Nagios в C#. У меня есть интерфейс плагина, определенный как:C# plugin architecture question

public interface IPlugin 
{ 
    PluginResult Execute(); 
} 

Каждый плагин, в зависимости от его функциональности, будет иметь переменное количество аргументов. Например, плагин 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 
    } 
} 

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

ответ

18

Считаете ли вы, что смотрите на Managed Extensibility Framework?

+3

Проголосуйте за это, MEF удивительно. Серьезно не нужно изобретать колесо здесь. –

+0

Не могу этого поддержать! Почему все настаивают на том, чтобы изобрести систему ** YET ANOTHER PLUG-IN **, даже после MEF?

+3

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

1

Вы можете применить атрибут [DefaultValue] к параметрам.

В C# для, вы можете использовать новый синтаксис для этого: int warningLossThreshold = 30,

0

Я голосовал +1 за MEF ответ тоже будет решить многие ваши проблемы.

Однако, если вы хотите сделать это без MEF, мне кажется, что вам не хватает какого-либо способа, чтобы плагины рассказывали ваше приложение через метаданные, о требуемых параметрах.

Один из возможных вариантов дизайна может заключаться в следующем: иметь интерфейс IPluginProvider, который может обнаружить ваше приложение. У этого должен быть конструктор без параметров, поэтому вы можете легко удалить экземпляр объекта new. Затем он должен иметь методы, которые возвращают любые метаданные (такие как «красивые имена» для параметров, которые требуются, какие разумные значения по умолчанию и т. Д.). Затем он должен включать метод CreateInstance, который принимает фактические параметры как IDictionary<string,object> и возвращает фактический пример IPlugin.

3

Вместо того, есть плагин конструктор определить параметры, вы могли бы рассмотреть что-то вроде этого:

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

Это дает большую гибкость для параметров, как вы можете добавить атрибуты, обеспечивают проверку сеттер и даже указать, дизайнер/конвертер для сетки свойств. Сетка свойств подключается непосредственно к объекту параметров.

+0

Мне очень нравится эта идея и исследую ее дальше. – Chris

+0

Это очень простая идея, мне это нравится. Однажды я посмотрел на MEF. Это было слишком много для простой архитектуры плагинов. – NotMe

+0

@ Крис, MEF, безусловно, стоит изучить, так как он отлично справляется с поиском и инстанцированием/инъекцией. Также есть хороший шанс, что он станет стандартным способом сделать это, поскольку теперь он включен в структуру. Если ваши требования к плагину усложняются, плагин, желающий взаимодействовать с хост-средой через служебные интерфейсы, MEF - хороший способ. Конечно, даже если вы используете MEF для проводки, вам все равно потребуется что-то подобное, чтобы инкапсулировать ваши параметры. –

0

Я не смотрел MEF (сделаю это сейчас).

У меня была проблема, почти идентичная вашей, я решил ее с помощью Attributes.
У меня есть пользовательский интерфейс, который (называет BL, который) использует отражение, чтобы показать все доступные «сервисы» (не более, чем соответственно оформленные классы).

Когда пользователь выбирает «службу», дополнительные атрибуты управляют пользовательским интерфейсом. Атрибут «схема» довольно прямолинейный и позволяет использовать любое количество параметров с любым именем.Вводя константы (с определением атрибута), вы можете стандартизировать общие вещи, такие как «имя», чтобы ваши службы были согласованными.

Все данные затем сохраняются в таблице пар ключей.

Замечательно, что вы можете просто сбрасывать новые/измененные «сервисные» сборки в мусорном баке - никакой дополнительной работы не требуется. Единственная зависимость - это сборка атрибутов - так что держите этот пост.

Исходный код находится в CodePlex, если вы хотите, чтобы «украсть» некоторые :)