C#插件架构问题

时间:2010-09-28 20:13:34

标签: c# architecture

我正在开发类似于C#中Nagios的系统监控应用程序。我有一个插件接口定义为:

public interface IPlugin
{
    PluginResult Execute();
}

每个插件,根据其功能,将具有可变数量的参数。例如,ping插件可能会占用主机名,数据包数,超时值等。我希望用户能够在我的用户界面中为每个服务定义这些参数,但显然这些参数在应用程序发现哪些插件可用。我很好奇其他人如何设计一个插件,以便应用程序可以发现这些变量参数。

现在,作为一个例子,我有一个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
    }
}

我以为我可能能够使用反射发现构造函数参数并向用户显示属性网格以允许配置插件,但我不确定使用此方法提供一组默认值的最佳方法设计。可能有哪些替代方案?

5 个答案:

答案 0 :(得分:18)

您是否考虑过查看Managed Extensibility Framework

答案 1 :(得分:4)

不是让插件构造函数确定参数,你可能会考虑这样的事情:

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

这为参数提供了更大的灵活性,因为您可以添加属性,提供setter验证,甚至可以为属性网格指定设计器/转换器集成。属性网格直接挂钩到参数对象。

答案 2 :(得分:1)

您可以将[DefaultValue]属性应用于参数。

在C#for中,您可以使用新语法:int warningLossThreshold = 30,

答案 3 :(得分:0)

我也为MEF答案投了+1,这将解决你的许多问题。

但是,如果你想在没有MEF的情况下做到这一点,我觉得你错过了一些方法让插件通过元数据告诉你的应用程序,它需要的参数。

一种可能的设计可能是:拥有一个IPluginProvider接口,您的应用程序可以发现它。这应该有一个无参数构造函数,因此您可以轻松new一个实例。然后它应该有返回所需元数据的方法(例如参数的“漂亮名称”,这是必需的,什么是合理的默认值,等等)。然后应该包含CreateInstance方法,该方法将实际参数作为IDictionary<string,object>并返回实际的IPlugin实例。

答案 4 :(得分:0)

我没有看过MEF(现在会做)。

我遇到了与你几乎完全相同的问题,我用Attributes解决了这个问题 我有一个UI(调用BL)使用反射来显示所有可用的“服务”(仅仅是适当装饰的类)。

当用户选择“服务”时,其他属性会驱动UI。属性“schema”非常简单,允许任意数量的带有任何名称的参数。通过引入常量(使用属性定义),您可以标准化诸如“名称”之类的常见事物,以便您的服务保持一致。

然后将所有数据存储在键值对表中。

关于这一点的好处是你可以在新的bin目录中转储新的/修改过的“服务”程序集 - 无需额外的工作。唯一的依赖是属性定义程序集 - 所以保持这种精益。

如果您想“窃取”某些内容,则源代码位于CodePlex。)