14

我为插件 DLL 创建了一个自定义配置部分,它将 .config XML 存储在一个单独的(来自主可执行应用程序)文件中。

这是自定义部分类的示例:

using System;   
using System.Configuration;

namespace PluginFramework.MyConfiguration
{

public class MyConfigurationSettings : ConfigurationSection
{
    private Configuration _Config = null;

    #region ConfigurationProperties     
    /// <summary>
    /// A custom XML section for an application's configuration file.
    /// </summary>
    [ConfigurationProperty("MyProjects", IsDefaultCollection = true)]
    public MyProjectConfigurationCollection MyProjects
    {
        get { return (MyProjectConfigurationCollection) base["MyProjects"]; }
    }

    // ...
    #endregion

    /// <summary>
    /// Private Constructor used by our factory method.
    /// </summary>
    private MyConfigurationSettings () : base () {
        // Allow this section to be stored in user.app. By default this is forbidden.
        this.SectionInformation.AllowExeDefinition =
        ConfigurationAllowExeDefinition.MachineToLocalUser;
    }

    // ...

    #region Static Members  
    /// <summary>
    /// Gets the current applications &lt;MyConfigurationSettings&gt; section.
    /// </summary>
    /// <param name="ConfigLevel">
    /// The &lt;ConfigurationUserLevel&gt; that the config file
    /// is retrieved from.
    /// </param>
    /// <returns>
    /// The configuration file's &lt;MyConfigurationSettings&gt; section.
    /// </returns>
    public static MyConfigurationSettings GetSection (ConfigurationUserLevel ConfigLevel) 
    {
        string appDataPath = System.Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
        string localDataPath = System.Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
        System.Configuration.ExeConfigurationFileMap exeMap = new ExeConfigurationFileMap();
        exeMap.ExeConfigFilename = System.IO.Path.Combine(appDataPath, @"MyCompany\MyPluginApp\Default.config");
        exeMap.RoamingUserConfigFilename = System.IO.Path.Combine(appDataPath, @"MyCompany\MyPluginApp\Roaming.config");
        exeMap.LocalUserConfigFilename = System.IO.Path.Combine(localDataPath, @"MyCompany\MyPluginApp\Local.config");

        System.Configuration.Configuration Config = ConfigurationManager.OpenMappedExeConfiguration(exeMap,ConfigLevel);
        MyConfigurationSettings myConfigurationSettings = null;

        try {
            myConfigurationSettings = (MyConfigurationSettings)Config.GetSection("MyConfigurationSettings");
        } 
        catch (System.Exception ex) {
            // ConfigurationErrorsException caught here ...
        }
        if (myConfigurationSettings == null) {
            myConfigurationSettings = new MyConfigurationSettings();
            Config.Sections.Add("MyConfigurationSettings", myConfigurationSettings);                    }
        } 
        if(myConfigurationSettings != null) {
            myConfigurationSettings._Config = Config;
        }

        return myConfigurationSettings;
    }       
    #endregion
}
} // PluginFramework.MyConfiguration

第一次保存时生成的 .config XML 如下所示:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <configSections>
        <!-- The exception complains about the following line (assembly attributes are compliant): -->
        <section name="MyConfigurationSettings" type="PluginFramework.MyConfiguration.MyConfigurationSettings, PluginFramework, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" allowDefinition="Everywhere" allowExeDefinition="MachineToLocalUser" />
    </configSections>
    <MyConfigurationSettings>
        <!-- Config properties are serialized fine according MyConfigurationSettings 
             properties marked with the ConfigurationProperty attribute ... -->
        <MyProjects>
            <MyProjectConfiguration GUID="{4307AC92-8180-4686-9322-830312ED59AB}">
                <!-- ... more complex configuration elements -->
            </MyProjectConfiguration>
        </MyProjects>
    </MyConfigurationSettings>
</configuration>

当尝试Config.GetSection()在后续运行中加载此 XML 时,我ConfigurationErrorsException在 XML 示例中标记的行中捕获一个,说明MyPlugin无法找到程序集或其依赖项之一(请原谅我没有发布原始异常消息,但我只有德语,并且怀疑此文本在这里会有所帮助)。内部异常来自System.IO尝试加载程序集并获得反射以解析“MyConfigurationSettings”类类型。

为了准确起见,上面的代码被放置在一个框架 DLL(程序集)中,而后者又被从主应用程序加载的实际插件 DLL 引用。

下面的 UML 图说明了几个组件的关系: 主要应用插件和框架组件

在环顾了一下这个问题之后,我觉得有必要对导出MyConfigurationSettings类(即PluginFramework)的程序集进行强命名(签名)并将其注册到 GAC。我还没有尝试过,并且出于几个原因想避免这一步(在知道它是否甚至可以帮助之前,这是解决问题的唯一选择)。

所以这里是问题(抱歉,我实际上在这里提出了 4 个问题,但它们之间的关系非常密切,因此为它们创建单独的 SO 问题是没有意义的)。

  1. 我可以通过强命名有问题的程序集并将其注册到 GAC 来解决定位失败问题吗?

  2. 配置管理抱怨的程序集很愚蠢,保证被加载(因为它调用Configuration.GetSection()自己)。
    有没有办法用ConfigurationManagerorConfguration类显式注册程序集或适当的配置类型反序列化器?

  3. 我也对有关Hans Passant 评论的更多信息感兴趣,他提到这可能是由从主应用程序加载(主)程序集的方式引起的问题。我无法控制这种机制,如果这会导致这种行为,我想知道是否有合理的解决方法?

  4. 另一个想法(如果上述任何方法都无法显示)是完全管理配置 XML 格式(使用 XML 反序列化支持)以及从何处加载和合并配置文件。如果这是最合适的选择,任何人都可以给出如何有效地做到这一点的好的指导(管理路径和合并的最少必要代码)?

