2013-02-05 29 views
14

.config XMLを格納するプラグインDLLのカスタム構成セクションを、メインの実行可能アプリケーションファイルから作成しました。ここで .NETカスタム設定セクション:Configuration.GetSectionがアセンブリの例外を見つけることができません。

は、カスタムセクションクラスのサンプルです:

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はConfig.GetSection()を使用してロードしようとする

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

その後の実行で、XMLサンプルに記された行にConfigurationErrorsExceptionというキャッチフレーズがあり、アセンブリMyPluginまたはそれの依存関係の1つが見つかりませんでした(私が元の例外メッセージを投稿していないことを許してください私はそれをドイツ語でしか持っていないし、このテキストがここで役に立つとは思えない)。内部例外はアセンブリを読み込み、リフレクションを取得して 'MyConfigurationSettings'クラス型を解決しようとしているときにSystem.IOから発生します。

正確には、上のコードは、メインアプリケーションからロードされた実際のプラグインDLLによって参照されるフレームワークDLL(アセンブリ)内に配置されます。

以下のUML図は、いくつかのコンポーネントの関係を示しています。この問題について少し周りを見た後 Main App plugin and framework components

を、私はそれが強い名前(記号)MyConfigurationSettingsクラスをエクスポートアセンブリ(すなわちに必要だ感じていますPluginFramework)、それをGACに登録します。私はまだこれを試していないし、いくつかの理由でこのステップを避けたい(それが助けてくれるかどうかを知る前に、問題を解決する唯一の選択肢です)。

ここに質問があります(申し訳ありませんが、私はここで実際に4つの質問をしていますが、彼らは強く関連しているので、別々の質問があります)。

  1. 私は強い疑問にアセンブリを命名し、GACでそれを登録することにより、位置決め不良の問題を解決できますか?

  2. 設定管理が不満を抱いているアセンブリは、(それがConfiguration.GetSection()を呼び出すので)ロードされることが保証されています。
    ConfigurationManagerまたはConfgurationクラスを使用してアセンブリまたは適切な構成タイプのデシリアライザまたはシリアライザを明示的に登録する方法がありますか?

  3. 私は、Hans Passant's commentについてさらに詳しく知りたいと考えています。これは、(プライマリ)アセンブリがメインアプリケーションからロードされる方法に起因する問題かもしれないと言及しています。私はこのメカニズムを制御することはできません。これが本質的にこの動作を引き起こす場合は、妥当な回避策があるかどうかを知りたいのですが?

  4. もう1つのアイデア(上記のいずれかが方法を示していない場合)は、構成XMLフォーマットをネイティブに(XMLデシリアライゼーションサポートを使用して)完全に管理し、構成ファイルをどこからロードしてマージするかです。これが最も適切なオプションであれば、どのようにして効率的に(パスとマージを管理するために必要なコードを)行うのか、良い指針を与えることができますか?

更新:誰が(2つの答えは実際にはさらに私を得ることはありません)、この質問(複数可)についての詳細洞察力を与えることができるように思わないので
、私はからオプションに変更しています4.、それをすべて手作業でやってください。

+1

私は確かに言いますが、GACにいる必要はありません。まず、Section Handlerが残りのコードと同じディレクトリに存在することを確認します。次に、正しい構成マネージャーを使用していることを確認します。第3に、すべての{<! - Config properties ... - >}が "MyConfigurationSettings"のプロパティに対応していることを確認してください。これが始まります... そして4番目!あなたのクラスを見て...私はそれを正しく活用しているとは思わない。そこには "GetSection"ではなく、プロパティが必要です。これは、GetSectionを呼び出すフレームワークであり、フレームワークは、-callプロパティを指定する必要がある右セクションハンドラを見つけます。 –

+1

メインアプリケーションが強い名前の場合、参照するアセンブリもすべて厳密な名前でなければなりません。 – groverboy

+0

@ T.S。有望な音!質問を改善するためのいくつかの編集を見てください。前述のフレームワークDLLは、プラグインDLLと同じディレクトリにありますが、メインアプリケーションの実行可能ファイルと同じディレクトリにはありません。私は設定プロパティに関するあなたの意見は無関係かもしれないと思います(少なくともこの特定の例外については、.config XMLでマークした行について正確に文句を言います)。 'GetSection()'メソッドの利用について、これはこのセクションにアクセスするための便宜のために書かれていますが、私はこれをどこで、どのようにしてプロパティに置き換えることができないのでしょうか... –

