簡體   English   中英

.NET自定義配置部分:Configuration.GetSection引發“無法找到程序集”異常

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

我為插件DLL創建了一個自定義配置部分,該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時,我在XML示例中標出的行上捕獲到ConfigurationErrorsException ,指出找不到程序集MyPlugin或其依賴項之一(請原諒,我沒有發布原始的異常消息,但是我只有德語,並且懷疑本文是否對您有幫助。 內部異常來自System.IO ,它試圖加載程序集並獲得反射以解析“ MyConfigurationSettings”類類型。

為了解決這種情況,將上面的代碼放置在框架DLL(程序集)中,該DLL又由從主應用程序加載的實際插件DLL引用。

以下UML圖說明了幾個組件之間的關系: App的主要插件和框架組件

PluginFramework了這個問題之后,我覺得有必要對導出MyConfigurationSettings類(即PluginFramework )的程序集進行強名稱(簽名)並在GAC中注冊它。 我還沒有嘗試過,並且出於幾個原因希望避免執行此步驟(在知道它是否甚至可以有所幫助之前,這是解決問題的唯一選擇)。

因此,這里有一些問題(很抱歉,我實際上在這里放置了4個問題,但是它們之間的聯系是如此緊密,以至於無法為他們創建單獨的SO問題)。

  1. 我可以通過強命名相關程序集並將其注冊到GAC來解決定位失敗問題嗎?

  2. 足夠愚蠢的配置管理器抱怨的程序集,可以保證被加載(因為它調用Configuration.GetSection()本身)。
    是否可以通過ConfigurationManagerConfguration類顯式注冊程序集或適當的配置類型解序列器?

  3. 我還對有關Hans Passant的評論的更多信息感興趣,他提到這可能是由於從主應用程序加載(主)程序集而引起的問題。 我無法控制此機制,如果這會導致此行為固有,我想知道是否有合理的解決方法?

  4. 另一個想法(如果以上任何內容都無法顯示出一種方法)是完全本地管理配置XML格式(使用XML反序列化支持)以及從何處加載和合並配置文件。 如果這是最合適的選擇,那么誰能為您提供有效執行此操作的最佳指示(用於管理路徑和合並的最少必需代碼)?

更新:
由於似乎沒有人能夠對這個問題提供更多的見解(這2個答案並不能使我更進一步),所以我將選項從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();
    }
}

.NET Framework不支持您要執行的操作。

1-有意義的是,plugin.dll是針對使用它的每個主機應用程序(.exe或Web)進行配置的(這就是為什么它是可配置的)

第二-配置文件支持繼承(例如:machine.config-> applicationHost.config-> web.config)。 那是他們的目的。 您的路徑配置在這方面將無法正常工作。

因此,如果您需要對應用程序或插件的一部分進行自定義配置,而不遵循.config概念,請制作標准XML文件或jsonconfig並從中加載設置。

@ 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>

在使用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;
....

負責配置處理的類:

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

我希望這有幫助

我遇到了同樣的問題,到目前為止還沒有找到一個完全令人滿意的解決方案。 我們的應用程序負載是通過引用專用程序集中定義的配置節類來編譯的。 該應用程序與一個強命名程序集鏈接,但是稍后當配置加載器嘗試讀取配置時,融合跟蹤表明它嘗試加載同一程序集的弱名稱。 由於某種原因,.net無法看到它是同一程序集,並引發System.IO.FileNotFound異常。 在我的情況下,工作解決方案是在配置中引用強名稱。

我還注意到一種奇怪的行為:.net“配置加載程序”使用強名稱加載程序集后,使用弱名稱的進一步引用實際上會成功! 出於某種原因,該框架“記住”弱名稱屬於同一程序集。

您發出的有關該問題的任何新聞都會很有趣!

AppDomain.CurrentDomain.AssemblyResolve添加事件處理程序。 這應該適用於option 2

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM