次の方法で共有



February 2009

Volume 24 Number 02

"Oslo" の基礎 - "Oslo" プラットフォームでメタデータ ベースのアプリケーションを構築する

Chris Sells | February 2009

この記事は、"Oslo" プラットフォームのプレリリース バージョンに基づいています。ここに記載されているすべての情報は、変更される場合があります。

この記事では、次の内容について説明します。

  • "Oslo" とメタデータ
  • MGraph と MSchema
  • SQL スクリプト、パッケージ化、および展開
  • Visual Studio の統合
この記事では、次のテクノロジを使用しています。
"Oslo"、SQL Server 2008

目次

メタデータ
"Oslo" での構築
MGraph と MSchema
SQL の生成
パッケージ化と展開
Visual Studio の統合
サンプル コード

Microsoft .NET Framework をベースとするアプリケーションは、従来、一連のコンパイル済みコードと一連の関連リソースによって定義されていました。たとえば、ASP.NET アプリケーションは、Web サイトのロジックを提供するコンパイル済みコード (アセンブリ) と、ページそのものを定義するリソース ファイル (.aspx ファイル) によって定義されます。コードは CPU によって (コンパイル後に) 実行され、ページは ASP.NET ランタイムによって解釈されます。同様に、Windows Workflow Foundation (WF) でも動作を一連の構成済みアクティビティとして定義できます。定義したアクティビティはすべて、WF ランタイムによって解釈されるデータです。

UI の側に ASP.NET を組み合わせ、動作の側に WF を組み合わせることで、あらゆるアプリケーションをほぼ完全にデータとして構築し、ランタイムで提供されない動作を指定するためだけにコードを使用することが考えられるようになります。

ランタイムを駆動するデータは、メタデータとも呼ばれます。メタデータは、アプリケーションを記述するデータであると定義できます。たとえば、ホーム ページをどのように表示するか、購入ワークフローがどのように動作するかなどを記述します。アプリケーション データは、アプリケーションの実行状態を表します。つまり、Bob が Web サイトでショッピング カートに入れた商品や、チェックアウト ワークフロー内の送り先住所を表します。

Microsoft では、メタデータ定義用の共通ツール セットを提供し、通常のアプリケーション データと同じ機能セットが使用できる場所にメタデータを格納できるように、データ駆動型アプリケーションの構築プラットフォームである "Oslo" を開発中です。"Oslo" は 3 つの要素で構成されます。まとめて "M" と呼ばれる言語ファミリ、"Quadrant" と呼ばれるビジュアル データ操作ツール、リポジトリと呼ばれるデータ ストアの 3 つです。

この記事では、"Oslo" プラットフォームの概念の一部を紹介します。また、それらの概念を実現するために展開する必要があるツールとテクノロジも紹介します。特に、MSchema および MGraph というツールを使用して "Oslo" でメタデータ駆動型アプリケーションを構築する方法と、"M" での型と値の定義およびリポジトリへの展開の基礎について説明します。

"Oslo": 従来からあるアイデアの新たな実装方法

データ駆動型フレームワークの考え方は何も新しいものではありません。別の例として、Windows Presentation Foundation (WPF) について考えてみましょう。ほとんどの WPF アプリケーションが記述されている方法では、アプリケーションの大部分が XAML で構成されています。XAML は実行時に読み込まれ、XAML パーサーによって解釈されます。これが WPF アプリケーションの定義を駆動するメタデータです。開発者が知っているコンパイル時点の XAML とは別の XAML を実行時に読み込むか生成することによって、WPF アプリケーションの定義を変更することもできます。

しかし、XAML (BAML と呼ばれる形式にコンパイルされる) をアプリケーション コードのリソース中にバンドルすることに関して、魔法のようなしかけは何もありません。それと同じぐらい簡単に、XAML を別のファイルに読み込むことや、リレーショナル データベースに読み込むこともできるはずです。データベースに読み込んだ場合、WPF ローダーはリソースを読み込む代わりに SQL クエリを実行してアプリケーション定義にアクセスしますが、ユーザーはその違いに気付きません。アプリケーション定義をデータベースから読み込むと、そのデータはすべて中央で一括更新できるので、アプリケーション開発者はアプリケーションを実行するすべてのクライアントに、アプリケーションを再展開することなく変更を伝達できます。実際、これは Web の優れた機能であり、ブラウザで実行するアプリケーションかどうかにかかわらず、すべての種類のアプリケーションにとって採用を進める価値がある機能です。

