Cutting Edge

ClearScript の概要

Dino Esposito

コード サンプルのダウンロード

Dino Esposito数年前、Active Server Pages の VBScript エンジン全体を Visual Basic アプリケーション内でホストするという可能性に魅了されていました。これを実現できれば、記事の内容を CD で販売しようと考える企業向けに驚くべき概念実証を作成でき、ローカルまたはリモートの Web サーバーの外部で既存の Active Server Pages コンテンツを再利用できるようになります。

当時は 1990 年代後半で、Microsoft .NET Framework はありませんでした。HTML5 もありませんでした。ほんの一握りの開発者が積極的にダイナミック HTML を詳しく調べていましたが、スクリプト エンジンを容易にホストできるようにはなりませんでした。当時、スクリプト エンジンをホストできるようにするには、スクリプト環境内で ActiveX コントロールを参照し、独自の ActiveX オブジェクトを公開する必要がありました。

最近、クエリによって SQL Server からテキスト ファイルを 抽出する最も効果的な方法について問い合わせを受けました。この問い合わせは通常担当しているトピックの範囲外なので、「申し訳ありませんが、分かりません」と答えようと思いましたが、私はこの顧客がデータベースの操作に長けていることを知っていました。そのため、この質問の裏には何か別の意味があるのではないかと考えました。

顧客は、SQL Server のインスタンス内のデータベース テーブルに格納されているコンテンツから、プレーン テキスト ファイル (主に CSV ファイルや XML ファイル) を定期的に作成していました。これは、大部分が、通常、業務上の問題による緊急性の高い業務部門からの要請になるため、データベース担当者にとってはわずらわしい作業です。少なくとも SQL Server 環境には、反復可能なルーチンを作成できる再帰ロジックはありません。

結局、その顧客が求めていたのは、業務部門の担当者が VBScript などの簡単なスクリプト言語を使用してプログラムを作成できるツールでした。ユーザーは、データベースへのアクセス許可を読み取り専用に制限する必要があります。当然、そのツールを使用して、ユーザーがテキスト ファイルを簡単に作成できなければなりません。このことにより、ActiveX と VBScript を使用していた日々を思い出し、ClearScript (clearscript.codeplex.com、英語) という比較的新しいライブラリを知ったときは、なんとなく悔しい思いが浮かび上がってきました。

ClearScript の Windows Presentation Foundation への統合

ClearScript は、スクリプト機能を .NET アプリケーションに追加できるようにします (.NET Framework 4 以上を使用している場合のみ)。ClearScript は VBScript、JavaScript、および V8 をサポートします。V8 は Google が作成したオープン ソース JavaScript エンジンで、Chrome に統合されています。また、V8 はパフォーマンスが高い JavaScript エンジンで、マルチスレッドの非同期運用シナリオに適しています。

ClearScript を .NET アプリケーションに追加した場合の実際の効果は、JavaScript や VBScript の式をエンジンに渡して、その式を処理および実行できることです。興味深いことに、使用可能なオブジェクトは、配列、JSON オブジェクト、プリミティブ型などのプレーン スクリプト オブジェクトに限定されません。外部 JavaScript ライブラリやスクリプト マネージ .NET オブジェクトを統合することができます。

ClearScript をアプリケーションに統合したら、後はスクリプトを作成できるオブジェクトをライブラリに通知するだけです。つまり、独自のオブジェクトを ClearScript のコンテキストに公開し、承認されたユーザーが既存のスクリプトを読み込んで実行したり、新しいスクリプトを作成できるようにします。

カスタマイズの層を追加し、ユーザーにそれほど負荷がかからずに独自のロジックを追加できるようにする場合、当然 ClearScript は不可欠ですが、それだけでは十分でないことがあります。ClearScript はジグソー パズルのピースにすぎません。ユーザーが独自のスクリプトを管理する方法を提供する必要があります。ファイル作成などの一般的なタスクを簡略化するアドホック オブジェクトを作成することも必要です。

