かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

ASP.NET Web APIでOData + Windows ストア アプリから読み込み

先日、セルフホスト機能を使ってASP.NET Web APIをホストしたプロセスの公開するサービスをWindows ストア アプリから呼び出してみました。今度は、ASP.NET Web APIのOData機能を使ってODataのサービスを作ってみようと思います。

プロジェクト作成

ASP.NET MVC4のプロジェクトを作成してWebAPIを選びます。ControllersフォルダにPeopleControllerを作成して以下のコードを書きます。

namespace ODataWebAPISample.Controllers
{
    using System.Web.Http.OData;

    // EntitySetController<TEntity, TKey>を継承したコントローラを作ります
    public class PeopleController : EntitySetController<Person, int>
    {
    }

    // ODataとして公開するクラス
    public class Person
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }
}

System.Web.Http.ODataという名前空間にEntitySetControllerというコントローラの基本クラスが用意されてるので、こいつを継承したコントローラを作ります。
とりあえず、データの取得だけならIQueryableを返すGetメソッドと、TEntityを返すGetEntityByIdメソッドをオーバーライドすればOKです。ダミーデータを返す形で作ってみたコードが以下です。

namespace ODataWebAPISample.Controllers
{
    using System.Collections.Generic;
    using System.Linq;
    using System.Web.Http.OData;

    // EntitySetController<TEntity, TKey>を継承したコントローラを作ります
    public class PeopleController : EntitySetController<Person, int>
    {
        // テスト用のデータ
        private static readonly ICollection<Person> People = Enumerable.Range(1, 100)
            .Select(i => new Person { Id = i, Name = "田中 太郎" + i })
            .ToList();

        // データ取得
        public override IQueryable<Person> Get()
        {
            return People.AsQueryable();
        }
        // キーを元にデータ取得
        protected override Person GetEntityByKey(int key)
        {
            return People.FirstOrDefault(p => p.Id == key);
        }
    }

    // ODataとして公開するクラス
    public class Person
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }
}

App_StartのWebApiConfig.csで、ODataのエンドポイントを構成します。先ほど作ったPersonクラスを公開するコードは以下のようになります。

namespace ODataWebAPISample
{
    using ODataWebAPISample.Controllers;
    using System.Web.Http;
    using System.Web.Http.OData.Builder;

    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // ODataのエンドポイントを構成する
            var builder = new ODataConventionModelBuilder();
            // EntityとしてPeopleを登録
            builder.EntitySet<Person>("People");
            // odataという名前で上で構成したODataを公開する
            config.Routes.MapODataRoute(
                "ODataDefault",
                "odata",
                builder.GetEdmModel());
            // クエリの実行を許可
            config.EnableQuerySupport();
        }
    }
}

ODataConventionModelBuilderを使ってれば割とよきにはからってくれるみたいです。EntitySetメソッドを呼び出してPersonクラスをPeopleという名前で登録してMapODataRouteメソッドでodataというURLでアクセスできるようにしています。

実行して動作確認

ODataの準備が出来たので実行してみます。実行したらhttp://localhost:ポート/odata/$metadataでメタデータを取得してみましょう。なんとなく、いけてそうですよね?

試しにFiddlerでhttp://localhost:5629/odata/Peopleにアクセスしてみると、以下のようなJSONが取得できます。ばっちり動いてます。

{
  "odata.metadata":"http://localhost:5629/odata/$metadata#People","value":[
    {
      "Id":1,"Name":"\u7530\u4e2d\u3000\u592a\u90ce1"
    },{
      "Id":2,"Name":"\u7530\u4e2d\u3000\u592a\u90ce2"
    },{
      "Id":3,"Name":"\u7530\u4e2d\u3000\u592a\u90ce3"
    },{
      "Id":4,"Name":"\u7530\u4e2d\u3000\u592a\u90ce4"
    },{
    ...省略...
}

ODataなので例えばhttp://localhost:port/odata/People(4)みたいにピンポイントでデータをとることもOKです。

クライアントアプリ

さて、ODataで公開してると何が嬉しいってクライアントアプリでメタデータをもとにスタブを作ってくれるところです。前回Windows ストア アプリからデータ取得をやったので、今回も同じ要領でやってみようとおもいます。

Windows ストア アプリでODataを読み込むためには以下のツールをインストールしてください
WCF Data Services Tools for Windows Store Apps
  http://www.microsoft.com/en-us/download/details.aspx?id=30714

ClientAppという名前でストアアプリを作ってサービス参照の追加を行います。
http://localhost:port/odata/$metadataを入力すると以下のように表示されると思うので名前空間を適当に入れて(ここではODataWebAPISampleにしました)追加をします。

追加すると、Service ReferencesにODataWebAPISampleが追加されます。

あとは画面に適当にGridViewを置いてODataのデータを取得して表示してみましょう。Windows ストア アプリでのODataの扱いは、この本に書いてあった気がします。(ステマ)

ちょっと色気を出してIdが偶数の人だけとってくるようにしています。

private ODataWebAPISample.Container container = new ODataWebAPISample.Container(new Uri("http://localhost:5629/odata/"));
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
    base.OnNavigatedTo(e);
    // IDが偶数の人のみ抽出
    var query = (DataServiceQuery<Person>) this.container.People.Where(p => p.Id % 2 == 0);
    var task = Task.Factory.FromAsync<IEnumerable<Person>>(
        query.BeginExecute,
        query.EndExecute, 
        null);
    this.gridView.ItemsSource = await task;
}

実行してみると、ばっちり偶数番の人のデータがとれてます!

なかなか、いいですな。