10

我有一个可由用户配置的数据库应用程序 - 其中一些选项是从不同的外部插件系统中选择的。

我有一个基本插件类型,我的数据库架构具有相同的插件记录类型和相同的字段。我必须PlugingMananger在应用程序启动时加载插件(通过 IoC 容器)并将它们链接到数据库(基本上将磁盘上的插件字段复制到数据库)。

public interface IPlugin
{
    Guid Id{ get; }
    Version Version { get; }
    string Name { get; }
    string Description { get; }
}

然后可以使用 检索插件PlugingMananger.GetPlugin(Guid pluginId, Guid userId),其中用户 ID 是可能调用插件操作的多个用户之一的用户 ID。

应用程序已经预先声明了一组已知接口,每个接口都特定于某个功能(格式化程序、外部数据、数据发送器等),如果插件实现了一个未知的服务接口,那么它将被忽略:

public interface IAccountsPlugin : IPlugin
{
    IEnumerable<SyncDto> GetData();
    bool Init();
    bool Shutdown();
}

插件还可以PluginSettingAttribute在多用户系统中为每个用户定义设置属性 - 这些属性是在为特定用户检索插件时设置的,而PluginPropertyAttribute对于所有用户通用且插件一次只读设置的属性在应用程序启动时注册插件时。

public class ExternalDataConnector : IAccountsPlugin
{
    public IEnumerable<AccountSyncDto> GetData() { return null; }
    public void Init() { }
    public void Shutdown() { }

    private string ExternalSystemUsername;
    // PluginSettingAttribute will create a row in the settings table, settingId
    // will be set to provided constructor parameter. this field will be written to
    // when a plugin is retrieved by the plugin manager with the value for the
    // requesting user that was retrieved from the database.
    [PluginSetting("ExternalSystemUsernameSettingName")]
    public string ExternalSystemUsername
    {
        get { return ExternalSystemUsername }
        set { ExternalSystemUsername = value; } 
    }

    // PluginPropertyAttribute will create a row in the attributes table common for all users
    [PluginProperty("ShortCodeName")]
    public string ShortCode
    {
        get { return "externaldata"; }
    }

    public Version PluginVersion
    {
        get { return new Version(1, 0, 0, 0); }
    }

    public string PluginName
    {
        get { return "Data connector"; }
    }

    public string PluginDescription
    {
        get { return "Connector for collecting data"; }
    }
}

以下是我寻求指导的问题和领域:

  1. 通过上述将 IoC 容器中的插件链接到数据库的抽象,用户可以选择数据库字段Customer.ExternalAccountsPlugin = idOfExternalPlugin。这感觉很重 - 是否有其他系统实现这一点的更简单方法(例如,SharePoint 有很多用户数据库引用的插件)?

  2. 我的应用程序在编译时规定了它支持的接口并忽略了所有其他接口 - 我已经看到一些系统声称可以使用开放插件完全扩展,我认为这意味着许多松散类型的接口和强制转换,是否存在中间立场这两个选项允许在不重新编译的情况下发布未来更新但仍使用具体接口?

  3. 我的插件可能包含元数据(PluginProperty 或 PluginSetting),我不确定存储它的最佳位置,无论是在插件元数据表中(会使 linq 查询更复杂)还是直接在插件数据库记录行中(简单的 linq 查询PluginManager.GetPluginsOfType<IAccounts>.Where(x => x.ShortCode = "externaldata").FirstOrDefault();,即用作最佳实践?

  4. 由于插件功能和接口严重依赖于数据库模式,我可以限制插件与特定模式修订版一起使用的推荐方法是什么?我是否会将此架构修订作为单行保留在数据库的设置表中,并在每次发布后手动更新?插件是否支持最大架构版本,或者应用程序是否支持已知插件版本列表?

4

1 回答 1

3

1) 对不起,但我不确定。但是,我很确定,在由自定义插件创建或处理数据的软件中,它们会按照您描述的方式处理插件。这个想法是,如果用户加载数据但缺少该特定插件,则数据不会损坏,并且不允许用户修改该数据。(我想到的一个例子是一般的 3D 软件)

2)只给出非常严格的接口实现,当然对插件的创建有很大的限制。(例如:Excel,我无法创建新的单元格类型)它没有好坏之分,这在很大程度上取决于您想要从中获得什么,这是一个选择。如果您希望插件创建者仅通过一些非常特定的管道访问数据,限制他可以创建的数据类型,那么它与您的设计相一致。否则,如果您的目标是开放您的软件以进行改进,那么您还应该公开一些您认为足够安全以供外部使用的类和方法。(例如:Maya,我可以创建一个从基类派生的新实体类型,而不仅仅是一个接口)

3)嗯,这取决于很多事情,不是吗?序列化数据时,您可以创建一个包装器,其中包含特定插件、ID、元数据以及您认为需要的任何其他信息的所有信息。我会走那条路,因为它更容易检索,但它是满足您需要的最佳方式吗?没有更多信息很难说。

4) Firefox 就是一个很好的例子。较小的版本增量不会改变插件的兼容性。考虑到插件实现的内容,如果插件仍然有效,则从数据库进行中等版本增量测试。如果插件没有实现一些改变,它仍然有效。主要版本增量需要重新编译所有插件以使用新定义。从我的角度来看,这是一个很好的中间地带,它允许开发人员不必总是重新编译,但它使主要软件的开发变得更加棘手,因为必须提前计划更改。这个想法是平衡软件开发人员和插件开发人员之间的 PitA(屁股痛)因素。

嗯……那是我长期收藏的 2 美分。

于 2012-10-16T20:30:27.283 回答