ASP.NE Web Siteプロジェクト(ASP.NET Web Applicationプロジェクトではありません)を作成すると、App_Codeに拡張子がxsd,wsdlなどのファイルを配置すると、自動的にプロキシが作成されます。これはWeb.configのsystem.webタグの直下にあるcompilationタブのbuildProvidersタグに拡張子に対応するBuildProviderが設定されているため、ビルドプロバイダがプロキシを作成することで実現されます。BuildProviderを作成するにはCodeDomの知識が必要です。

今回は同じようにカスタムビルドプロバイダを作成してみます。

動作確認環境

  • ASP.NETサーバー : Visual Studio 2008 Professional(英語版)組み込みサーバ
  • クライアント: IE 7.0
  • 開発環境: Visual Studio 2008 Professional 英語版
  • .NET 3.5(.NET 2.0以上ならOKのはず)

作成するプロジェクトの種類について
今回作成するのは"Web サイト"プロジェクトです。Webアプリケーションプロジェクトではないので注意して下さい。 ASP.NET Web Applicationプロジェクトでは、今回作成するBuildProviderはうまく動作しません。リンク先MSDNライブラリの中段のメモを参照して下さい。
- BuildProvider クラス
http://msdn.microsoft.com/ja-jp/library/system.web.compilation.buildprovider.aspx

1. カスタムBuildProviderが対象とするファイルのスキーマ定義

以下のxmlファイルを対象とします。ファイル名はStand.jojoとします。(ファイル名やスキーマはとくに深い意味はありませんので)

<?xml version="1.0" encoding="utf-8" ?>
<stands>
  <stand name="DeepPurple">
    <growth>S</growth>
    <power>3</power>
    <speed>4</speed>
  </stand>
</stands>

 2. ソリューションの作成

Visual Studio 2008を起動して、空のソリューションを新規作成します。ソリューション名はBuildProviderとしました。

2.1 カスタムBuildProviderの作成

ソリューションエクスプローラから、ソリューションを右クリック→Add→New ProjectからClass Libraryプロジェクトを作成します。プロジェクト名はStandBuildProviderとしました。

既定で作成されるClass1.csをStand.csにリネームし、以下のように編集します。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Compilation;
using System.Xml;
using System.CodeDom;
using System.IO;

namespace StandBuildProvider
{
    public class StandBuildProvider : BuildProvider
    {
        public override void GenerateCode(AssemblyBuilder assemblyBuilder)
        {
            XmlDocument xml = new XmlDocument();
            using (Stream s = OpenStream())
            {
                xml.Load(s);
            }

            XmlNode stand = xml.SelectSingleNode("/stands/stand");
            string standName = stand.Attributes["name"].Value;

            XmlNode growthPotential = stand.SelectSingleNode("growth");
            string growthPotentialValue = growthPotential.InnerText;

            XmlNode power = stand.SelectSingleNode("power");
            string powerValue = power.InnerText;

            XmlNode speed = stand.SelectSingleNode("speed");
            string speedValue = speed.InnerText;

            CodeCompileUnit ccu = new CodeCompileUnit();
            CodeNamespace cn = new CodeNamespace();
            cn.Name = "StandProxy";
            cn.Imports.Add(new CodeNamespaceImport("System"));

            CodeMemberProperty nameProp = new CodeMemberProperty();
            nameProp.Name = "StandName";
            nameProp.Type = new CodeTypeReference(typeof(string));
            nameProp.Attributes = MemberAttributes.Public;
            nameProp.GetStatements.Add(new CodeSnippetExpression("return \"" + standName + "\""));

            CodeMemberProperty growthProp = new CodeMemberProperty();
            growthProp.Name = "Growth";
            growthProp.Type = new CodeTypeReference(typeof(string));
            growthProp.Attributes = MemberAttributes.Public;
            growthProp.GetStatements.Add(new CodeSnippetExpression("return \"" + growthPotentialValue + "\""));

            CodeMemberProperty powerProp = new CodeMemberProperty();
            powerProp.Name = "Power";
            powerProp.Type = new CodeTypeReference(typeof(int));
            powerProp.Attributes = MemberAttributes.Public;
            powerProp.GetStatements.Add(new CodeSnippetExpression("return " + powerValue));

            CodeMemberProperty speedProp = new CodeMemberProperty();
            speedProp.Name = "Speed";
            speedProp.Type = new CodeTypeReference(typeof(int));
            speedProp.Attributes = MemberAttributes.Public;
            speedProp.GetStatements.Add(new CodeSnippetExpression("return " + speedValue));

            CodeTypeDeclaration ctd = new CodeTypeDeclaration(standName);
            ctd.Members.AddRange(new CodeTypeMember[] { nameProp, growthProp, powerProp, speedProp });

            cn.Types.Add(ctd);
            ccu.Namespaces.Add(cn);

            assemblyBuilder.AddCodeCompileUnit(this, ccu);
        }
    }
}

上記のプログラムにより、standタグの属性nameと名前が同じクラスが作成されます。作成されるプロキシクラスで、growth,speed,powerタグはプロパティとして現されます。

2.2 サンプルサイトの作成

ソリューションエクスプローラを右クリックし、Add→New Web Siteを選択し、WebSiteというWebサイトプロジェクト作成します。サイトプロジェクトを右クリック→Add Referenceを選択し、StandBuildProviderプロジェクトへの参照をプロジェクトに追加します。次のWeb.configを編集して、ビルドプロバイダと拡張子を関連付けます。

<system.web>
  <!-- 省略 -->
  <compilation debug="true">
    <buildProviders>
      <add extension=".jojo" type="StandBuildProvider.StandBuildProvider, StandBuildProvider"/>
    </buildProviders>
  </compilation>
</system.web>

次に1. カスタムBuildProviderが対象とするファイルのスキーマ定義で定義したxmlをStand.jojoという名前で、App_Codeフォルダに作成します。

すぐに、クラスが自動作成されます。クラス作成後はインテリセンスも働きます。以下の図は既定で作成されるDefault.aspx.csで自動生成されたクラスにインテリセンスを表示した例です。

 

説明は以上です。誤り、指摘とうがあればご連絡ください。