C# · 12月 27, 2021

C#插件架构和对用户可配置数据库设置的引用

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

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

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

然后可以使用PlugingMananger.GetPlugin(Guid pluginId,Guid userId)检索插件,其中用户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); } } public string PluginName { get { return “Data connector”; } } public string PluginDescription { get { return “Connector for collecting data”; } }}

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

>通过以上抽象将IoC容器中的插件链接到数据库,用户可以选择数据库字段Customer.ExternalAccountsPlugin = idOfExternalPlugin.这感觉很沉重 – 其他系统是否实现了更简单的方式(例如SharePoint具有大量用户数据库引用的插件)?
>我的应用程序在编译时指定它支持的接口并忽略所有其他接口 – 我已经看到一些系统声称可以使用开放式插件完全展开,我认为这意味着许多松散类型的接口和投射,是否有一个中间路在两个选项之间,这将允许未来的更新发布,而不重新编译,但仍然使用具体的接口?
>我的插件可以包含元数据(PluginProperty或PluginSetting),我不确定存储这个的最佳位置,在插件元数据表(将使linq查询更复杂)或直接插入数据库记录行(easy linq查询PluginManager. GetPluginsOfType< IAccounts> .Where(x => x.ShortCode =“externaldata”).FirstOrDefault();作为最佳实践使用?
>由于插件功能和接口在数据库架构上非常依赖,所以推荐的方法是限制一个插件与特定架构修订版本的配合使用?我将数据库中的设置表中的单个行保留在该模式修订版中,并在每次发布后手动更新?插件是否支持最大模式版本,还是应用程序支持已知插件版本的列表?

解决方法 1)对不起,我不知道.不过,我非常确定,在通过自定义插件创建或处理数据的软件中,他们按照您所描述的方式处理插件.这个想法是,如果用户加载数据但缺少该特定插件,则数据不会被损坏,并且不允许用户修改该数据. (我想到的一个例子是3D软件)

2)只有给出非常严格的界面实现,当然高度限制了插件的创建. (例如:Excel,我不能创建一个新的单元格类型)它不坏还是不错,它高度依赖于你想要的,这是一个选择.如果您希望插件创建者仅通过一些特定的管道访问数据,则可以限制他可以创建的数据类型,然后与您的设计一起使用.否则,如果您的目标是开发您的软件来改进,那么您还应该公开一些您认为足够安全的类和方法,以便在外部使用. (例如:Maya,我可以创建一个从基类派生的新实体类型,而不仅仅是一个接口)

3)嗯,这确实取决于很多事情,不是吗?序列化数据时,您可以创建一个包含特定插件,ID,MetaData以及其他任何您需要判断的信息的包装器.我会去那样,因为它会更容易检索,但它是你需要什么的最佳方法?很难说没有更多的信息.

4)一个很好的例子就是Firefox.较小的版本增量不会改变插件兼容性.如果插件仍然有效,则从数据库进行中等版本的增量测试,以考虑其实现的内容.如果插件没有实现改变的东西,它仍然有效.主要版本增量需要重新编译所有插件才能使用新的定义.从我的角度来看,这是一个很好的中间原因,允许开发人员不会总是重新编译,但是由于变更必须在未来计划中,因此主要软件的开发会更加棘手.这个想法是平衡软件开发人员和插件开发人员之间的PitA(Pain in the Ass)因素.

那么这是我的2美分的收集.