更新:
由于似乎没有人能够对这个问题提供更多见解(2个答案并没有真正让我进一步了解),所以我从 4. 更改为选项,手动完成所有操作。

4

5 回答 5

4

我也试过了,但我从来没有让它像那样工作。我只是想自动加载 .config 不适用于 .dll 仅适用于 .exe。然后我放弃了,决定手动加载 .config 文件会更容易。您可以在此处查看完整代码:https ://github.com/GeertBellekens/Enterprise-Architect-Toolpack/blob/master/EANavigator/NavigatorSettings.cs 这是最相关的部分:

public NavigatorSettings() {
     Configuration roamingConfig = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoaming);

     // the roamingConfig now get a path such as C:\Users\<user>\AppData\Roaming\Sparx_Systems_Pty_Ltd\DefaultDomain_Path_2epjiwj3etsq5yyljkyqqi2yc4elkrkf\9,_2,_0,_921\user.config
     // which I don't like. So we move up three directories and then add a directory for the EA Navigator so that we get
     // C:\Users\<user>\AppData\Roaming\GeertBellekens\EANavigator\user.config
     string configFileName =  System.IO.Path.GetFileName(roamingConfig.FilePath);
     string configDirectory = System.IO.Directory.GetParent(roamingConfig.FilePath).Parent.Parent.Parent.FullName;

     string newConfigFilePath = configDirectory + @"\Geert Bellekens\EANavigator\" + configFileName;
     // Map the roaming configuration file. This
     // enables the application to access 
     // the configuration file using the
     // System.Configuration.Configuration class
     ExeConfigurationFileMap configFileMap = new ExeConfigurationFileMap();
     configFileMap.ExeConfigFilename = newConfigFilePath;       

     // Get the mapped configuration file.
     currentConfig = ConfigurationManager.OpenMappedExeConfiguration(configFileMap, ConfigurationUserLevel.None);
     // merge the default settings
     this.mergeDefaultSettings();
 }

访问配置属性:

public bool trackSelectedElement
{
    get {
        bool result;
        if(bool.TryParse(this.currentConfig.AppSettings.Settings["trackSelectedElement"].Value, out result)) {
            return result;
        }
        else {
            return true;
        }
    }
    set {
        this.currentConfig.AppSettings.Settings["trackSelectedElement"].Value = value.ToString();
    }
}
于 2013-02-06T04:24:23.960 回答
1

.NET Framework 不支持您尝试执行的操作。

第一 - 有意义的是,您的 plugin.dll 是为每个使用它的主机应用程序(.exe 或 web)配置的(这就是为什么它是可配置的)

第二 - 配置文件支持继承(例如:machine.config -> applicationHost.config -> web.config)。这就是他们的设计目的。在这方面,您的非路径配置将无法正常工作。

因此,如果您需要为部分应用程序或插件进行自定义配置,而不遵循 .config 概念,请制作标准 XML 文件或 jsonconfig 并从那里加载设置。

于 2013-02-14T22:39:23.477 回答
1

@g-makulik

在这里,我有一份在真实环境中完成的工作副本,并被证明是有效的。

在 App.config 文件中:

<configSections>
    <sectionGroup name="mySectionGroupName">
        <section name="mySectionName" type="MyNamespace.MySectionHandler,MyNamespace" />
    </sectionGroup>
</configSections>
....
<mySectionGroupName>
    <mySectionName>
        <add key="MyKey" value="MyKeyValue" />
    </mySectionName>
</mySectionGroupName>

在您使用配置的类中:

....
Hashtable ht = ConfigurationManager.GetSection("mySectionGroupName/mySectionName") as Hashtable; 
// when you call this, your handler will do what you want in there
string keyVal = ht["MyKey"] as String;
....

负责配置处理的类:

public class MySectionHandler : DictionarySectionHandler 
{
    public override object Create(object parent, object context, XmlNode section) 
    {
        // here do what you want with the value of "MyKey" - "MyKeyValue"
    }
}

我希望这有帮助

于 2013-02-08T05:40:56.327 回答
0

我遇到了同样的问题,到目前为止还没有找到一个完全令人满意的解决方案。我们的应用程序加载是使用对专用程序集中定义的配置节类的引用进行编译的。应用程序与强名称程序集链接,但稍后当配置加载器尝试读取配置时,融合跟踪显示它尝试加载同一程序集的弱名称。出于某种原因,.net 无法看到它是同一个程序集并引发 System.IO.FileNotFound 异常。在我的情况下,工作解决方案是在配置中引用强名称。

我还注意到一个奇怪的行为:一旦.net“配置加载器”使用强名称加载程序集,使用弱名称的进一步引用实际上会成功!出于某种原因,框架“记住”了弱名称指的是同一个程序集。

任何关于您的问题的消息都会很有趣!

于 2013-12-12T18:19:27.543 回答
0

为 .添加事件处理程序AppDomain.CurrentDomain.AssemblyResolve。这应该适用于option 2.

于 2015-09-09T16:06:58.530 回答