ここでは、顧客が Web API バックエンドを通じて公開される一連のサービスからテキストや XML レポートを生成できるよう、今回行ったサポートを紹介します。一番の機能要件は、ユーザーがテキスト ファイルを作成できるようにすることです。概念実証のため、ClearScript をホストするシェル アプリケーションが必要です。そこで、スクリプト コードを手動で入力するテキストボックスを備えた Windows Presentation Foundation (WPF) アプリケーションを作成することにします。その後、既定の入力フォルダーと、既存のスクリプト ファイルを開く/インポートするための UI のサポートを追加します。図 1 に、今回の WPF サンプル アプリケーションを示します。

ClearScript エンジンをホストする Windows Presentation Foundation サンプル アプリケーション
図 1 ClearScript エンジンをホストする Windows Presentation Foundation サンプル アプリケーション

繰り返しになりますが、ClearScript は、アセンブリをリンクすることでプロジェクト内で直接参照できるオープン ソース プロジェクトです。サードパーティ製の NuGet パッケージの使ってインストールすることも可能です (図 2 参照)。

NuGet による ClearScript のインストール
図 2 NuGet による ClearScript のインストール

ClearScript の初期化

スクリプト エンジンをプログラムで使用できるようにするには、いくつか作業が必要ですが、セットアップが完了したら最終的には図 3 のコードを使用するだけでスクリプト コードの実行をトリガーできます。

図 3 スクリプト コードをトリガーするコード

public void Confirm()
{
  try
  {
    SonoraConsole.ScriptEngine.Execute(Command);
    OutputText = SonoraConsole.Output.ToString();
  }
  catch(Exception e)
  {
    OutputText = e.Message;
  }
}

Confirm メソッドは、サンプル アプリケーションのメイン ビューをサポートするプレゼンター クラスに属します。[Run] ボタン (図 1 参照) をクリックするとメソッドがトリガーされます。リストで参照されている SonoraConsole クラスは今回独自に作成した ClearScript ライブラリの中核となるクラスのラッパーです。

ClearScript エンジンの初期化はアプリケーションの起動時に行われ、XAML Application クラスの Startup イベントにバインドされます。

public partial class App : Application
{
  void Application_Startup(Object sender, StartupEventArgs e)
  {
    SonoraConsole.Initialize();
  }
}

初期化は、好みに応じて複雑にも洗練されたものにもなりますが、最低でも選択した言語のスクリプト エンジンを初期化する必要があります。スクリプト エンジンのインスタンスをアプリケーションの他の部分でも使用できるようにする必要があります。この方法の一例を次に示します。

public class SonoraConsole
{
   public static void Initialize()
   {
     ScriptEngine = new VBScriptEngine()
   }
   public static ScriptEngine ScriptEngine { get; private set; }
   ...
}

アプリケーション内で有効にするスクリプト言語を構成ファイルから読み取ってもかまいません。この場合の構成スキーマの一例を次に示します。

<appSettings>
  <add key="language" value="vb" />
</appSettings>

選択したスクリプト エンジンのインスタンスの準備が整ったら、有効な JavaScript (または VBScript) コードを実行できます。実際には、これらの基本機能の準備が整うまで、できることはほとんどありません。

スクリプトを作成できるオブジェクトの追加

すべての ClearScript エンジンは、プログラミング可能なインターフェイスを公開しているため、このインターフェイスを使用してスクリプトを作成できるオブジェクトをランタイム環境に追加できます。具体的には、次のように AddHostObject メソッドを使用します。

ScriptEngine.AddHostObject("out", new SonoraOutput(settings));
ScriptEngine.AddHostObject("xml", new XmlFacade());

