简体   繁体   English

.NET自定义配置部分:Configuration.GetSection引发“无法找到程序集”异常

[英].NET custom configuration section: Configuration.GetSection throws 'unable to locate assembly' exception

I have created a custom configuration section for a plugin DLL that stores the .config XML in a separate (from the main executable application) file. 我为插件DLL创建了一个自定义配置部分,该DLL将.config XML存储在一个单独的文件中(与主要的可执行应用程序分开)。

Here's a sample of the custom section class: 这是自定义节类的示例:

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

The .config XML generated when saving 1st time looks like this: 第一次保存时生成的.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>

When this XML is tried to be loaded using Config.GetSection() on subsequent runs, I catch a ConfigurationErrorsException at the line marked in the XML sample, stating that the assembly MyPlugin or one of it's dependencies couldn't be located (please forgive that I'm not posting the original exception message, but I have it only in german, and doubt this text would be helpful here). 当尝试在后续运行中使用Config.GetSection()加载此XML时,我在XML示例中标出的行上捕获到ConfigurationErrorsException ,指出找不到程序集MyPlugin或其依赖项之一(请原谅,我没有发布原始的异常消息,但是我只有德语,并且怀疑本文是否对您有帮助。 The inner exception comes from System.IO while trying to load the assembly and get reflection to resolve the 'MyConfigurationSettings' class type. 内部异常来自System.IO ,它试图加载程序集并获得反射以解析“ MyConfigurationSettings”类类型。

To precise the situation, the code from above is placed inside a framework DLL (assembly), that in turn is referenced by the actual plugin DLL loaded from the main application. 为了解决这种情况,将上面的代码放置在框架DLL(程序集)中,该DLL又由从主应用程序加载的实际插件DLL引用。

The following UML diagram illustrates the several components' relationships: 以下UML图说明了几个组件之间的关系: App的主要插件和框架组件

After looking around a bit about this problem, I have the feeling it's necessary to strong name (sign) the assembly exporting the MyConfigurationSettings class (ie PluginFramework ) and register it with the GAC. PluginFramework了这个问题之后,我觉得有必要对导出MyConfigurationSettings类(即PluginFramework )的程序集进行强名称(签名)并在GAC中注册它。 I didn't try this yet, and would like to avoid this step for several reasons (before knowing if it could even help and it's the only choice to solve the problem). 我还没有尝试过,并且出于几个原因希望避免执行此步骤(在知道它是否甚至可以有所帮助之前,这是解决问题的唯一选择)。

So here are the questions (sorry I'm placing actually 4 questions here, but they're so strongly related that it wouldn't make sense to create separate SO questions for them). 因此,这里有一些问题(很抱歉,我实际上在这里放置了4个问题,但是它们之间的联系是如此紧密,以至于无法为他们创建单独的SO问题)。

  1. Could I solve the locating failure problem by strong naming the assembly in question and registering it with the GAC? 我可以通过强命名相关程序集并将其注册到GAC来解决定位失败问题吗?

  2. Stupidly enough the assembly the configuration management complains about, is guaranteed to be loaded (since it calls Configuration.GetSection() itself). 足够愚蠢的配置管理器抱怨的程序集,可以保证被加载(因为它调用Configuration.GetSection()本身)。
    Is there may be a way to register the assembly or the appropriate configuration type de-/serializers explicitly with the ConfigurationManager or Confguration class? 是否可以通过ConfigurationManagerConfguration类显式注册程序集或适当的配置类型解序列器?

  3. I'm also interested in more information about Hans Passant's comment mentioning this might be a problem caused by the way the (primary) assembly is loaded from the main app. 我还对有关Hans Passant的评论的更多信息感兴趣,他提到这可能是由于从主应用程序加载(主)程序集而引起的问题。 I have no control over this mechanism, and if this causes this behavior inherently I'd like to know if there's a reasonable workaround? 我无法控制此机制,如果这会导致此行为固有,我想知道是否有合理的解决方法?

  4. Another idea (if anything of the above fails to show a way) is to completely manage a configuration XML format natively (using XML de-/serialization support) and from where to load and merge the configuration files. 另一个想法(如果以上任何内容都无法显示出一种方法)是完全本地管理配置XML格式(使用XML反序列化支持)以及从何处加载和合并配置文件。 If this is the most appropriate option, can anyone give good pointers how to do this efficiently (least necessary code for managing paths' and merging)? 如果这是最合适的选择,那么谁能为您提供有效执行此操作的最佳指示(用于管理路径和合并的最少必需代码)?

Update: 更新:
Since no one seems to be able to give more insight about this question(s) (the 2 answers don't really get me further), I'm changing to option from 4., doing it all manually. 由于似乎没有人能够对这个问题提供更多的见解(这2个答案并不能使我更进一步),所以我将选项从4更改为手动完成。

I tried that as well, but I never got it to work like that. 我也尝试过,但是我从来没有那样做过。 I just figured loading a .config automatically doesn't work for .dll's only for .exe's. 我只是想自动加载.config不适用于.dll,而仅适用于.exe。 Then I gave up and decided it would be easier to just load the .config file manually. 然后我放弃了,决定手动加载.config文件会更容易。 You can see the full code here: https://github.com/GeertBellekens/Enterprise-Architect-Toolpack/blob/master/EANavigator/NavigatorSettings.cs This is the most relevant part: 您可以在此处查看完整的代码: 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();
 }

Accessing a configuration property: 访问配置属性:

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();
    }
}

What you're trying to do is not supported by .NET Framework. .NET Framework不支持您要执行的操作。

1st - it makes sense that your plugin.dll is configured per host application (.exe or web) that is using it (that why it's configurable) 1-有意义的是,plugin.dll是针对使用它的每个主机应用程序(.exe或Web)进行配置的(这就是为什么它是可配置的)

2nd - config files support inheritance (ex: machine.config -> applicationHost.config -> web.config). 第二-配置文件支持继承(例如:machine.config-> applicationHost.config-> web.config)。 That what they are designed for. 那是他们的目的。 Your off-the-path config would not work properly in that respect. 您的路径配置在这方面将无法正常工作。

So, if you need custom configuration for part of app, or plugin, not following .config concepts, make standard XML file, or jsonconfig and load settings from there. 因此,如果您需要对应用程序或插件的一部分进行自定义配置,而不遵循.config概念,请制作标准XML文件或jsonconfig并从中加载设置。

@g-makulik @ g-makulik

Here I have a working copy of what was done in real environment and proven to work. 在这里,我有一份在真实环境中所做的工作副本,并证明可以工作。

In the App.config file: 在App.config文件中:

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

In the class where you use config: 在使用config的类中:

....
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;
....

The class responsible for config handling: 负责配置处理的类:

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

I hope, this helps 我希望这有帮助

I am having the same problem and haven't found a fully satyisfying solution so far. 我遇到了同样的问题,到目前为止还没有找到一个完全令人满意的解决方案。 Our application loads is compiled with a reference to a configuration section class defined in a dedicated assembly. 我们的应用程序负载是通过引用专用程序集中定义的配置节类来编译的。 The application is linked with a strongly named assembly but later when the configuration loader tries to read the configuration, fusion traces show that it tries to load a weak name of the same assembly. 该应用程序与一个强命名程序集链接,但是稍后当配置加载器尝试读取配置时,融合跟踪表明它尝试加载同一程序集的弱名称。 For some reason, .net fails to see that it is the same assembly and throws a System.IO.FileNotFound exception. 由于某种原因,.net无法看到它是同一程序集,并引发System.IO.FileNotFound异常。 On working solution in my case is to reference strong names in the configuration. 在我的情况下,工作解决方案是在配置中引用强名称。

I have also noticed a strange behavior: Once an assembly is loaded by the .net "configuration loader" with a strong name, further references with a weak name actually succeed ! 我还注意到一种奇怪的行为:.net“配置加载程序”使用强名称加载程序集后,使用弱名称的进一步引用实际上会成功! For some reason, the framework "remembers" that the weak name reffers to the same assembly. 出于某种原因,该框架“记住”弱名称属于同一程序集。

Any news from the issue on your end would be interesting ! 您发出的有关该问题的任何新闻都会很有趣!

Add an event handler for AppDomain.CurrentDomain.AssemblyResolve . AppDomain.CurrentDomain.AssemblyResolve添加事件处理程序。 This should work for option 2 . 这应该适用于option 2

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 .Net Core Configuration.GetSection()。获取&lt;&gt;()不绑定 - .Net Core Configuration.GetSection().Get<>() not binding Configuration.GetSection返回null值 - Configuration.GetSection returns null value 为什么 static Configuration.GetSection() 不可用? - Why is static Configuration.GetSection() not available? 如何使用 FakeItEasy 语法模拟 configuration.GetSection? - How to mock configuration.GetSection with FakeItEasy syntax? Configuration.GetSection(“的connectionStringName”)。获取 <?> 总是为空 - Configuration.GetSection(“ConnectionStringName”).Get<?> always null ConfigurationManager.GetSection和Configuration.GetSection有什么区别? - What is Difference between ConfigurationManager.GetSection and Configuration.GetSection? C# 为对象列表创建 Mock Configuration.GetSection(“Section:SubSection”) - C# Create Mock Configuration.GetSection(“Section:SubSection”) for objects list configuration.getValue 或 configuration.getsection 总是返回 null - configuration.getValue or configuration.getsection always returns null Asp.Net Core 2.0 中的 Configuration.GetSection 获取所有设置 - Configuration.GetSection in Asp.Net Core 2.0 getting all settings Configuration.GetSection()轻松获取原始字符串值,但不获取复杂值 - Configuration.GetSection() easily gets primitive string values but not complex values
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM