App.configやWeb.configに固有のセクションを追加する方法を掲載します。今回無意識にASP.NETでサンプルプログラムを作成してしまいましたが、ASP.NETに特化した内容ではありません。

今回掲載する方法は次の4種類です。ConfigurationSectionGroupを使った構成のグループ化は掲載していないです。

方法 特徴
NameValueFileSectionHandlerを使用する セクションの値はNameValueCollectionとして表現されます。
DictionarySectionHandlerを使用する セクションの値はHashtableとして表現されます。
SingleTagSectionHandlerを使用する セクションの値はHasttableとして表現されます。ただし、キー,値のペアは属性とし記述します。
ConfigurationSectionを継承し、独自のセクションを作成する 独自のセクションを現すクラスを作成します。もっとも柔軟性が高いですが、コードを書く必要があります。

確認環境

  • ブラウザIE7
  • 開発環境Visual Studio 2008 Professional 英語版
  • .NET のバージョン3.5(2.0以降なら問題ないはず)

1.サンプルプロジェクトの作成

ソリューションを新規作成し、Webサイトプロジェクトを新規作成します。

ConfigurationSectionを継承して、独自のセクションを作成する方法以外は、構成ファイルのconfigSectionsにカスタムセクションを登録し、実際にカスタムセクションを追加するだけで使えるようになります。

1.1 カスタムセクションの構成

ConfigurationSectionを作成するのに使用するスキーマは次の定義例になるようにしました。

<custom.customsection>
  <customelements>
    <addname name="key1" value="3" />
    <addname name="key2" value="6" />
  </customelements>
  <customText message="hello world" />
</custom.customsection>

 最も外側のタグ名custom.customsectionは構成ファイルにカスタムセクションを登録するときに決定される要素名で、実体はConfigurationSectionを継承して定義します。内部のタブcustomTextは単一のタグで表現する要素で、ConfigurationElementを継承して作成します。customeelements要素はappSettingセクションのようにタグのコレクションを含むことができるタグで、ConfigurationSectionGroupを継承して実装します。customelementsの子タグはcustomText同様、ConfigurationElementを継承して作成します。

1.2カスタムセクションの作成

App_Codeフォルダが無ければ作成し、App_Codeフォルダを右クリック→Add New Itemをクリックし、クラスファイルCustomSection.csを作成します。

CustomSectionは次のように編集します。タグ名は、ConfigurationPropertyAttributeをアノテートして設定します。

using System;
using System;
using System.Data;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;

/// <summary>
/// Summary description for CustomSection
/// </summary>
public class CustomSection : ConfigurationSection
{
    public CustomSection()
    {
    }
    [ConfigurationProperty("customelements", IsRequired=true)]
    [ConfigurationCollection(typeof(CustomeGroup), AddItemName="addname",ClearItemsName="clearname", RemoveItemName="removename")]
    public CustomeGroup CustomElements
    {
        get { return (CustomeGroup)base["customelements"]; }
        set { base["customelements"] = value; }
    }
    [ConfigurationProperty("customText", IsRequired=true)]
    public CustomElement2 CustomString
    {
        get { return (CustomElement2)base["customText"]; }
        set { base["customText"] = value; }
    }
}

public class CustomeGroup : ConfigurationElementCollection
{
    public CustomeGroup()
    {
        // 追加するときに使用する属性名をここで指定もできる
        // AddElementName = "addname"; 
    }
    /// <summary>
    /// 子カスタムエレメントのコレクションのタイプ
    /// </summary>
    public override ConfigurationElementCollectionType CollectionType
    {
        get
        {
            return ConfigurationElementCollectionType.AddRemoveClearMap;
        }
    } 
    /// <summary>
    /// 子カスタムエレメントを作成
    /// </summary>
    /// <returns></returns>
    protected override ConfigurationElement CreateNewElement()
    {
        return (CustomElement)new CustomElement();
    }

    /// <summary>
    /// カスタムエレメントのキー名を返す
    /// </summary>
    /// <param name="element"></param>
    /// <returns></returns>
    protected override object GetElementKey(ConfigurationElement element)
    {
        return ((CustomElement)element).Name;
    }
    /// <summary>
    /// アクセッサ
    /// </summary>
    /// <param name="index"></param>
    /// <returns></returns>
    public CustomElement this[int index]
    {
        get { return (CustomElement)BaseGet(index); }
        set
        {
            if (BaseGet(index) != null)
            {
                BaseRemoveAt(index);
            }
            BaseAdd(index, value);
        }
    }
    /// <summary>
    /// アクセッサ
    /// </summary>
    /// <param name="name"></param>
    /// <returns></returns>
    public CustomElement this[string name]
    {
        get { return (CustomElement)BaseGet(name); }
    }
}
public class CustomElement : ConfigurationElement
{
    [ConfigurationProperty("name", IsRequired = true)]
    public string Name
    {
        get { return (string)base["name"]; }
        set { base["name"] = value; }
    }
    [ConfigurationProperty("value", DefaultValue = 0)]
    public int Value
    {
        get { return (int)base["value"]; }
        set { base["value"] = value; }
    }
}
public class CustomElement2 : ConfigurationElement
{
    [ConfigurationProperty("message")]
    public string Message
    {
        get { return (string)base["message"]; }
        set { base["message"] = value; }
    }
}

ソースでCustomeSectionがトップレベルのセクション,CustomElement2がcustomTextタグ、CustomGroupがcustomelementsを表現するクラスです。

