しばやん雑記

Azure Serverless とメイドさんが大好きなフリーランスのプログラマーのブログ

WebMatrix 2 の拡張機能を作成する (1) - はじめに

こんばんわ、そろそろウェブマトリクスマンは公式化してもいいんじゃないかと思います。しばやんです。

最近は特にやることもないので WebMatrix 2 ベータの拡張機能について、いろいろ調べていました。そうすると割と拡張ポイントが多く、いろいろな機能があったので久しぶりに連載形式で書いていきます。

さて、今回は「はじめに」ということなのでまずは環境から作っていきましょうか。とりあえず Visual Studio 2010 と WebMatrix 2 ベータは用意しておかないと始まらないので、インストールしていない方は Web PI を使ってサクッとインストールしちゃってください。

WebMatrix 2 ベータをインストールするためには Web PI 4.0 ベータが必要なので注意してください。

WebMatrix 3

Visual Studio 2010 と WebMatrix 2 ベータの準備が出来たら、次は WebMatrix 2 の拡張機能用のテンプレートをダウンロードして追加します。公式な配布ではありませんが、以下のページからダウンロードが可能です。

Make it your own [WebMatrix's new extensibility support enables developers and applications to customize the web development experience to their liking!] - Delay's Blog - Site Home - MSDN Blogs

「Microsoft.WebMatrix.Extensibility CHM file」は拡張機能 API のリファレンスで「"WebMatrix Extension" Visual Studio project template」がテンプレートになります。ダウンロードしたテンプレートは展開せずに「ドキュメント」ディレクトリ内にある

Visual Studio 2010\Templates\ProjectTemplates\Visual C#

に置いておきましょう。

これで Visual Studio を起動した時にテンプレートが追加されていますが、そのままだと探しにくいので右上の検索窓に「WebMatrix」と入力すると楽に探すことが出来ます。

後は表示されたテンプレートを選択して「OK」ボタンをクリックすると、基本的なコード付きでプロジェクトが作成されます。

これで拡張機能を作成する準備が出来ましたので、次回は拡張機能の心臓部とも言える IExtention インターフェース周りについて説明したいと思います。お疲れ様でした。

WebMatrix 2 拡張機能についてメモ書き

ほぼ自分用メモです。箇条書きで。

  • IExtension を MEF 使ってエクスポート
    • 実際には ExtensionBase クラスを継承して作る
  • WebMatrix 本体は IWebMatrixHost
    • ExtensionBase のプロパティからインスタンスを簡単に取れる
  • 基本的には Ribbon UI とダッシュボードの拡張がメイン
    • エディタ部分もテーマなどを定義しての操作はできる
  • Ribbon UI 周りは割と何でも出来るし、作りやすい
    • ICommand が必要なので DelegateCommand が要る
  • EnvDTE とか使ってないので、ウェブサイト内のファイルはパス文字列で扱ってる
    • レシピよりわかりやすい

つまり

Make it your own [WebMatrix's new extensibility support enables developers and applications to customize the web development experience to their liking!] - Delay's Blog - Site Home - MSDN Blogs

からテンプレートを落とせってことですよ(何

WebPages 2 で使えるカスタムバリデータを実装する

WebMatrix 2 というか、WebPages 2 ではバリデーション周り非常に使いやすくなっていいですね。下手に Fluent Interface で定義させるよりわかりやすいと思いました。

しかし、標準で使えるバリデータは思ったより多くありません。正規表現使えるので大体のことはできますが、ちょっと複雑なことをしようとすると困りますね。なので、今回はカスタムバリデータを作ってみたいと思います。

とりあえず ASP.NET MVC 3 で独自の検証属性を作成して、クライアントサイド検証を行う - しばやん雑記 でクライアントサイド検証を実装した時のように CERO のバリデータを作ります。

バリデータは IValidator インターフェースを実装したクラスです。じゃあこれを実装すれば何でも作れる!と思ったら、ちょっとインターフェースが難しいです。

public interface IValidator
{
    ModelClientValidationRule ClientValidationRule { get; }
    ValidationResult Validate(ValidationContext validationContext);
}

今まで MVC で開発してきて ModelClientValidationRule と ValidationContext とか見ないですよね。このあたりは綺麗にフレームワークが隠してくれてるので、我々は適当に使っておけばよかったのですが、今回は逃げることが出来ません。

とは言っても、ClientValidationRule プロパティはその名の通りクライアントサイド検証用なので、クライアントサイド検証を使わないのなら null 返しとけば問題ありません。

とりあえず完成したコードをいつも通り載せておきます。

public class CeroValidator : IValidator
{
    public CeroValidator(char rating, string errorMessage = null)
    {
        _rating = rating;
        _errorMessage = errorMessage;
    }

    private char _rating;
    private string _errorMessage;

    public ModelClientValidationRule ClientValidationRule
    {
        get { return null; }
    }

    public ValidationResult Validate(ValidationContext validationContext)
    {
        // ObjectInstance には HttpContext が入ってるので、キャストして取得
        var context = (HttpContextBase)validationContext.ObjectInstance;

        // メンバー名はフォーム要素名なので、コレクションから値を直接取得
        var value = context.Request.Form[validationContext.MemberName];

        // バリデータでは null or 空文字列の場合は成功にしないとダメ(required があるので)
        if (string.IsNullOrEmpty(value))
        {
            return ValidationResult.Success;
        }

        var result = false;
        
        // 数字の時だけ検証する
        if (value.IsInt())
        {
            var age = value.AsInt();

            switch (_rating)
            {
                case 'Z':
                    result = age >= 18;
                    break;

                case 'D':
                    result = age >= 17;
                    break;

                case 'C':
                    result = age >= 15;
                    break;

                case 'B':
                    result = age >= 12;
                    break;

                case 'A':
                    result = true;
                    break;
            }
        }

        if (result)
        {
            // 成功したら ValidationResult.Success を返す
            return ValidationResult.Success;
        }

        // 検証に失敗したらエラーメッセージとメンバー名で初期化した ValidationResult を返す
        return new ValidationResult(_errorMessage, new[] { validationContext.MemberName });
    }
}

流れはコードとコメントでわかってください、お願いします。基本的には MVC 向けに検証属性作るのとあまり変わらないですが、入力値が文字列なので気を付けてください。

クライアントサイド検証を使いたい場合には MVC 3 と同じように ModelClientValidationRule を作って返せばいいです。JavaScript 側を忘れないように気を付けてくださいね。

そしてサンプルコードは以下のような感じ。Validator.ほげほげ と指定していた部分を new CeroValidator に変えただけです。

Validation.RequireFields("name");
Validation.Add("age", new CeroValidator('Z', "検証に失敗しました"));

if (IsPost)
{
    Validation.Validate();
}

実際に試してみたところ、Z 指定なので 18 才未満の場合にはちゃんとエラーが出ました。入力値が分からないので確認しようがないですね(汗

WebMatrix 2 ベータでクライアントサイド検証を試す

前回の続きとも言える内容です。

ASP.NET MVC では 2 でクライアントサイド検証が、3 で Unobtrusive (控えめな) JavaScript による検証に対応しましたが、WebMatrix 2 というか Web Pages 2 で控えめな JavaScript によるクライアントサイド検証に対応しました。

使い方はこれまた簡単。前回のコードで ValidationHelper を使って検証の設定を行いましたので、数行のコードを追加するだけで簡単に対応出来ます!

さっそくコードを見てもらいましょう。

@{
    Validation.RequireFields("name");
    Validation.Add("level", Validator.Integer(), Validator.Range(0, 100));

    if (IsPost)
    {
        Validation.Validate();
    }
}

<!DOCTYPE html>

<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title></title>
        <script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.6.3.min.js"></script> 
        <script src="http://ajax.aspnetcdn.com/ajax/jquery.validate/1.8.1/jquery.validate.min.js"></script> 
        <script src="@Href("~/Scripts/jquery.validate.unobtrusive.min.js")"></script>
    </head>
    <body>
        <form method="post">
            <label for="name">名前</label>
            <input type="text" name="name" @Validation.GetHtml("name") />
            @Html.ValidationMessage("name")
            <br />
            <label for="level">レベル</label>
            <input type="text" name="level" @Validation.GetHtml("level") />
            @Html.ValidationMessage("level")
            <br />
            <input type="submit" value="送信" />
        </form>
    </body>
</html>

変更点は head タグの中にある

<script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.6.3.min.js"></script> 
<script src="http://ajax.aspnetcdn.com/ajax/jquery.validate/1.8.1/jquery.validate.min.js"></script> 
<script src="@Href("~/Scripts/jquery.validate.unobtrusive.min.js")"></script>

この 3 行と、input タグの中にある

@Validation.GetHtml("name")

の呼び出しだけです。

head タグに書いているのは単なる jQuery やその他必要なファイルの読み込みです。そして、input タグに書いている GetHtml メソッドの呼び出しは、クライアントサイド検証に必要な情報を Custom Data Attributes として出力するためのものになっています。

実際に実行させてソースを確認すると、ちゃんと Custom Data Attributes で検証の設定が出力されていますね。

<input type="text" name="name" data-val-required="This field is required." data-val="true" />

<input type="text" name="level" data-val-range="Value must be an integer between 0 and 100." data-val-range-min="0" data-val-range-max="100" data-val="true" />

それでは実行して確認したいと思います。前回と同じく真実の値を入力して確認してみます。

今回もエラーが出ていることを確認してもらいたいのですが、それ以上に戻る・進むボタンの状態を確認してください。クライアントサイドでの処理なので、ページ遷移が発生していないので戻るボタンが有効になっていません。

そして当然ながら名前を空にしたり、レベルに数字以外を入力するとエラーになります。

ちなみに、今回はクライアントサイド検証の設定出力のために Validation.GetHtml を使いましたが、Html.TextBox などを使うと自動で出力されます。

<label for="name">名前</label>
@Html.TextBox("name")
@Html.ValidationMessage("name")
<br />
<label for="level">レベル</label>
@Html.TextBox("level")
@Html.ValidationMessage("level")

入力値の復元とかされないのであまりメリットはないですが、割と面倒なのでこっちを使ってもいいと思います。

WebMatrix 2 ベータで入力値の検証を試す

WebMatrix 2 というか Web Pages 2 で入力値の検証周りが大幅に使いやすくなりました。ASP.NET MVC ではアノテーションベースの検証を行うのが普通ですが、Classic ASP や PHP ではモデルクラス作ってとかせずに、基本的には POST 値を直接参照するのが普通ですよね。

まあ、ごちゃごちゃ言わずに実際にコード見ながら試していきましょう。Web Pages 2 では cshtml の基本クラスの WebPage クラスに Validation というプロパティが追加されました。中身は ValidationHelper というクラスで、こいつが面倒な検証周りを自動で行ってくれます。

とりあえずコードを見てください。

@{
    Validation.RequireFields("name");
    Validation.Add("level", Validator.Integer(), Validator.Range(0, 100));

    if (IsPost)
    {
        Validation.Validate();
    }
}

<!DOCTYPE html>

<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title></title>
    </head>
    <body>
        <form method="post">
            <label for="name">名前</label>
            <input type="text" name="name" />
            @Html.ValidationMessage("name")
            <br />
            <label for="level">レベル</label>
            <input type="text" name="level" />
            @Html.ValidationMessage("level")
            <br />
            <input type="submit" value="送信" />
        </form>
    </body>
</html>

とりあえず先頭のコードブロックに注目してください。Web Pages 2 では、ここで検証の設定を全て行ってしまいます。

流れとしては RequireField/RequireFields で必須かどうか、Add でフィールドごとにさらに細かい検証設定を、POST された時には Validate で検証を行っています。エラーメッセージはお馴染みの Html.ValidationMessage で出力です。

必須設定は単純なのでいいのですが、Add ではフィールド名と可変個の IValidator を実装したクラスのインスタンスを受け取るようになっています。IValidator はそれぞれが 1 つの検証機能を持っているので、複数個組み合わせることで目的の検証機能を組み上げることが出来るというわけです。使える部品は全て Validator クラスの静的プロパティとして実装されているので、これを使うようにしてください。

それでは実際に試してみます。WebMatrix の画面から [Run] をクリックすると起動するので、フォームに真実の値を入れました。それでは送信してみましょう。

ちゃんとエラーが表示されました。このサンプルではレベルに 0 から 100 までの値しか入力できない設定なので正しい動作ですね。

そして名前を空に、レベルに数値以外を入力してもちゃんとエラーが表示されます。ASP.NET MVC とは全然やり方が違いますが、あらかじめ一括で定義できるのは非常に使いやすく便利だと思います。

そろそろ PHP 以外の選択肢として考えてもいいんじゃないでしょうか?

WebMatrix 2 ベータも公開されてます

WebMatrix 2

何ですかこのテライケメンなキャッチは。

「再びウェブと恋に落ちる」*1とか実はイケメンなウェブマトリクスマンに言われたらどんな男もイチコロじゃないっすか。信頼できる筋によると、ダウンロードボタンはウェブマトリクスマンを意識して緑色になったということです。

そして新機能とかはこっちを。HTML5, CSS, jQuery, PHP, C# のインテリセンスが使えるようになったのはかなり大きいですね。

WebMatrix 2 Beta Features

そして MVC 4 とも関連しますが、Web Pages (Razor) がバージョン 2 になりました。新機能はこっち。

Top Features in ASP.NET Web Pages 2 : The Official Microsoft ASP.NET Site

見たところ、Validation で jquery.validate.unobtrusive が使えるようになったり、OAuthWebSecurity クラスで OAuth と OpenID を使ったログイン機能を簡単に組み込めるようになりました。OAuth や OpenID は MVC 4 ではレシピで提供されるはずですが、DP ではまだみたいですね。

WebMatrix 2 で個人的に注目したいところはスタートアップ画面に [Open Remote Site] という項目が追加されたところです。

選択すると Web Deploy の設定画面が出てくるので ExpressWeb とか使っている場合に限定されるのですが、既にデプロイされている Web サイトを簡単に編集できるようになりました。

設定を行うと以下のようにリモートにあるファイルが表示されます。今回は ExpressWeb のデフォルトページしか出ていないですが、ファイルを開こうとすると最新版を自動でダウンロードしてくれ、編集後に保存すると自動でサーバにデプロイされるようになっています。最高ですね!

今回インテリセンスが追加されましたが、Razor の @ の後だと出てこないとかまだ残念な点があります。しかし、着実に進化している WebMatrix は ASP.NET の開発環境だけではなく、PHP の開発環境としても今後が期待できますね。

*1:Google 翻訳によると

WebMatrix で Entity Framework 4.1 を使う

こんばんわ。目が痛いけど頑張ってブログを書きます、しばやんです。

今回は普通は使わないだろうけど、実際には普通に使えるんだよシリーズ第一弾(謎)として Entity Framework 4.1 を使ったコードファースト開発を行ってみます。

とりあえずは普通に WebMatrix で Web サイトを作って、パッケージマネージャから Entity Framework 4.1 をインストールするわけですが、最新版はパッケージのフォーマットが古いのでエラーになります。4.1.10331.0 を選んでインストールしてください。

Entity Framework の準備が出来たら、WebMatrix に戻って Web サイトに App_Code ディレクトリを追加します。

この名前のディレクトリは特殊で、実行時に中にあるファイルがコンパイルされるようになっているのですが、今までヘルパーを作った時には cshtml だけ入れてきました。しかし、拡張子 cs ファイルを入れておいてもコンパイルしてくれるので、今回はこれを利用してコードファースト開発を行います。

それでは App_Code ディレクトリに DbContext を継承したクラスとエンティティクラスを追加していきます。ディレクトリを右クリックして新規作成を選択すると、ポップアップが表示されて作成するファイルの種類が選べます。そのままでは cs ファイルは出てきませんが、サイドバーから推奨を選択すると Class (C#) という名前で cs ファイルが出てきます。

ファイル名を適当に付けて「OK」をクリックするとクラスが作られますが、Visual Studio のようにクラス名を自動で付けてくれないので手動で付けます。今回はとりあえず ProductContext と Product という名前にしました。

public class ProductContext : DbContext
{
    public DbSet<Product> Products { get; set; }
}
public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
}

まあ、非常に簡単なコンテキストとエンティティです。ついでなので _AppStart.cshtml に普段は Global.asax で書くコードを追加しておきましょう。

@using EF = System.Data.Entity;

@{
    EF.Database.SetInitializer(new EF.DropCreateDatabaseIfModelChanges<ProductContext>());
}

これでテーブルが自動的に生成されるようになりました。ちなみに using で別名を割り当ててる理由は Database というクラス名が WebMatrix 標準のクラスと衝突するからです。

これで DbContext API を使う上で必要なコードは準備できましたが、まだ接続文字列を定義していなかったので Web.config を追加します。これはルートディレクトリを右クリックで追加してください。

作成された Web.config はほぼ空っぽの状態なので、接続文字列を定義する上で必要な要素ごと追加します。名前はデフォルトで DbContext を継承したクラス名になります。

あと、このままだと Entity Framework が内部で使っているクラスを持っているアセンブリへの参照が足りないと怒られるので、一緒に定義してあげます。アセンブリ名は System.Data.Entity です。

<?xml version="1.0"?>

<configuration>

    <connectionStrings>
        <add name="ProductContext"
             connectionString="Data Source=|DataDirectory|Product.sdf"
             providerName="System.Data.SqlServerCe.4.0" />
    </connectionStrings>
    
    <system.web>
        <compilation debug="false" targetFramework="4.0">
            <assemblies>
                <add assembly="System.Data.Entity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
            </assemblies>
        </compilation>
    </system.web>

</configuration>

これで全ての準備は完了したので、実際に ProductContext クラスを使ってデータを格納してみたいと思います。Default.cshtml にデータを全件出力して、さらに格納するコードを追加しました。

@{
    var context = new ProductContext();
    
    // 全件出力する
    foreach (var product in context.Products)
    {
        <text>ID:@product.Id 商品名: @product.Name 値段:@product.Price</text><br />
    }

    // データを追加
    context.Products.Add(new Product
    {
        Name = "商品",
        Price = 1980
    });

    context.SaveChanges();
}

実行するとリロードの度に表示される件数が増えるので、データベースに格納されていることが分かります。

今回、わざわざ WebMatrix で Entity Framework 4.1 を使ってみましたが、普段は WebMatrix 内蔵の機能を使いながら、多対多など複雑なエンティティが必要な場合だけ Entity Framework を使えば楽できそうですね。

続・WebMatrix でインストールできないパッケージ

WebMatrix には NuGet 1.2 で作ったパッケージがインストールできない - しばやん雑記 の続き?です。

WebMatrix と cshtml だけを使って Entity Framework 4.1 のコードファースト出来ないかなぁと思って、Entity Framework 4.1.10715.0 をインストールしようとするとエラーになりました。

ええ、同じくスキーマのエラーです。普通は WebMatrix で Entity Framework 4.1 なんてインストールしないので実害はないんですが、古い NuGet.exe でパッケージングを行ってるものがありそうですね。

てか、最新版使ってください>Entity Framework の中の人

WebMatrix と Hatena.Helper ではてな認証を使う

ウェブマトリクスマンから WebMatrix のネタでブログ書けという無言の圧力を何となく感じていたので、今回は私が作成した Hatena.Helper の使い方説明をかねて書いてみます。

とりあえず WebMatrix って何?どうやって使う??基本的な操作法は???という場合は、超わかりやすい開発入門が公開されていますので、一通り読んでおくのを推奨します。

Web 開発入門 - Microsoft Web Platform

みんな大好きウェブマトリクスマンが優しく教えてくれる XPS/PDF も公開されています。冗談抜きでわかりやすいのでお勧めです。

とりあえず WebMatrix で新規サイトを空っぽで作成して、Index.cshtml を追加しました。サイドバーのファイルツリーに反映されないときは表示を更新してください。

そして Hatena.Helper のインストールを行うのですが、その為には「ASP.NET Web ページの管理」から行う必要があります。そして管理ページを表示するにはパスワードの設定などが必要ですが、そのへんは全て先程の開発入門に書いてあるので省略します。

「ASP.NET Web ページの管理」では最初にパッケージマネージャが表示されるので、検索窓に "Hatena" と入力して検索すると以下のように表示されます。

バージョン違いも表示されていますが、1.4 が最新版となるので「インストール」をクリックします。クリックするともう一度インストールボタンが表示されるので、クリックすると実際にインストールされて使えるようになります。

インストールが完了したら WebMatrix の画面に戻りましょう。ファイルツリー表示が反映されていない場合は最新へ更新してください。
以下のように App_Code\Hatena.cshtml が追加されます。

それでは Index.cshtml に必要なコードを書いていきたいところなんですが、その前にはてな認証の設定を行いましょう。

はてな認証API から新規 API キーを取得します。「新規 API キーを取得」をクリックすると説明を入力するボックスがありますが、あとからも変更可能なので適当に入力して登録ください。

登録が完了してもまだ使える状態ではないので、編集を選択して以下のように設定を行います。必要なのはタイトル、コールバック URL、ステータスの 3 つで、コールバック URL には IIS Express で使われているアドレスを入力します。ステータスを有効にするのを忘れずに設定を完了してください。

これで作成した API キーと秘密鍵が使えるようになりましたので、Index.cshtml に以下のようなコードを書きます。先程取得した API キーと秘密鍵は ApiKey と SecretKey プロパティに設定します。

API キーと秘密鍵の設定が終われば、あとは Auth メソッドを呼び出すだけではてな認証の処理が行われます。認証に成功した場合は Auth メソッドの戻り値にユーザー情報が格納されたクラスのインスタンスが返ってきますので、あとはその情報を利用してユーザー ID とアイコン画像の表示を行います。

@{
    Hatena.ApiKey = "(API キー)";
    Hatena.SecretKey = "(秘密鍵)";
    
    var user = Hatena.Auth();
}
<!DOCTYPE html>

<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title></title>
    </head>
    <body>
        ユーザー名: @user.Name<br />
        アイコン: <img src="@user.ImageUrl" />
    </body>
</html>

そして WebMatrix のメニューから「実行」を選択すると実際に認証処理が行われ、はてな側の確認ページへリダイレクトされます。

このページで「アカウント情報の読み取りを許可」を選択すると、設定したコールバック URL へリダイレクトが行われて、ユーザー情報が利用できるようになります。

実際に読み取りを許可した場合には以下のようにユーザー名とアイコン画像が表示されます。

非常に簡単なサンプルでしたが、WebMatrix と Hatena.Helper の組み合わせで簡単にはてな認証を使うことが出来ました。

ここで取得したユーザー名を使って認証クッキーを発行すると、実際にログイン機能として利用出来ますね。

WebMatrix には NuGet 1.2 で作ったパッケージがインストールできない

先日公開した Hatena.Helper 1.3 は NuGet 1.2 でパッケージングを行ったのですが、WebMatrix でインストールしようとすると以下のようなエラーが発生します。

どうやら schemaVersion という属性がスキーマに定義されていないので検証時にエラーが発生しているようです。そして schemaVersion 属性は NuGet 1.2 でパッケージングされた時に追加されるようになったみたいですね。

参考までに Hatena.Helper 1.2 と 1.3 の nuspec を出しておきます。

Hatena.Helper 1.2 の nuspec

<?xml version="1.0"?>
<package xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <metadata xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
    <id>Hatena.Helper</id>
    <version>1.2</version>
    <title>Hatena.Helper</title>
    <authors>shiba-yan</authors>
    <owners>shiba-yan</owners>
    <requireLicenseAcceptance>false</requireLicenseAcceptance>
    <description>The Hatena Web Services (Bookmark, Haiku, Profile, Star) Helper for WebMatrix.</description>
    <language>en-US</language>
    <tags>ASPNETWEBPAGES</tags>
  </metadata>
</package>

Hatena.Helper 1.3 の nuspec

<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
  <metadata schemaVersion="2">
    <id>Hatena.Helper</id>
    <version>1.3</version>
    <title>Hatena.Helper</title>
    <authors>shiba-yan</authors>
    <owners>shiba-yan</owners>
    <requireLicenseAcceptance>false</requireLicenseAcceptance>
    <description>The Hatena Web Services (Auth, Bookmark, Haiku, Profile, Star) Helper for WebMatrix.</description>
    <language>en-US</language>
    <tags>ASPNETWEBPAGES</tags>
  </metadata>
</package>

NuGet 1.2 でパッケージングした nuspec には schemaVersion="2" という属性が追加されていました。WebMatrix というか ASP.NET Web Pages が内蔵している NuGet バージョンが古いのが原因のようです。

折角なので NuGet 最新版のソースコードを読んでみました。nuspec ファイルの処理を行っているのは NuGet.Manifest クラスになります。

// Get the metadata node and look for the schemaVersion attribute
XElement metadata = GetMetadataElement(document);

if (metadata != null) {
    // Yank this attribute since we don't want to have to put it in our xsd
    XAttribute schemaVersionAttribute = metadata.Attribute(SchemaVersionAttributeName);

    if (schemaVersionAttribute != null) {
        schemaVersionAttribute.Remove();
    }

はい、見事に SchemaVersionAttributeName を削除するようになっていました。最新版ではこのような仕組みになっていたので、スキーマの変更なしでもエラーが発生していなかったわけです。

恐らく ASP.NET Web Pages が更新されれば修正されると思うのですが、ASP.NET MVC を見ても分かるようにこまめにアップデートされる気がしないのでどうなるのでしょう…?

追記

中の人から Twitter で返事をいただきました。

私は NuGet 1.2 が出たその日に更新して使っていたのですが、その後にもう一度アップデートされていたようです。コマンドラインから

NuGet.exe update

と打ち込んで NuGet.exe を更新してからパッケージングをやり直したところ、WebMatrix でもインストール出来るようになりました!!