WCF Data Servicesの新機能 – リクエストパイプライン

WCF Data Servicesの新機能、リクエストパイプラインをご紹介します。(2/24に実施されたTechDaysセッションのフォローアップも兼ねています)

このシリーズの目次は以下になります。
1) WCF Data Servicesの新機能 – 射影
2) WCF Data Servicesの新機能 – カウント
3) WCF Data Servicesの新機能 – Server Driven Paging(SDP)
4) WCF Data Servicesの新機能 – Feed Customization
5) WCF Data Servicesの新機能 – データバインド
6) WCF Data Servicesの新機能 – カスタムプロバイダ1
7) WCF Data Servicesの新機能 – カスタムプロバイダ2
8) WCF Data Servicesの新機能 – リクエストパイプライン
9)Open Data Protocolの実装 – Share Point Server 2010のデータを操作する

また以下のトピックに関しては1)の記事を参照してください。
■名称の変更
■新バージョン
■更新モジュール(開発環境)

■リクエストパイプライン
データサービスのパイプライン処理のイベントが解放されました。例えば、サービスのリクエスト処理の直前などに処理を挟むことが可能です。開発チームのBlogにはこの機能の目的について以下のように書いてあります。

The goal of exposing our processing pipeline is to allow services further transparency into a data service such that a service author can do things such as setting HTTP response cache headers, wrapping interceptor processing and data service request processing in a single transaction, etc.

(超意訳)
サービスの作者が、HTTPレスポンスのキャッシュヘッダーのセッティングとかとか、出来るようにしたいんだよね。

では、実際に処理を記述してみたいと思います。

まず、典型的な使用方法としては、以下のように、カスタムプロバイダ作成の中で、処理を挟む形になるはずです。(Boldの部分)

public object GetService(Type serviceType) {     if (serviceType == typeof(IDataServiceMetadataProvider))         return metadata;     else if (serviceType == typeof(IDataServiceQueryProvider))     {         this.ProcessingPipeline.ProcessingRequest += new EventHandler<DataServiceProcessingPipelineEventArgs> (ProcessingPipeline_ProcessingRequest);         return query;     }     else         return null; }

void ProcessingPipeline_ProcessingRequest(object sender, DataServiceProcessingPipelineEventArgs e) {     throw new NotImplementedException(); }

ここでは、イベントハンドラになにも書いてありませんので、エラーが発生するだけですが、ここに何らかの処理(開発チームのゴールならキャッシュ関係など)を書きます。
また、指定できるイベントの種類は以下になります。
https://msdn.microsoft.com/en-us/library/system.data.services.dataserviceprocessingpipeline_events.aspx

カスタムプロバイダだけで使用するだけではなく、Entity Providerでも似たようなことをするための手軽な手法の一つとして、DataService<T>のOnStartProcessingRequestがあります。リクエスト処理前にコールされるメソッドです。純粋なリクエストパイプライン処理とは異なりますが、これで実装をしてみます。

public class pubsService : DataService< pubsEntities > {     public static void InitializeService(DataServiceConfiguration config)     {         config.SetEntitySetAccessRule("*", EntitySetRights.All);         config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;     }

    protected override void OnStartProcessingRequest (ProcessRequestArgs args) { base.OnStartProcessingRequest(args);           string method = HttpContext.Current.Request.HttpMethod.ToUpper(); if (method == "GET") { HttpContext context = HttpContext.Current; HttpCachePolicy c = HttpContext.Current.Response.Cache; c.SetExpires(HttpContext.Current.Timestamp.AddSeconds(20)); c.SetCacheability(HttpCacheability.Public); c.SetValidUntilExpires(true); c.VaryByHeaders["Accept-Language"] = true; } } }

普通に作成したDataServiceにBold部分を追加しただけです。
開発チームがいうようにキャッシュヘッダーに手を加えています。ここでは、Languageの設定ごとに20秒キャッシュを保つような設定です。

ちなみに、この方法は以下を参考にしています。(こんなのもCodeRecipeの逆引きにあるといいですよね・・・)
方法 - HTTP ヘッダーを使用してページのバージョンをキャッシュする

では、実行してみます。以下のように、適当ににクエリーしてみます。

https://localhost:3049/pubsService.svc/jobs

そうすると、赤枠あたりに時間が表示されていると思いますが、 F5キーなどで再リクエストしても20秒はキャッシュが使用されて時刻が変化しないのがわかります。( 上記のBoldを除いた通常のリクエストで確認すれば、この時刻は毎回変更されています。)

image