アプリケーション構成ファイルを書く <カスタムセクション編>

System.Diagnostics 名前空間の Debug/Trace クラスのように、独自の構成セクションを定義することで、より多彩な設定を保存することができるようになります。

カスタムセクションの登録

まず、カスタムセクションを登録するところから始めなければなりません。カスタムセクションは configSections セクションにて宣言する必要があります。

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="MyConfig" type="System.Configuration.DictionarySectionHandler" />
  </configSections>
</configuration>

name 属性は構成セクションの名前、type 属性には構成セクションを読み込む System.Configuration.IConfigurationSectionHandler インターフェスの実装クラスを指定します。DictionarySectionHandler は appSettings と同様に add/remove/clear タグでキーと値のペアを読み取る実装を提供していますので、

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="MyConfig" type="System.Configuration.DictionarySectionHandler" />
  </configSections>
  <MyConfig>
    <add key="greeting" value="Hello, World." />
  </MyConfig>
</configuration>

という構成ファイルを読み込むことができるようになります。System.Configuration.ConfigurationSettings.AppSettings のかわりに、GetConfig() メソッドを利用し、

Hashtable myConfig = System.Configuration.ConfigurationSettings.GetConfig("MyConfig") as Hashtable;

として結果を取得できます。コードからわかるかもしれませんが、DictionarySectionHandler は Hashtable を出力として生成するようになっています。ちなみに、appSettings セクションそのものもマシン構成ファイルで上記のようにセクションが設定されていて、NameValueFileSectionHandler を利用しています。この NameValueFileSectionHandler ハンドラは file 属性による1階層の外部ファイル読み込みをサポートしており、上記の MyConfig セクションに対して指定した場合の外部ファイルは、

<?xml version="1.0" encoding="utf-8" ?>
<MyConfig>
  <!-- MyConfig の設定 -->
</MyConfig>

と、ルートノードが MyConfig セクションになっていなければならないです。まあ予想つくよね?

標準の構成セクションハンドラ

すでに書いたように、DictionarySectionHandler は add/remove/clear タグを読み込んで Hashtable を返します。NameValueSectionHandler と NameValueFileSectionHandler は出力として System.Collections.Specialized.NameValueCollection を返すだけで、DictionarySectionHandler との違いはありません。これは ConfigurationSettings.AppSettings が NameValueCollection 型ってところから予測できますね?
標準の構成ファイル読み込みハンドラは、あと2つあります。IgnoreSectionHandler と SingleTagSectionHandler です。名前から想像できるように IgnoreSectionHandler はカスタムセクションを完全に無視したい場合に設定します。これは、IConfigurationSectionHandler の実装を提供せず、ConfigurationSettings.GetConfig() を経由しないで内容を読み取るときに指定するらしいですが、そんなことをすると構成ファイルの階層構造とかの利点をまったく受けられなくなるので利用価値は低いと思われます。XML として読み込むにしても、XmlDocument や XmlNode のお世話になることが多いでしょうし、XmlNode を解析する処理を実装するなら IConfigurationSectionHandler を実装したって対してかわりないと思うんです。
もう1つの実装 SingleTagSectionHandler も、名前の通り構成セクション全体が単一のタグで構成されていることを想定されたハンドラです。SingleTagSectionHandler を設定した MyConfig セクションは

  <MyConfig Key1=Value1 Key2=Value2 Key3=Value3 />

のようになり、出力は Hashtable となります。これは add 専用の DictionarySectionHandler みたいなものですね。

ASP.NET のカスタムセクション追加属性

ASP.NET の構成ファイルの場合、Section タグに2つの属性が追加されます。allowDefinition と allowLocation です。allowDefinition は、カスタムセクションを定義することを許可される構成ファイルの種類、allowLocation は Web.config の Location タグの子要素としてカスタムセクションを許すかどうかを設定します。
省略すると、allowDefinition="Everywhere" allowLocation="true" となり、すべての場所でカスタムセクションが有効になります。

カスタムセクションを階層化する

System.Diagnostics などの標準ライブラリの構成セクションを見るとわかるように、カスタムセクションは階層化することができます。階層化には sectionGroup タグを利用して、

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <sectionGroup name="MyApp">
      <section name="Messages" type="System.Configuration.DictionarySectionHandler" />
      <section name="Label" type="System.Configuration.SingleTagSectionHandler" />
    </sectionGroup>
  </configSections>
  <MyApp>
    <Messages>
      <add key="greeting" value="Hello, World." />
    </Messages>
    <Label Font="MS UI Gothic" Size="9pt" Color="Red" />
  </MyApp>
</configuration>

こんな感じで宣言して内容を記述できます。この構成セクションにアクセスするためには ConfigurationSettings.GetConfig("MyApp/Messages") などと、 / を使って区切った文字列を指定します。"MyApp" で GetConfig() することはできません……ということは、System.Diagnostics セクションは下位の Switches セクションなどを ConfigurationSettings の機能を用いずに SectionHandler で処理しているということです。*1
sectionGroup は入れ子にして階層を多層化することができます。

*1:実際に、マシン構成ファイルを見れば、そのようになっていることを確認できます。