ページングとか、最大取得件数とかソートとかetc...そこらへんとかは、誰が実装しても同じようなコードになるので提供されているライブラリで楽して実装したい。そういうのが人情だと思います。今回はASP.NET WebAPIのOData対応の機能を使ってソート、フィルター、ページングなどの処理をODataのクエリにお任せしてしまう方法をちょろっとやってみようと思います。
ODataQueryOptionsクラス
そういう役目をしてくれるクラスがWebAPIのODataの機能の中にばっちりいたりします。
- System.Web.Http.OData.Query.ODataQueryOptions<TEntity>
- http://msdn.microsoft.com/query/dev11.query?appId=Dev11IDEF1&l=JA-JP&k=k("System.Web.Http.OData.Query.ODataQueryOptions`1");k(TargetFrameworkMoniker-.NETFramework,Version%3Dv4.5);k(DevLang-csharp)&rd=true
こいつをODataのクエリを有効にしたWebAPIの引数で受け取るようにすると勝手にクエリを解釈した結果が入ってます。ODataのクエリを有効化するには以下の方法でさくっとやっちゃいましょう。 - ASP.NET 空の Web アプリケーションからWebAPIを使えるようにするための道のり「作成からヘルプページ、ODataのクエリの有効化まで」
上記の方法は、空のテンプレートからですがASP.NET MVC4のプロジェクトを作ってWebAPIのテンプレートを選択して、WebApiConfigで以下の一行のコメントを外すでもOKです。
config.EnableQuerySupport();
さて、ODataQueryOptoionsを使うと何が嬉しいかというと、このオブジェクトをRepositoryあたりまで引数で渡してやると、ApplyToメソッドでIQueryableに対してODataで設定された条件を指定してくれること…!自前でパラメータ解析しなくていい点がいいですね。よくある、ControllerクラスにDbContextとか持たせてる方法だと、あんまり気にしなくてもいいですが、Controllerの下に何層かレイヤがあるときは、IQueryableに対する操作をControllerでしたくないということもあると思いますので、こいつを使うのがいいと思います。
さっそく使ってみましょう。
コントローラーの前にコントローラーから呼び出す予定のクラスを作ります。なんとんく、こいつにODataQueryOptionsを直で渡すのは、いやだったので、ラムダ式でフィルタを追加で指定できるようなシグネチャにしてみました。name引数で受け取った値をもとに絞り込み条件を設定したあとに、optionalFilterに値が設定されていたら、IQueryable<Person>を渡して追加処理を行います。
publicclass PeopleService { privatestaticreadonly IList<Person> Store = Enumerable.Range(1, 100) .Select(i => new Person { Id = i, Name = "tanaka" + i }) .ToList(); public IQueryable<Person> GetPerson( string name, Func<IQueryable<Person>, IQueryable<Person>> optionalFilter = null) { var q = Store.AsQueryable() .Where(p => p.Name.Contains(name)); if (optionalFilter != null) { q = optionalFilter(q); } var list = q.ToList(); return list.AsQueryable(); } } publicclass Person { publicint Id { get; set; } publicstring Name { get; set; } }
あとは、コントローラーだけです。サービスを呼ぶだけの簡単な感じで。
publicclass PeopleController : ApiController { private PeopleService service = new PeopleService(); public IQueryable<Person> Get(ODataQueryOptions<Person> options, string name) { returnthis.service.GetPerson( name, optionalFilter: q => options.ApplyTo(q) as IQueryable<Person>); } }
ODataQueryOptions<Person>を引数で受け取ってApplyToメソッドを使ってOData関連のクエリをお任せしています。もちろんOData以外のパラメータを受けることはできるので、nameという引数も追加してみました。こいつを実行してFiddlerあたりから軽く呼び出してみます。名前にa1を含む人を3人とか
orderbyしてみたりとか
ばっちり動きますね!あとはクライアント側で、このクエリ文字列を組み立てるのが簡単にできたら文句ないんですが…。