構成管理とDIの評価式

久々のDIコンテナネタ。

拙作のDIコンテナを使用した、.NET 2.0 WindowsForms用のフレームワーク開発も佳境に入っているが、そんな中、DIコンテナによるインジェクションの方法に関して、アプリケーション開発サイドから要望が発生した。

.NET C#言語は#if〜#endifというディレクティブを用いてコードを処理対象にしたり、処理対象から除外できるが、

#if develop
    Console.Writeline("Debug build");
#elif test
    Console.Writeline("Product build");
#elif product
    Console.Writeline("Product build");
#endif

このようなディレクティブ同様に、実行時に特定のビルド条件等でDIコンテナでインジェクションする値を含む評価式を、選択させたいというのだ。そこで設定ファイル(.config)の語彙をちょっと考えてみた。

例えば.NET FrameworkにおけるUriクラス型のプロパティに対して明示的にパスをインジェクションする、それも実行時にどこかでセットされた環境変数"build"を参照し、設定されている値によってインジェクションする値を変えるとする。


    
      http://localhost/app/endPoint
      http://testServer/app/endPoint
      http://xxx.xxx.xxx.xxx/app/endPoint
      http://localhost/app/endPoint
   

こんな感じで、環境変数"build"の値によってインジェクションする評価式の内容を変える訳だ。比較する値の中にある"$else"という値は大体想像が付くだろうが、他の全てのどの比較にも合致しなかった場合に評価されることを表す。
この時に与える環境変数は、例えばエントリポイントや、DIコンテナを生成する直前で以下のようなコードで値を設定すれば良い。

Environment.SetEnvironmentVariable("build", "develop"); //develop

機能の実装では、設定ファイルのパースから評価式を生成し、実行時に対象の環境変数の値により実際に評価する値を決定するが、長くなるのでコードは掲載しない。
なお、環境変数を設定、参照するには一定以上の権限が必要となる。従って環境変数に依存したくないというケースもあるだろう。そういった場合は環境変数の代わりになるアノテーション(カスタム属性)をアセンブリ単位に設定し、そのアノテーションに設定された値により、インジェクションする評価式を選択すれば良い。

アノテーションは、BuildParamAttributeという名前のカスタム属性クラスで実装しよう。

[AttributeUsage(
    AttributeTargets.Assembly, AllowMultiple = false, Inherited = false)]
[Serializable]
public class BuildParamAttribute : Attribute
{
    private string condition;
    public BuildParamAttribute(string condition)
    {
        this.condition = condition;
    }
    public string Condition
    {
        get { return this.condition; }
        set { this.condition = value;}
    }
}

このカスタム属性クラスはアセンブリにしか設定できない。従って、#ifディレクティブと共にAssemblyInfo.cs等に、以下のように設定しておく。

#if develop
  [assembly: BuildParam("develop")]
#elif test
  [assembly: BuildParam("test")]
#elif product
  [assembly: BuildParam("product")]
#endif

そうして同じような語彙を使い設定ファイルを書くが、カスタム属性の場合はテストする値がどのプロパティを指しているかを指定する属性が必要だ。従って属性"property="を語彙に追加している。


    
      http://localhost/app/endPoint
      http://testServer/app/endPoint
      http://xxx.xxx.xxx.xxx/app/endPoint
      http://localhost/app/endPoint
   

環境や構成によってパラメタを変えたいという要求は、実際の開発では必ず発生するので、DIコンテナ側でこのような対応が出来ると便利だろう。