アプリケーションを中央の場所に読み込むとすぐに、レプリケーション、セキュリティ、バージョン管理などのマルチユーザー機能を使ってみたくなります。また、切断されたユーザーや世界中のさまざまなロケールのユーザーに対するサポートも必要になります。

最終的には、アプリケーション定義を使用して、単にアプリケーションのインスタンスを制御する以上のことができます。開発中は、アプリケーション定義を使用して、アプリケーションの特定の部分が仕様どおりに動作しているかどうかをテストできます。実行時には、リソースが割り当てられている部分やトラブルが発生している部分を明らかにするためにアプリケーション定義を使用できます。問題にタグを付け、トラブルの具体的な原因を開発者が把握できるようにすることができます。アプリケーションのメタデータを利用することで、インデックス付け、チューニング、レプリケーション、セキュリティ保護、バージョン管理が可能な、ローカライズされた最新のオフライン対応データベースのすべてのクエリ機能を使用できるようになります。

それが "Oslo" の本質的な特徴である、SQL Server の機能を使用してアプリケーションのメタデータを保存するということです。メタデータを使用してアプリケーションの動作を駆動するという方法は、少なくとも、コードを書かずにコンソール ウィンドウを取得する目的でバイナリにコンソール ビットのタグを付け始めたときから、Windows で使用されてきました。"Oslo" は、簡単に言えば、アプリケーションのメタデータを形式化して生成し、アプリケーションのデータに使用するのと同じ機能とツールを提供する技術です。

メタデータ

メタデータとアプリケーション データの明らかな違いは、アプリケーションのメタデータはめったに変更されないことです。最も活動的な新しい企業でさえ、アプリケーションのメタデータは、通常は 1 日に 1 回、Web サイトの新しいバージョンが公開されたときに変更されるだけです。一方、アプリケーションのデータは非常に頻繁に変更されることが多く、人気のある Web サイトの場合、ユーザーは 1 日がかりで毎日更新に追われています。

もう少し詳しく言うと、この記事を書くために私が使っているワード プロセッサを定義しているメタデータは、2007 年に製品がリリースされたときから変更されていませんが、データは、私が文字を入力したりマウスを動かしたりして作業中の文書を編集するたびに、今日だけでも何千回も変更されています。実際、メタデータが書き換えられることは非常にまれなので、メタデータは多くの場合に読み取り専用パッケージとして配布されます。例として、.NET アセンブリのクラス定義や、アセンブリにリソースとして埋め込まれたコンパイル済みの XAML ファイルがあります。開発者は、多くの場合、メタデータの静的な性質を利用してアプリケーションを作成します。ASP.NET ページ内に定義されたボタンのイベント ハンドラがその例です。そのため、アプリケーションのメタデータがめったに変更されないのは悪いことではありません。

しかし、現在のメタデータ インフラストラクチャに関して少々都合が悪いのは、メタデータの定義および読み取り用のツールが、アプリケーション データに使用するツールとかけ離れていることです。

リレーショナル データベースを介してデータを読み書きするアプリケーションを開発するときは、クエリ、セキュリティ保護、レプリケーション、バージョン管理などを行うための優れたツールがいくつもあります。しかし、.NET Framework のメタデータにクエリを実行するときは、ごく限られたツールしかなく、基本的には Reflection API になります。

さらに、メタデータを操作するための .NET Framework ツールは、SQL メタデータの操作に使用するツールとは大きく異なります。アプリケーションのデータとメタデータの間のクエリ、つまり、ワークフローの定義を使用して現在実行されているワークフロー全体に対してクエリを実行しようと思えば、データ ソースが異なるため、独自のツールを作成する必要があります。ソースは SQL ファイルと XOML (Extensible Object Markup Language) ファイルです。

"Oslo" での構築

"Oslo" を使用して作成できるものはたくさんあります。社内でそのシナリオについて話し合うと、多くの場合、データを使用するアプリケーションを作成するということに話は落ち着きます。一方、ここまで "Oslo" の特徴をいくつか挙げましたが、ランタイムについては言及しませんでした。実際には、.aspx ファイルは ASP.NET がなければそれほど便利なものではなく、XOML ファイルは WF がないと機能性が大きく下がります。

