草庐IT

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

coder 2024-06-03 原文

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

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

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

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

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

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. 我的应用程序在编译时指定它支持的接口(interface)并忽略所有其他接口(interface) - 我看到一些系统声称可以使用开放插件完全扩展,我认为这意味着很多松散类型的接口(interface)和转换,是吗两个选项之间的中间立场,允许在不重新编译的情况下发布 future 的更新,但仍然使用具体的接口(interface)?

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

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

最佳答案

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

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

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

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

嗯...那是我长期收集的 2 美分。

关于C# 插件架构和对用户可配置数据库设置的引用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12922585/

有关C# 插件架构和对用户可配置数据库设置的引用的更多相关文章

  1. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  2. ruby - 如何每月在 Heroku 运行一次 Scheduler 插件? - 2

    在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/

  3. ruby-on-rails - 无法使用 Rails 3.2 创建插件? - 2

    我对最新版本的Rails有疑问。我创建了一个新应用程序(railsnewMyProject),但我没有脚本/生成,只有脚本/rails,当我输入ruby./script/railsgeneratepluginmy_plugin"Couldnotfindgeneratorplugin.".你知道如何生成插件模板吗?没有这个命令可以创建插件吗?PS:我正在使用Rails3.2.1和ruby​​1.8.7[universal-darwin11.0] 最佳答案 随着Rails3.2.0的发布,插件生成器已经被移除。查看变更日志here.现在

  4. ruby-on-rails - 独立 ruby​​ 脚本的配置文件 - 2

    我有一个在Linux服务器上运行的ruby​​脚本。它不使用rails或任何东西。它基本上是一个命令行ruby​​脚本,可以像这样传递参数:./ruby_script.rbarg1arg2如何将参数抽象到配置文件(例如yaml文件或其他文件)中?您能否举例说明如何做到这一点?提前谢谢你。 最佳答案 首先,您可以运行一个写入YAML配置文件的独立脚本:require"yaml"File.write("path_to_yaml_file",[arg1,arg2].to_yaml)然后,在您的应用中阅读它:require"yaml"arg

  5. ruby-on-rails - 使用 rails 4 设计而不更新用户 - 2

    我将应用程序升级到Rails4,一切正常。我可以登录并转到我的编辑页面。也更新了观点。使用标准View时,用户会更新。但是当我添加例如字段:name时,它​​不会在表单中更新。使用devise3.1.1和gem'protected_attributes'我需要在设备或数据库上运行某种更新命令吗?我也搜索过这个地方,找到了许多不同的解决方案,但没有一个会更新我的用户字段。我没有添加任何自定义字段。 最佳答案 如果您想允许额外的参数,您可以在ApplicationController中使用beforefilter,因为Rails4将参数

  6. Ruby Sinatra 配置用于生产和开发 - 2

    我已经在Sinatra上创建了应用程序,它代表了一个简单的API。我想在生产和开发上进行部署。我想在部署时选择,是开发还是生产,一些方法的逻辑应该改变,这取决于部署类型。是否有任何想法,如何完成以及解决此问题的一些示例。例子:我有代码get'/api/test'doreturn"Itisdev"end但是在部署到生产环境之后我想在运行/api/test之后看到ItisPROD如何实现? 最佳答案 根据SinatraDocumentation:EnvironmentscanbesetthroughtheRACK_ENVenvironm

  7. ruby - Ruby 有 `Pair` 数据类型吗? - 2

    有时我需要处理键/值数据。我不喜欢使用数组,因为它们在大小上没有限制(很容易不小心添加超过2个项目,而且您最终需要稍后验证大小)。此外,0和1的索引变成了魔数(MagicNumber),并且在传达含义方面做得很差(“当我说0时,我的意思是head...”)。散列也不合适,因为可能会不小心添加额外的条目。我写了下面的类来解决这个问题:classPairattr_accessor:head,:taildefinitialize(h,t)@head,@tail=h,tendend它工作得很好并且解决了问题,但我很想知道:Ruby标准库是否已经带有这样一个类? 最佳

  8. c# - 如何在 ruby​​ 中调用 C# dll? - 2

    如何在ruby​​中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL

  9. ruby - 一个 YAML 对象可以引用另一个吗? - 2

    我想让一个yaml对象引用另一个,如下所示:intro:"Hello,dearuser."registration:$introThanksforregistering!new_message:$introYouhaveanewmessage!上面的语法只是它如何工作的一个例子(这也是它在thiscpanmodule中的工作方式。)我正在使用标准的ruby​​yaml解析器。这可能吗? 最佳答案 一些yaml对象确实引用了其他对象:irb>require'yaml'#=>trueirb>str="hello"#=>"hello"ir

  10. C# 到 Ruby sha1 base64 编码 - 2

    我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha

随机推荐