2. 構成ファイルへのカスタムセクションの登録

Web.config(クライアントアプリの場合App.config)に次のようにconfiguSectionsにカスタムセクションを登録します。

<configuration>
  <configSections>
    <sectionGroup name="system.web.extensions" type="System.Web.Configuration.SystemWebExtensionsSectionGroup, System.Web.Extensions, ..">
    略..
    </sectionGroup>
    <section name="custom.namevaluesection" type="System.Configuration.NameValueFileSectionHandler, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" restartOnExternalChanges="false"/>
    <section name="custom.dictionarysection" type="System.Configuration.DictionarySectionHandler, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" restartOnExternalChanges="false"/>
    <section name="custom.singletagsection" type="System.Configuration.SingleTagSectionHandler, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" restartOnExternalChanges="false"/>
    <section name="custom.customsection" type="CustomSection, App_Code"/>
  </configSections>
  <!-- カスタムセクション -->
  <custom.namevaluesection>
    <add key="key1" value="namevalue value1"/>
    <add key="key2" value="namevalue value2"/>
  </custom.namevaluesection>
  <custom.dictionarysection>
    <add key="dic_key1" value="dictionaryvalue value1" />
    <add key="dic_key2" value="dictionaryvalue value1" />
  </custom.dictionarysection>
  <custom.singletagsection key1="single tag value1" key2="single tag value2" />
  <custom.customsection>
    <customelements>
      <addname name="key1" value="3" />
      <addname name="key2" value="6" />
    </customelements>
    <customText message="hello world" />
  </custom.customsection>
  <appSettings/>
  <connectionStrings/>
  <system.web> .. 略
  </system.web>
  .. 略
</configuration>

confrigurationSectionにNameValueFileSectionHandler,DictionarySectionHandler,SingleTagSectionHandlerと作成したCustomSectionを使うカスタムセクションを登録しています。

あとはconfigurationSectionsで登録した名前でカスタムセクションを使用しています。

3. プログラム上からカスタムセクションの値を取得

Webサイトプロジェクト作成時に既定で作成される、Derfault.aspx.csのPage_Loadイベントハンドラでカスタムセクションの値を取得して、ブラウザに表示するようにしています。

Default.aspx.csを以下のように編集

using System;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
using System.Collections.Specialized;
using System.Collections;

public partial class _Default : System.Web.UI.Page 
{
    protected void Page_Load(object sender, EventArgs e)
    {
        // NameValueFileSectionHandler, NaveValueCollectionが返る
        NameValueCollection nameValues = ConfigurationManager.GetSection("custom.namevaluesection") as NameValueCollection;
        if (nameValues != null)
        {
            Response.Write(nameValues["key1"]);
            Response.Write("<br/>");
            Response.Write(nameValues["key2"]);
            Response.Write("<hr/>");
        }

        // DictionarySectionHandler, Hashtableが返る
        Hashtable hashTable = ConfigurationManager.GetSection("custom.dictionarysection") as Hashtable;
        if (hashTable != null)
        {
            Response.Write(hashTable["dic_key1"]);
            Response.Write("<br/>");
            Response.Write(hashTable["dic_key2"]);
            Response.Write("<hr/>");
        }
        // SingleTagSectionHandler, シングルタグでキー,値のペアが属性になる, Hashtableが返る
        hashTable = ConfigurationManager.GetSection("custom.singletagsection") as Hashtable;
        if (hashTable != null)
        {
            Response.Write(hashTable["key1"]);
            Response.Write("<br/>");
            Response.Write(hashTable["key2"]);
            Response.Write("<hr/>");
        }
        // ConfigurationSectionを継承した場合、CustomSectionが返る
        CustomSection section = ConfigurationManager.GetSection("custom.customsection") as CustomSection;
        if (section != null)
        {
            foreach (CustomElement elem in section.CustomElements)
            {
                Response.Write(elem.Name);
                Response.Write("&nbsp;");
                Response.Write(elem.Value);
                Response.Write("<br/>");
            }
            Response.Write("<hr/>");
            Response.Write(section.CustomString.Message);
            Response.Write("<hr/>");
        }
    }
}

動作結果はこんな感じになります。

 

 

4. 任意の属性を設定できるようにするには

カスタムセクションで設定したカスタムプロパティ以外の属性をセクションに使用すると例外が発生します。任意の属性値を構成セクションに指定できるようにする場合は、ConfigurationElement.OnDeserializeUnrecognizedAttribute(string name, string value) をオーバーライドして、次のようにカスタム属性を実行時に設定するようにします。想定外の属性値を区別できるように、UnrecognizedAttribute に別途値を格納するようにしています。

private Hashtable _unrecoginizedAttribute;
public Hashtable UnrecoginizedAttribute
{
    get
    {
        if (this._unrecoginizedAttribute == null)
        {
            this._unrecoginizedAttribute = new Hashtable(StringComparer.InvariantCultureIgnoreCase);
        }
        return this._unrecoginizedAttribute;
    }
}
protected override bool OnDeserializeUnrecognizedAttribute(string name, string value)
{
    ConfigurationProperty property = new ConfigurationProperty(name, typeof(string), value);
    base.Properties.Add(property);
    base[property] = value;
    this.UnrecoginizedAttribute.Add(name, value);
    return true;
}

解説は以上です。間違い、指摘等があればご連絡ください。