"Oslo" を利用する多数のランタイムとサービスの開発が進められています。たとえば、ドメイン固有言語 (DSL) として MWeb を、Web エディタとして "Quadrant" をサポートする新しいバージョンの ASP.NET、"MService" という DSL と "Quadrant" サービス エディタをサポートする Windows Server 拡張機能である "Dublin"、MEntity DSL と "Quadrant" エンティティ エディタをサポートする Entity Framework、MSchema 言語 (この記事で後述) と "Quadrant" スキーマ エディタを備えた SQL Server 2008 があります。リポジトリにデータを読み込み、これらのランタイムを駆動できるようになります。

"Oslo" リポジトリは、企業の開発者がリポジトリに似た独自のメタデータ保存方法を構築しているアプリケーション向けです。たとえば、"Oslo" は、スタッフがコンピュータの構成を簡単に照会して理解できるコンピュータ構成情報リポジトリのプラットフォームとして、または同様に運用手順やスクリプトのリポジトリのプラットフォームとして適しています。開発者にとって、リポジトリはクエリ可能な統合されたメタデータ保存場所となり、そのため、"特定のメソッドを実装するアプリケーションとクラスはどれか?"、"このメソッドを変更するとパフォーマンスに影響するか?" などの質問に答えることが可能になります。

"Oslo" を採用する別の領域としては、分散アプリケーションを把握する場合が考えられます。リポジトリ内のプロファイリング、コード カバレッジ、負荷テスト、およびアーキテクチャのデータを組み合わせることで、開発者は、たとえば、関連した分散コンポーネントを参照し、さまざまな構成パラメータの設定を調べ、関連性のある実装の詳細を確認することによって、パフォーマンスの問題を簡単に解決できます。さらに、設計の不具合を自動的に報告する静的な分析ツールを開発できます。

特に私の気持ちがくすぐられる領域は、展開とインストールです。セットアップ パッケージをドメイン固有言語で定義し、リポジトリに読み込むことにより、開発者は現代のあらゆる言語の言語サービスと、ポリシーに準拠するためのクエリ ツールを手に入れることができます。たとえば、セットアップが適切なライセンス契約で保護されているかどうかについてクエリを実行できます。

その例を次に示します。Microsoft ダウンロード センターの Web サイトは、顧客のためにさまざまな規模のダウンロードに対応しています。そのサイトでファイルをホストするには、Microsoft インストーラ (MSI) ファイルとしてパッケージ化し、使用許諾契約書 (EULA) のタグを付ける必要があります。Visual Studio の GUI を使用してこれを 6 回ほど行った後、私は自動化されたソリューションを探し、WiX (Windows インストーラ XML) でそれを見つけました。これで、XML を使用してセットアップを定義し、コンパイルして MSI を作成できます。

WiX XML の例を図 1 に示します。専門家以外の目には、グラフィカル UI での操作に比べて進歩しているようには見えないかもしれませんが、これと同じ 27 回のマウス操作の手順を数回実行すれば、あなたの中のプログラマが目覚めるでしょう。

図 1 MSI ファイルを作成するための Wix XML

<?xml version='1.0' encoding='Windows-1252'?>
<Wix xmlns="https://schemas.microsoft.com/wix/2006/wi">
  <Product Name="SuperNotepad" Id="d65d2125-560b-4ba9-bdca-3b3bcd3f7b70"
           UpgradeCode="ac5c9dac-b3f0-469a-8f1a-02c2566eee33" 
           Language="1033" Codepage="1252"
           Version="1.0.0.0" Manufacturer="SuperNotepad Corp.">
    <Package Description="A really super pad for notes" 
             Manufacturer="SuperNotepad Corp."
             InstallerVersion="200" Languages="1033" Compressed="yes" />
    <Media Id="1" Cabinet="setup.cab" EmbedCab="yes" />
    <Directory Id="TARGETDIR" Name="SourceDir">
      <Directory Id="ProgramFilesFolder" Name="ProgramFilesFolder">
        <Directory Id="Directory3" Name="SuperNotepad Corp.">
          <Directory Id="INSTALLDIR" Name="Better NotePad">
            <Component Id="Component1" 
                       Guid="f7554ac8-32cd-44fb-bf85-0559c7d2e15c">
              <File Id="File1" Name="Manual.txt"
                    Source="C:\temp\SuperNotepad\Files\manual.txt" />
              <File Id="File2" Name="dependency.dll"
                    Source="C:\temp\SuperNotepad\Files\dependency.dll" />
              <File Id="File3" Name="BetterNotePad.exe"
                    Source="C:\temp\SuperNotepad\Files\notepad.exe" />
            </Component>
          </Directory>
        </Directory>
      </Directory>
    </Directory>
    <Feature Id="Feature1" Level="1">
      <ComponentRef Id="Component1" />
    </Feature>
  </Product>
