カスタム ASP.NET 式を使う

にはどうすれば良いかという話です。AquaSKK とは全然関係のない話です。

業務系の ASP.NET 開発ではエラーメッセージを一元管理したいというニーズが持ち上がると思いますが、大規模な開発だと複数のプロジェクト(ソリューション)に分割して影響範囲を狭め、並行開発をガガガっと進めることになります。そうすると、メッセージ ID などもフレームワーク的なものに同梱して各プロジェクトで共有したくなるのですが、標準的な埋め込みリソースだといまいち使い勝手が悪い。これをなんとかできないかと悩むわけです。

理想としては、プロジェクトテンプレートを配布してフレームワークの参照設定だけすれば、サクサク開発が進むようにしたい。以下、記憶から掘り起こして書いてみます(未コンパイル)。

MessageId

まずはメッセージ ID 用の単純なクラスを定義します。

public static class MessageId {
    public static string E0001 = "エラーメッセージ1";
    public static string E0002 = "エラーメッセージ2";
    // ...
}

このクラスは Excel 仕様書からの自動生成が前提になります(VBA マクロ)。コードからは MessageId.E0001 のようにしてアクセスできますね。So far so good.

ExpressionBuilder

メッセージ ID 用のクラスは定義できたので、これを検証コントロールで使いたい。

ASP.NET では「ASP.NET 式」なるものを使って様々な値をコントロールのプロパティに設定することができますが、標準では Web.config の特定のセクションかリソースだけしか設定できません。そこでまずは ExpressionBuilder を継承して MessageId 用の ASP.NET 式を生成するクラスを実装してみます。

static class Util {
    public static string GetMessage(string id) {
        var field = typeof(MessageId).GetField(id);
        return (field != null) ? field.GetValue(null) : id;
    }
}

[ExpressionPrefix("MessageId")]
[ExpressionEditor(typeof(MessageIdEditor))]
public class MessageIdBuilder : ExpressionBuilder {
    public override CodeExpression GetCodeExpression(BoundPropertyEntry entry, object parsedData, ExpressionBuilderContext context) {
        return new CodePrimitiveExpression(Util.GetMessage(entry.Expression.Trim()));
    }
}

要するに、"E0001" を評価して "エラーメッセージ1" を返すようなコードを生成して返しています。

ExpressionEditor

式の prefix として "MessageId" が選択された時に使用されるエディタを実装します。private クラスで OK。

class MessageIdEditor : ExpressionEditor {
    public override object EvaluateExpression(string expression, object parseTimeData, Type propertyType, ServiceProvider serviceProvider) {
        return Util.GetMessage(expression);
    }

    public override ExpressoinEditorSheet GetExpressionEditorSheet(string expression, IServiceProvider serviceProvider) {
        return new MessageIdEditorSheet(expression, serviceProvider);
    }
}

キモは、GetExpressionEditorSheet でカスタムプロパティシートオブジェクトを返すところです。

ExpressionEditorSheet

デザイン時に設定する一連のプロパティを定義するクラスです。private クラスで OK。

class MessageIdEditorSheet : ExpressionEditorSheet {
    public MessageIdEditorSheet(string expression, IServiceProvider serviceProvider) : base(serviceProvider) {
        MessageId = expression;
    }

    [TypeConverter(typeof(MessageIdListConverter))];
    [Description("メッセージ ID を設定します")]
    public string MessageId { get; set; }

    public string GetExpression() { return MessageId; }
}

MessageId プロパティを定義しています。Description 属性はデザイン時の説明に利用されるので、定義しておいたほうが親切でしょう。面倒くさければここで終わっても良いのですが、せっかくなので、MessageId をドロップダウンリストから選択できるようにするために、独自の TypeConverter を実装してみます。

TypeConverter

メッセージ ID をリストするための TypeConverter です。

class MessageIdListConverter : StringConverter {
    public bool GetStandardValuesExclusive(ITypeDescriptorContext context) { return true; }
    public bool GetStandardValuesSupported(ITypeDescriptorContext context) { return true; }

    public StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) {
        var list = new SortedList<string>();

        FieldInfo[] fields = typeof(MessageId).GetFields();
        foreach(var id in fields) {
            list.Add(id.Name);
        }

        return new StandardValuesCollection(list);
    }
}

MessageId クラスの全フィールドをリストして返しています。

Web.config

あとは Web.config に式ビルダを設定しておけば OK です。

<expressionBuilder>
  <add expressionPrefix="MessageId" type="MessageIdBuilder"/>
</expressionBuilder>

これで、検証コントロールASP.NET 式の prefix として MessageId を選択すれば、E0001 などがドロップダウンでリストできるようになるはずです。めでたしめでたし。

まとめ

カスタム ASP.NET 式を使う方法を調べてみました。案外簡単なので、活用したほうが幸せになれると思います。

それにしても、var による型推論は便利ですね。C++0x の auto も早く使いたいものです。