答えて

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

:ここではG-makulik

@

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

ありがとう!私はそれを試してみる...奇妙な行動についてまだ興味がある... –

+0

今は時間がない。後で実生活の例が得られます。 –

+0

あなたのコードをさらに勉強してから、 'Configuration.GetSection()'をまったく呼び出さずに、ロードされマージされた設定から手で(文字通り)プロパティマッピングを行うのを避けることができます。あなたの答えにプロパティアクセスサンプルを追加しました。しかし、私はカスタマイズされた 'ConfigurationSettings'(' ConfigurationElementCollection'と 'ConfigurationElement'から派生した)にいくつかの複雑なプロパティを持っています。これらを扱う方法を見なければなりません。 –

0

私が作業を持っています実際の環境で何が行われたのかを確認し、動作することが証明されています。

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

、これは役に立ちます

+0

あなたのサンプルをありがとう、私はこれを試してみる必要があります... –

+0

ええと、私は(メイン/実行可能な)アプリケーションの設定ファイルを参照していないが、プラグインアプリケーションのために自分自身を作成し​​、それを管理したい別に(私が投稿したコンポーネント図のサンプルを見てください)。メインのアプリケーション構成内でセクショングループを使用することは、解決策かもしれませんが、私が探しているものではありません。 –

+0

私が試した第一のことは、 'Version = 1.0.0.0、Culture = neutral、PublicKeyToken = null'のものを除いた' section'要素の 'type'属性を適合させることでしたが、それは助けになりませんでした。私は 'DictionarySectionHandler'のコードを書き直しても、' Configuation'/'ConfigurationManager'クラスのアセンブリのロードの問題を本当に解決できるとは思いません。 –

1

実行しようとしていることは、.NET Frameworkではサポートされていません。マシン:

第一は - 設定ファイルの継承(EXをサポート - それは(それが設定可能である理由という)

第二は、それを使用しているあなたのplugin.dllがホストアプリケーション(.exeまたはウェブ)ごとに設定されていることを意味します。 config - > applicationHost.config - > web.config)。それが彼らのために設計されたもの。オフ・ザ・パスの設定は、その点では正しく動作しません。

したがって、.configの概念に従わないアプリケーションやプラグインの一部のカスタム設定が必要な場合は、標準のXMLファイルまたはjsonconfigを作成し、そこから設定を読み込みます。

+0

はい、これは事実ですが、@giamminに対する私のコメントの回答を見てください。現在、 'System.Configuration'名前空間の基底クラスと' ConfigurationManager'クラスと 'Configuration'クラスのサロゲートを使った私自身のXML直列化実装を再利用することを試みています。私が最終的に解決策をここに掲載します。 –

+0

それは私のポイントです。あなたは美容目的でのみSystem.Configurationを使用します。 app.configが動作すると予想され、プラグインのユーザーを混乱させる可能性があるため、動作しません。そして、正当な理由なしに実装にオーバーヘッドを追加しています。 代わりにPOCOを作成してXMLにシリアライズするか(intellisenseが必要な場合は+ XTD)、JSONにシリアライズします。 – Nenad

0

私は同じ問題を抱えていますが、今までのところ完全に不満足な解決策は見つかりませんでした。 アプリケーションの読み込みは、専用のアセンブリで定義された構成セクションクラスへの参照でコンパイルされます。 アプリケーションは厳密な名前のアセンブリにリンクされていますが、後で構成ローダーが構成を読み取ろうとすると、フュージョントレースは同じアセンブリの弱い名前を読み込もうとしていることを示します。何らかの理由で、.netが同じアセンブリであることを確認できず、System.IO.FileNotFound例外がスローされます。 私の場合の作業上の解決策は、設定で強い名前を参照することです。

また、奇妙な動作に気づきました。アセンブリ名が.netの "コンフィギュレーションローダ"によって厳密な名前でロードされると、弱い名前の参照が実際に成功します。 何らかの理由で、フレームワークは弱い名前が同じアセンブリに通っていることを「記憶」しています。

ご迷惑をおかけして申し上げます。

+0

私はこれをあきらめて、自分のアセンブリのための自分の設定ファイルフォーマットを選んだ。 –

0

AppDomain.CurrentDomain.AssemblyResolveのイベントハンドラを追加します。これはoption 2で有効です。

関連する問題