</Wix>

MGraph と MSchema

WiX XML の作成をある程度自動化できても、山のようなタグ記号を使ってセットアップを作成するのは気が進みません。"Oslo" を使用すると、MSchema、MGrammar、および MGraph を含む言語ファミリである "M" でセットアップを作成できます (MSchema と MGrammar の仕様は、それぞれ「"Oslo" モデリング言語仕様」と「MGrammar 言語仕様」で参照できます)。

MGraph と MSchema について簡単に紹介します (MGrammar については今後の記事で詳しく取り上げます)。試してみるなら、"Oslo" の SDK に付属している Mr. Epl ツールを強くお勧めします。"Mr. Epl" (ミスター イーピーエル) は、MREPL.EXE の読み方を表します (MREPL は M Read-Evaluate-Print-Loop の略)。Mr. Epl は、"Oslo" SDK に付属している Intellipad というテキスト エディタ内から使用できます。そのためには、[スタート] メニューから [Intellipad (Samples Enabled)] (Intellipad (サンプル有効)) を選択し、Ctrl + / キーを押してミニバッファを起動します。次に、「SetMode('MScriptMode')」と入力して (大文字小文字の区別あり)、[Enter] (入力) をクリックします。

MGraph は、構造化されたデータのインスタンス (通常、値と呼ばれる) を定義するための言語です。この例では、3 つのデータを定義しています。匿名型整数、3 つの整数の匿名型コレクション、いずれも整数である X および Y という 2 つのフィールドを持つ "pt1" というレコードです。

42
{ 1, 2, 3 }
pt1 { X = 10, Y = 20 }

MSchema は、データに対する制約を定義するための言語です。MSchema では、一連の制約を結び付けて型にします。ここでは、2 つの名前付きの型を定義しています。組み込みの Integer32 型を 256 より小さい値のみに制約する SmallInteger 型と、X および Y という 2 つのフィールドを持つ Point エンティティ型 (M でレコード型を表す名前) です。

type SmallInteger : Integer32 where value < 256;
type Point { X : Integer32; Y : Integer32; };

X と Y は、両方とも -2,147,483,648 から 2,147,483,647 までの整数値を持つように制約されます。

この記事を読んでいる読者なら、CLR については既にご存じでしょう。CLR では、"名目的な型指定" と呼ばれる型指定を使用します。つまり、値が属する単一の型を指定します。一方、"M" は、XML と同じように構造化された型指定に基づいています。構造化された型指定の世界では、データがその型によって設定された制約を満たしている限り、1 つの値がいくつの型に属していてもかまいません。値が型によって定義される集合に属するかどうかを確認するには、"in" 演算子を使用します (ここに示す例は Mr. Epl ツールに含まれています)。

M>>> type SmallInteger : Integer32 where value < 256;
M>>> 4 in SmallInteger
true
M>>> 4 in Integer32
true
M>>> 256 in SmallInteger
false
M>>> 256 in Integer
true

この例で、4 は SmallInteger であり Integer32 でもありますが、256 は Integer32 に限定されることがわかります。エンティティの値も同様にどの型に属すかを確認できます。

M>>> type Point { X : Integer32; Y : Integer32; };
M>>> { X = 100, Y = 200 } in Point
true
M>>> pt1 { X = 10, Y = 20 }
M>>> pt1 in Point
true
M>>> pt1 in { X : SmallInteger; Y : SmallInteger; }
true
M>>> pt1 in { X; Y; }
true

名前付きおよび名前なしの X と Y のペアは両方とも Point であり、名前付きの X と Y のペアはどちらのフィールドも SmallInteger 値であることがわかります。この確認のために匿名型を定義しています。最後の行は、名前付き X と Y のペアが、X および Y というフィールドを持つ値の集合に属し、値に対する制約はないことを示しています。値は文字列、日付、コレクション、またはその他の何であってもかまいません。

もう 1 つ例を示します。この場合、名前付き X と Y のペアには Z フィールドがないので、X、Y、および Z フィールドが必要な匿名型の条件には適合しません。

M>>> pt1 in { X; Y; Z; }
false
M>>> pt1 in { x; y; }
false

さらに、最後の行は、M が大文字と小文字を区別することを示しています。

最後に、既定値とコレクション表記について確認します。