このメソッドには 2 つのパラメーターが必要です。1 つ目のパラメーターはパブリック名で、エンジンはこの名前を使って、公開するオブジェクトを参照します。2 つ目のパラメーターは単なるオブジェクト インスタンスです。上記のコードを見ると、JavaScript や VBScript では、"out" という名前を使用して SonoraOutput インターフェイスで使用可能なパブリック メソッドを呼び出せることが分かります。図 1 に示した内容を参照する JavaScript の例を次に示します。

var x = 4;
out.print(x + 1);

JavaScript ではキャメルケース規約に従ってメンバーの名前を付けるのが一般的です。.NET プログラミングでは、パスカルケース規約がよく使われ、推奨されます。SonoraOutput クラスの実装時では、プレーンな C# プログラミングで使用されることを考えて、意図的に JavaScript 規約に従うことを選択し、Print ではなく print メソッドを呼び出しています。

私の経験によると、ClearScript の使用開始時に理解しておくべき事項はこれだけです。ほとんどの場合、アプリケーション固有のオブジェクトを使用できるようにすることを主な目的として、ホスト アプリケーション内で ClearScript 環境を構成できます。また、大半のオブジェクトは既存のビジネス オブジェクトをラップするようにカスタマイズされたオブジェクトで、スクリプト環境内で使用する方が適しています。

ClearScript 環境の主なユーザーは、通常、フルタイムの開発者ではなく、ソフト開発のスキルを持つユーザーであることがほとんどです。このようなユーザーは、.NET クラスの細部にまで対処することを、複雑でわずらわしいと考えます。ClearScript では、.NET Framework の大部分を直接 JavaScript や VBScript に公開できます。ここでは非常に説明をわかりやすくするため、独自にカスタマイズしたオブジェクトを使用します。ClearScript では、オブジェクトではなく型を以下のように公開します。

ScriptEngine.AddHostType("dt", typeof(DateTime));

型を参照すると、ユーザーはその型のインスタンスをプログラムで作成できるようになります。たとえば、上記のコード行では、.NET の DateTime オブジェクトの機能をスクリプト環境に追加しています。その結果、次のような JavaScript コードを作成できるようになります。

var date = new dt(1998, 5, 20);
date = date.AddDays(1000);
out.print(date)

JavaScript コード内から、AddDays メソッドや AddHours メソッドなど、DateTime オブジェクトの全メソッドを利用できるようになります。2 つの日付の差を求める場合は、次のようなコードを使用します。

var date1 = new dt(1998, 5, 20);
var date2 = date1.AddDays(1000);
var span = date2.Subtract(date1);
out.print(span.Days)

TimeSpan オブジェクトが適切に処理され、span.Days 式によって 1000 が返されます。このような処理が可能なのは JavaScript 言語の動的な性質によるもので、"span" という名前のオブジェクトが Days という名前のメンバーを公開することが動的に決定されます。代わりに TimeSpan インスタンスを作成する場合は、最初にそのインスタンスの存在をエンジンに通知する必要があります。

さまざまな型が大量に公開されないように、ClearScript を使用してアセンブリ全体をホストすることができます。この方法の例を次に示します。

ScriptEngine.AddHostObject("dotnet",
  new HostTypeCollection("mscorlib", "System.Core"));

dotnet キーワードが、mscorlib と System.Core 内のすべての型と静的メンバーにアクセスするためのキーになります。新しい日付オブジェクトを作成するとコードは少し長くなりますが、代わりに TimeSpan オブジェクトを明示的に使用できます。

var date1 = new dotnet.System.DateTime(1998, 5, 20);
var ts1 = new dotnet.System.TimeSpan(24, 0, 0);
var ts2 = ts1.Add(new dotnet.System.TimeSpan(24, 0, 0));
out.print(ts2.Days);

この JavaScript コードは、それぞれ 24 時間カウントする 2 つの異なる TimeSpan オブジェクトの合計から、2 という数値を出力します。ClearScript で適切に機能しないものの 1 つが演算子のオーバーロードです。この機能は単純に存在しません。つまり、日数や時間間隔を合計する場合は、Add メソッドや Subtract メソッドを使用しなければなりません。リフレクションもサポートされます。

