14

.config XML を (メインの実行可能アプリケーションとは) 別のファイルに格納するプラグイン DLL のカスタム構成セクションを作成しました。

カスタム セクション クラスのサンプルを次に示します。

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またはその依存関係の 1 つが見つからないことを示すメッセージが表示されます (元のファイルを投稿していないことをご容赦ください)。例外メッセージですが、私はドイツ語しか持っていません。このテキストがここで役立つとは思えません)。内部例外はSystem.IO、アセンブリを読み込んでリフレクションを取得して 'MyConfigurationSettings' クラス タイプを解決しようとしているときに発生します。

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

次の UML ダイアグラムは、いくつかのコンポーネントの関係を示しています。 メインアプリのプラグインとフレームワークのコンポーネント

この問題について少し調べてみたところ、MyConfigurationSettingsクラスをエクスポートするアセンブリ (つまりPluginFramework) に厳密な名前 (署名) を付け、それを GAC に登録する必要があると感じました。私はまだこれを試していませんでしたが、いくつかの理由でこの手順を避けたいと思います (それが役立つかどうか、問題を解決する唯一の選択肢であるかどうかを知る前に)。

ここに質問があります(申し訳ありませんが、実際には4つの質問をここに配置していますが、それらは非常に強く関連しているため、個別のSO質問を作成することは意味がありません)。

  1. 問題のアセンブリに厳密な名前を付けて GAC に登録することで、検索エラーの問題を解決できますか?

  2. 愚かなことに、構成管理が不平を言うアセンブリは、ロードされることが保証されています (Configuration.GetSection()それ自体を呼び出すため)。アセンブリまたは適切な構成タイプのデシリアライザー/シリアライザーをまたはクラス
    に明示的に登録する方法はありますか?ConfigurationManagerConfguration

  3. これは、(プライマリ) アセンブリがメイン アプリから読み込まれる方法によって引き起こされる問題である可能性があると言及しているHans Passant のコメントに関する詳細情報にも興味があります。私はこのメカニズムを制御できません。これが本質的にこの動作を引き起こす場合、合理的な回避策があるかどうか知りたいですか?

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

更新:
誰もこの質問についてより多くの洞察を与えることができないように思われるため (2 つの回答では実際にはそれ以上理解できません)、4. からオプションに変更し、すべて手動で行います。

4

5 に答える 5

4

私もそれを試しましたが、そのように機能することはありませんでした。.config を自動的にロードしても、.exe の場合のみ .dll の場合は機能しないと考えました。その後、あきらめて、.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 ではサポートされていません。

1 つ目 - plugin.dll が、それを使用しているホスト アプリケーション (.exe または Web) ごとに構成されていることは理にかなっています (そのため、構成可能です)。

2 つ目 - 構成ファイルは継承をサポートします (例: 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>

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

これが役立つことを願っています

于 2013-02-08T05:40:56.327 に答える