type Product {
    Name : Text;
    ...
};

type Package {
    Product : Product; // required value
    Keywords : Text*; // collection of 0 or more values
    Description : Text?; // optional value
    Comments : Text?; // optional value
    Manufacturer : Text; // required value
    InstallerVersion : Integer32 = 200; // default value
    Language : Integer32 = 1033; // default value
    Compressed : Logical = true; // defaults value
};

ここでは、WiX モジュールの Package 型が示されています。すべての製品 (製品は MSI セットアップの最上位の概念) にはパッケージがあります。パッケージには、オプションである一連のテキスト キーワード、オプションの説明とコメント、必須の製造元、インストーラのバージョン (既定では Windows インストーラ 2.0)、英語 (米国) が既定の言語、および圧縮フラグ (既定では true) が設定されます。

Product や Manufacturer のように、必須の値にはサフィックスを付けません。InstallerVersion や Language のように既定値は等符号と値で示します。Description や Comments のようにオプションの値にはサフィックスとして疑問符を付けます。コレクション値 (ゼロ個以上の値を持つ) は末尾にアスタリスクを付けます。疑問符とアスタリスクの表記 (? と *) は、お気に入りの正規表現言語の Kleene 演算子を思い起こしてもらうために使用しています。

Package 値の例を次に示します。

Products { 
    SuperNotepad : Product* {
        Name = "SuperNotepad",
        ...
    }
}

Packages : Package* where item.Product in Products {
    Package1 {
        Product = Products.SuperNotepad,
        Description = "A really super pad for notes",
        Manufacturer = Products.SuperNotepad.Manufacturer,
        Keywords = { "Installer", "Super", "Notepad", "Sample" }
    }
}

この例は、Product の一部と Package の全体を示しています。2 種類の制約が設定されています。最初の制約は、次のように ": xxxxx" の形式です。