出力の生成

図 1 に示したツールでは、ユーザーになんらかの結果を表示できる必要があります。既定では、ClearScript エンジンに追加された SonoraOutput オブジェクトによって、StringWriter 内部オブジェクトが管理されるだけです。print メソッドによって処理されるテキストは実際にはすべて基になるライターに書き込まれます。ライターのコンテンツは、SonoraConsole クラスを通じて外部に公開されます。このクラスは、ClearScript エンジンとホスト アプリケーションの唯一の接点になります。ホスト アプリケーション プレゼンターは、プロパティを通じて文字列ライターのコンテンツを返すにすぎません。その後、このプロパティを、WPF UI の TextBlock にバインドします。print メソッドは文字列ライターを通じて UI に書き込みを行います。clr メソッドによってバッファーと UI をクリアします。

テキスト ファイルへの保存

顧客の要請は、テキスト ファイル (主に CSV ファイル) を作成することだけでした。これは比較的簡単に実現できます。file メソッドを作成し、そのメソッドに直接テキスト コンテンツを渡すだけです。また、このメソッドを使用して、既に画面に出力され、内部バッファーに保存されているものをすべて取得できるようにもします。ファイルに関して最も対処が難しいのは、名前付けとファイルの場所になります。スクリプトを高速で作成するには、ファイルを作成して取得するのが簡単です。今回は 2 つの既定のフォルダーを作成し、一方を入力用、もう一方を出力用にしました。また、ファイルはすべて TXT 形式で、ファイル名が指定されていなければ、既定の名前を使用するものとします。

このような前提は、シナリオによっては制限が厳しすぎる場合がありますが、今回作成したプロジェクトは、ファイルを保存するのではなく生成するツールの概念実証にすぎません。図 4 に示すとおり、XmlWriter オブジェクトを適切なコンポーネント内に簡単にラップし、スクリプトを使用して XML ファイルを作成できました。

スクリプトを使用した XML ファイルの作成
図 4 スクリプトを使用した XML ファイルの作成

まとめ

スクリプトを使用して XML ファイルを作成することは、実際、エンタープライズ アプリケーションにスクリプト機能を追加することと同じです。タスクを自動化したいので、スクリプトが必要です。場合によっては、アドホック テキストや XML ファイルの作成が必要になることもあります。おそらく、SQL Server でクエリを実行して CSV にインポートできますが、そのためには運用データベースに管理者権限でアクセスでき、さらには適切なスキルが必要になります。xp_cmdshell を使用して SQL Server クエリからテキスト ファイルを取得するのは難しいと考えられます。開発者にとって、スクリプト作成用に準備された使いやすいアドホックなオブジェクトを調整することは、難しい作業ではありません。

私が ClearScript の使用を好むように、顧客もこの方法を気に入ってくれました。顧客は私に、さらに多くのオブジェクトを動的環境に追加するよう依頼しました。最終的には、制御の反転機能を追加して、オブジェクトが起動時に読み込まれるように構成しました。また、新しいツールのリリース時に備えて、会社全体に ClickOnce 配置することも検討しています。


Dino Espositoは、『Microsoft .NET: Architecting Applications for the Enterprise』(Microsoft Press、2014 年)、および近日出版予定の『Programming ASP.NET MVC 5』(Microsoft Press、2014 年) の共著者です。JetBrains の .NET Framework および Android プラットフォームのテクニカル エバンジェリストでもあります。世界各国で開催される業界のイベントで頻繁に講演しており、software2cents.wordpress.com (英語) や Twitter (twitter.com/despos、英語) でソフトウェアに関するビジョンを紹介しています。

この記事のレビューに協力してくれたマイクロソフト ClearScript チームの技術スタッフに心より感謝いたします。