SuperNotepad : Product* {

これはタイプ アスクリプションと呼ばれ、中かっこの内側のすべての値を型の制限に従うように制約します。

2 つ目の制約は、Product エンティティ値の格納場所を "M" コンパイラに指示する where 句です。

Packages : Package* where item.Product in Products {

また、値のうち 2 つには名前 (SuperNotepad と Package1) があります。MGraph の構文では、値グラフの初期化の途中で名前を使用できるので、データを複製することなくグラフの一部分から他の部分を参照することができます。サンプルでは、パッケージの定義内で製品の名前を参照するために、これを利用しています。

Mr. Epl は役に立ちますが、大きいものにはテキスト エディタを使用すると便利です。Visual Studio 2008 と Intellipad のどちらにも、"M" の編集を快適にする言語サービスが用意されています。たとえば、構文的に有効なコードであることを確認するには、エラー (右二重引用符の抜けなど) を表す赤い波線の上にカーソルを合わせると、データに関するヒントを表示できます。

すべての .NET ベース コードが名前空間の中になければならないのと同じように、すべての "M" コードはモジュール内に存在する必要があります。これが型と値の範囲を決定します。

波線がないだけでは安心できない場合は、さらにコマンド ラインから "M" コンパイラを実行することができます。

C:\>set path=%path%;"c:\Program Files\Microsoft Oslo SDK 1.0\Bin"
...
C:\>m.exe /t:none wixbits.m
Microsoft (R) "Codename M" Compiler version 1.0.0925.0
Copyright (C) Microsoft Corporation. All rights reserved.
C:\>

"M" コンパイラのエラーが報告されなければ、コードに不具合はありません。この例では、.m という拡張子を持つファイルを m.exe に渡すことに加えて、none というターゲットを (/t スイッチを使用して) 指定しています。これは構文チェックだけを行うことを意味します。ターゲット スイッチがない場合、"M" コンパイラは、テーブルを作成して値を挿入する SQL スクリプトをプログラムから生成しようとします。

SQL の生成

この WiX モデル データ サンプルから SQL の生成を試みると (閉じ二重引用符を追加した後)、次のようなエラー レポートが返されます。

C:\>m wixbits.m
Microsoft (R) "Codename M" Compiler version 1.0.0925.0
Copyright (C) Microsoft Corporation. All rights reserved.

C:\wixbits.m(16,9): error M2010: The referenced type 'Product' does not define an identity constraint.
...

このエラー メッセージは、Intellipad から [M Mode] (M モード) の [Reach SQL Preview] (SQL プレビューの表示) を選択した場合にも表示されます。このとき "M" コンパイラは、プログラム内に定義された型と値に一致した SQL スクリプトを生成するために、最上位レベルの名前付きの値 (エクステントと呼ばれる) をテーブル作成ステートメントにマッピングし、値自体を挿入ステートメントにマッピングしようとします。しかし、"M" ではあらゆる種類の汎用データ記述が使用されるので、対応する SQL 構造がない場合もあります (たとえば、ID フィールドのないエンティティの型)。"M" としてはその場合でも有効ですが、SQL を生成する目的では有効ではありません。

SQL の生成は "M" コンパイラの既定の動作になっています。なぜなら、最新のリレーショナル データベースはデータの格納および操作のための優れた環境だからです。その環境は、セキュリティ、レプリケーション、バージョン管理、監査など、あらゆる種類の優れた機能を備えています。データのホスティングに SQL Server を使用すれば、それらの機能のすべてと、堅牢性およびパフォーマンスに関するさまざまな最適化の効果を手に入れることができます。

"M" の型に SQL Server との互換性を持たせるには、マッピングに ID 列が必要です。次のコードを使用して ID 列を追加できます。

type Package {
    Id : Integer32 = AutoNumber();
    ...
} where identity Id;

ご覧のように、Id という名前のフィールドを作成し、AutoNumber 関数を使用して既定値を設定します。次に、Id フィールドを ID 列としてマークする where キーワードを使用して、型に制約を追加します。この制約を追加したことで、この型に対応した有効な SQL を生成できるデータができました。図 2 を参照してください。

図 2 "M" で生成した SQL

...
create table [WixBits].[Packages](
  [Id] int not null identity,
  [Comments] nvarchar(max) null,
  [Compressed] bit not null default 1,
  [Description] nvarchar(max) null,
  [InstallerVersion] int not null default 200,
  [Language] int not null default 1033,
  [Manufacturer] nvarchar(max) not null,
  [Product] int not null,
  constraint [PK_Packages] primary key clustered ([Id]),
  constraint [FK_Packages_Product_WixBits_Products] foreign key ([Product]) references [WixBits].[Products] ([Id])
);
...

create table [WixBits].[Packages_Keywords](
  [_Id] bigint not null identity,
  [Packages_Id] int not null,
  [Item] nvarchar(max) not null,
  constraint [PK_Packages_Keywords] primary key clustered ([_Id]),
  constraint [FK_Packages_Keywords_Packages_Id_WixBits_Packages] foreign key 
  ([Packages_Id]) references [WixBits].[Packages] ([Id]) on delete cascade
);
...

うまくいけば、"M" コンパイラによって生成された SQL は、自分で書いた場合の SQL と一致します。モジュール名は SQL スキーマ名にマッピングされます。型名はテーブル名にマッピングされます。AutoNumber はフィールドの "ID" にマッピングされ、ID 制約はフィールドの主キー制約にマッピングされます。Integer32 や Text などの "M" の組み込み型は、int や nvarchar などの対応する SQL 型にマッピングされます。コレクション フィールド (Text* 型のキーワード フィールド) は、別のテーブルにマッピングされ、対応する外部キー制約と共にそのコレクション内のゼロ個以上のアイテムを保持します。図 3 に、値がどのようにマッピングされるかを示します。

図 3 "M" の型と SQL のマッピング

insert into [WixBits].[Packages] ([Product], [Description], [Manufacturer])
 values (@WixBits_Products_Id0, N'A really super pad for notes', @WixBits_Products_Manufacturer0);
declare @WixBits_Packages_Id0 bigint = @@identity;

insert into [WixBits].[Packages_Keywords] ([Item], [Packages_Id])
 values (N'Installer', @WixBits_Packages_Id0);

insert into [WixBits].[Packages_Keywords] ([Item], [Packages_Id])
 values (N'Super', @WixBits_Packages_Id0);

insert into [WixBits].[Packages_Keywords] ([Item], [Packages_Id])
 values (N'Notepad', @WixBits_Packages_Id0);

insert into [WixBits].[Packages_Keywords] ([Item], [Packages_Id])
 values (N'Sample', @WixBits_Packages_Id0);

パッケージ化と展開

"M" コンパイラの既定の出力である SQL は、SQL Server 2008 インスタンスにプッシュすることができます。既定では、その SQL は単一の SQL スクリプト ファイルとしてパッケージ化されます。

C:\>m.exe wixbits.m
Microsoft (R) "Codename M" Compiler version 1.0.0925.0
Copyright (C) Microsoft Corporation. All rights reserved.

C:\>dir wixbits.sql
...
11/22/2008  04:43 PM             2,545 wixbits.sql
...

このスクリプト ファイルを使用して、sqlcmd.exe や SQL Server Management Studio などの好きな方法で SQL データベース インスタンスにデータを格納できます。ただし、SQL スクリプトには元の "M" ソース コードよりもはるかに少ないメタデータしかありません。そのメタデータを残しておきたい場合、また残しておくことに価値がある場合は、この後に説明するように、m.exe に対して /p:image コマンド ライン スイッチを指定し、"M" イメージ パッケージ化の形式を選択できます。

C:\>m.exe /p:image wixbits.m
Microsoft (R) "Codename M" Compiler version 1.0.0925.0
Copyright (C) Microsoft Corporation. All rights reserved.
C:\>dir wixbits.mx
...
11/22/2008  04:44 PM             9,073 wixbits.mx
...

wixbits.mx ファイルは "M" イメージ ファイルです。生成された SQL と共に、マニフェストおよび他のメタデータが格納されています。このファイルは OPC (Open Packaging Conventions) 形式なので、.zip というファイル拡張子を付けて、他の .zip ファイルと同じように開くことができます。それよりも重要なのは、.mx ファイルを参照の目的および展開の目的に使用できることです。

.NET アセンブリをビルドするとき、ビルド プロセスの過程でそのアセンブリを参照し、パブリック型と静的インスタンスを .NET ベースのプログラムに挿入できます。コマンド ラインから C# または Visual Basic コンパイラを使用している場合は、/r スイッチ (r は reference の r) を使用して既存の .NET アセンブリを参照できます。同様に、"M" プログラムをコンパイルしている場合は、"M" コンパイラに対して /r スイッチを使用して、"M" イメージから他の型を挿入できます。そのためには、最初に参照先モジュールから型またはエクステントをエクスポートする必要があります。

// WixbitsTypes.m 
module WixBits {
    export Product, Package; // export types
    export Products, Packages; // export extents

    type Product {...};
    type Package {...};
    Products : Product*;
    Packages : Package* where item.Product in Products;
}

それから、エクスポートした型とエクステントをインポートします。

// WixbitsValues.m
module WixBits {
    import WixBits; // bring in exported types and extents
    ...
}

エクスポートを使用して "M" のイメージ ファイルをビルドした後、インポートを使用して "M" プログラムをコンパイルするときに、ビルドしたファイルを参照できます (詳細については図 4 を参照)。

図 4 インポートした型とエクステントを使用したコンパイル

C:\>m.exe /p:image /t:Repository WixbitsTypes.m
Microsoft (R) "Codename M" Compiler version 1.0.0925.0
Copyright (C) Microsoft Corporation. All rights reserved.

C:\>dir WixbitsTypes.mx
...
11/22/2008  05:48 PM            28,332 WixbitsTypes.mx
...

C:\>m.exe /p:image /t:Repository /r:WixbitsTypes.mx WixbitsValues.m
Microsoft (R) "Codename M" Compiler version 1.0.0925.0
Copyright (C) Microsoft Corporation. All rights reserved.

C:\>dir WixbitsValues.mx
...
11/22/2008  05:50 PM             5,606 WixbitsValues.mx

他の "M" プログラムで参照できるように型とエクステントをパッケージ化する以外にも、"M" のローダー ユーティリティである mx.exe を使用して、SQL Server データベース インスタンスに "M" のイメージ ファイルを展開できます。次のようにします。

C:\>mx.exe /db:Repository /i:WixbitsTypes.mx /i:WixbitsValues.mx
Microsoft (R) "Codename M" Command-line Utility version 1.0.0925.0
Copyright (C) Microsoft Corporation. All rights reserved.

ここでは、/db スイッチを使用して接続先データベースを指定し、ローカル ホストをデータベース サーバーと想定して 2 つのイメージ ファイルをインストールしています。テーブルを SQL で読み込み、テーブルに値を挿入します。これは、任意のデータ アクセス技術を使用して値を再び取り出すことができる (または、値を更新するか、削除するか、新しい値を挿入できる) ことを意味します。この例では、sqlcmd コマンド ライン ツールを使用しています。

C:\>sqlcmd -d Repository
1> select Name from WixBitx.Products
2> go
Name
----------------
SuperNotepad
(1 rows affected)

Intellipad は、特別な構成なしに SQL の生成を含む "M" 言語サービスをサポートしている一方、イメージのビルドまたは他のイメージの参照用にサポートされているメカニズムはありません。ただし、Visual Studio はこれらの機能をサポートしています。

Visual Studio の統合

"Oslo" SDK には、出荷の時点で Visual Studio 2008 SP1 のサポートが組み込まれています。図 5 に示すように、SDK をインストールすると、"M" プロジェクト テンプレートがインストールされます。

既定では、Model1.m ファイルがプロジェクトに追加され、Intellipad と同じ IntelliSense 機能が提供されます。その他のモデル ファイルが必要な場合は、ソリューション エクスプローラでプロジェクトを右クリックし、新しいアイテムを追加して、"M" モデル テンプレートを選択します。

fig05.gif

図 5 Visual Studio 2008 の "M" プロジェクト テンプレート

さらに、既存の "M" イメージを参照する場合は、プロジェクトを右クリックして [参照の追加] を選択し、.mx ファイルを選択します。たとえば、例の中で設定した値をビルドし、例に示した型を参照するプロジェクトを作成する場合は、図 6 のようになります。

fig06.gif

図 6 ソース コードと .mx ファイル参照を表示した "M" プロジェクト

ビルドの際、予想どおりエラー リストにエラーが表示され、出力は \bin\Debug または \bin\Release (ビルド対象の構成によって異なる) に送られます。出力には .sql ファイルと .mx ファイルの両方が含まれています。これらのファイルに対して、適切と思われる処理を実行できます (出力 .mx ファイルに mx.exe を実行するなど)。これらと同じ機能が Visual Studio 2010 の最新 CTP でも使用できます。

Visual Studio 内でのビルド プロセスは、.mproj という拡張子を持つ MSBuild ファイルによって駆動されます (図 7 を参照)。このファイルは、独自のプロジェクト ファイル内で "Oslo" の msbuild ターゲット ファイルを使用する場合に便利です。

図 7 "M" プロジェクトの MSBuild ファイル

<?xml version="1.0" encoding="utf-8"?>
<Project ...>
  <PropertyGroup>
    <MTarget>Repository</MTarget>
    <MPackageScript>true</MPackageScript>
    <MPackageImage>true</MPackageImage>
    <MTargetsPath Condition="$(MTargetsPath) == ''">
      $(MSBuildExtensionsPath)\Microsoft\M\v1.0</MTargetsPath>
    ...
  </PropertyGroup>
  <ItemGroup>
    <Compile Include="MixbitsValues.m" />
  </ItemGroup>
  <ItemGroup>
    <MReference Include="C:\WixbitsTypes.mx" />
  </ItemGroup>
  <Import Project="$(MTargetsPath)\MProject.targets" />
</Project>

サンプル コード

この記事に掲載したサンプル コードは、単純なリポジトリ インストーラ ユーティリティ (repinst.exe) であり、これまで説明してきた MSI ファイルを記述するための、もう少し充実した機能を持つモデルに対応しています。WiX モデルを記述する型と値をコンパイル (m.exe を使用) してリポジトリに読み込んだ後 (mx.exe を使用)、値データに対して repinst.exe ユーティリティを実行すると、リポジトリからデータが読み取られ、そのデータから MSI ファイルが作成されます。その後、セットアップが起動され、データベースにアクセスできるユーザーならだれでもアプリケーションを展開できるようになり、図 8 に示すようにアプリケーションが読み込まれます。

実行中の repinst のサンプル

図 8 実行中の repinst のサンプル

このサンプルには少し手を加えました。主に公開のための並べ替えですが、大部分の作業の担当者 (功労者) は、Thomas Delrue、Alejandro Trigo、そして Giovanni Della-Libera です。彼らは、WiX のモデリング、"M" から WiX への変換、および SQL データからの MSI の生成を行いました。パイオニアとしての彼らの仕事がなければ、私がこの記事を書き上げるにはもっと時間がかかったでしょう。Josh Williams、Martin Gudgin、John Doty にも感謝します。リポジトリ機能を紹介するための完全に別のサンプル ("Oslo" デベロッパー センターで間もなく公開予定) を作成してくれました。

Chris Sells は、Microsoft の Connected Systems Division のプログラム マネージャとして、次世代アプリケーション開発手法に取り組んでいます。Chris と Chris の手掛けたプロジェクトについては、sellsbrothers.com を参照してください。