情報
要求されたトピックは次のとおりです。しかし、このトピックはこのライブラリには含まれていません。

Windows Phone 8 の OData クライアント

2014/06/18

対象: Windows Phone 8 および Windows Phone Silverlight 8.1 | Windows Phone OS 7.1

Open Data Protocol (OData) はエンティティ関係モデルに基づいており、Representational State Transfer (REST) リソースのスタイルでのデータ アクセスを可能にします。Windows Phone の OData クライアント ライブラリを使用すると、Windows Phone アプリで標準の HTTP プロトコルを使用してクエリを実行したり、データ サービスからのデータの作成、更新、削除を行うことができます。Windows Phone の OData クライアントは Windows Phone SDK には含まれておらず、WCF Data Services Tools for Visual Studio から別途ダウンロードする必要があります。クライアント ライブラリは、OData v3 プロトコルをサポートするサービスに対する HTTP 要求を生成し、応答フィード内のデータをクライアント上でオブジェクトに変換します。OData の詳細および Windows Phone の OData クライアント ライブラリを使用してアクセスできる既存のデータ サービスについては、OData の Web サイトを参照してください。

クライアント ライブラリの主要なクラスには、DataServiceContext クラスと DataServiceCollection クラスの 2 つがあります。DataServiceContext クラスは、特定のデータ サービスに対して実行される操作をカプセル化します。OData ベースのサービスはステートレスです。ただし、データ サービスを使用したインタラクション間のエンティティの状態は、DataServiceContext クラスによってクライアント上で保持されます。これによって、変更追跡や ID 管理などの機能をクライアント側でサポートすることができます。Windows Phone の OData クライアント ライブラリには、.NET Framework に含まれる WCF Data Services クライアントで提供される OData サービスに非同期でアクセスするための同様の機能が用意されています。

このトピックの以下のセクションでは、クライアント ライブラリを使用して OData サービスにアクセスする方法について説明します。

OData フィードを公開するサービスの参照は、Visual Studio の [サービス参照の追加] ダイアログ ボックスを使用して追加できます。詳細については、「Windows Phone 8 の OData サービスの使用方法」を参照してください。クライアント プロキシ クラスは、コマンド プロンプトで DataSvcUtil.exe ツールを使用して生成することもできます。

DataServiceCollection クラスは、ObservableCollection<T> クラスを継承し、コレクション項目が追加または削除されたときの通知機能を備えた動的データ コレクションを表します。これらの通知により、変更追跡メソッドを明示的に呼び出さなくても、DataServiceContext で変更を自動的に追跡できます。

クエリによって、DataServiceCollection インスタンスに含まれるデータ オブジェクトが決まります。このクエリは、DataServiceQuery クラスを使用して統合言語クエリ (LINQ) として構成するか、または URI (Uniform Resource Identifier) の形で提供することができます。OData の URI クエリの構文の詳細については、「OData: URI 表記」を参照してください。このクエリはその後、DataServiceCollection クラスの LoadAsync(IQueryable<UTP>) メソッドのパラメーターとして指定されます。実行すると、このメソッドは、コレクションのデータ オブジェクトに逆シリアル化される OData フィードを返します。

DataServiceCollection クラスの LoadAsync(IQueryable<UTP>) メソッドによって結果が正しいスレッドにマーシャリングされるため、Dispatcher オブジェクトを使用する必要はありません。LoadCompleted() イベントは、データ サービスから応答を受信した後に発生します。

メモメモ:

アクティブな LoadAsync() 操作をキャンセルするには、CancelAsyncLoad() メソッドを呼び出します。

データ バインドに DataServiceCollection のインスタンスを使用した場合、DataServiceContext によって追跡されたオブジェクトと、バインド先の UI 要素のデータとの同期状態がクライアントによって確実に維持されます。バインディング コレクションのエンティティに変更を手動で報告する必要はありません。

ヒントヒント:

データ アプリでは Model-View-ViewModel (MVVM) のデザイン パターンを使用することをお勧めします。MVVM では、データ サービスによって返されるモデルに基づいてモデルが生成されます。この方式を使用すると、ViewModel クラスで DataServiceContext を作成し、Windows Phone のコントロールへのデータ バインドに必要な DataServiceCollection インスタンスやその他のデータ構造を公開することができます。MVVM パターンに関するその他の一般的な情報については、「Windows Phone 8 での Model-View-ViewModel パターンの実装」を参照してください。

Windows Phone アプリでは、データ サービスに対する操作はすべて非同期に実行されます。非同期操作は、DataServiceContext クラスのそれぞれ BeginEnd で始まる一対のメソッドを使用して実行します。Begin メソッドは、操作の完了時にサービスによって呼び出されるデリゲートを登録します。End メソッドは、完了した操作からのコールバックを処理するために登録したデリゲートで呼び出す必要があります。

メモメモ:

DataServiceCollection クラスを使用する場合、非同期操作およびマーシャリングは自動的に処理されます。非同期操作を直接使用する場合、応答操作をアプリのメイン アプリ スレッド (UI スレッド) に正しくマーシャリングするためには、Dispatcher クラスの BeginInvoke メソッドを使用する必要があります。

End メソッドを呼び出して非同期操作を完了する際は、操作を開始したときと同じ DataServiceContext インスタンスから End メソッドを呼び出す必要があります。各 Begin メソッドは状態パラメーターを受け取ります。このパラメーターを使用して、状態オブジェクトをコールバックに渡すことができます。この状態オブジェクトは、コールバックで提供される IAsyncResult インターフェイスを使用して取得されます。この状態オブジェクトを使用して、対応する End メソッドを呼び出すことによって、非同期操作を完了させます。

たとえば、DataServiceContext インスタンスの BeginExecute``1(DataServiceQueryContinuation<UMP>, AsyncCallback, Object) メソッドを呼び出すときに、そのインスタンスを state パラメーターとして指定した場合は、同じ DataServiceContext インスタンスが IAsyncResult パラメーターとして返されます。さらに、この DataServiceContext インスタンスを使用し、EndExecute``1(IAsyncResult) メソッドを呼び出すことによって、クエリ操作を完了させます。詳細については、「非同期操作 (WCF Data Services)」を参照してください。

リソースの照会

Windows Phone の OData クライアント ライブラリを使用すると、統合言語クエリ (LINQ) など、使い慣れたプログラミング パターンを使用して、OData サービスに対してクエリを実行することができます。DataServiceQuery またはクエリ URI が実行されると (通常、LoadAsync(IQueryable<UTP>) メソッドを呼び出したとき)、クライアント ライブラリがクエリまたは URI を HTTP GET 要求メッセージへと変換します。このクライアント ライブラリは対応する応答メッセージを受け取り、返されたフィードのエントリをクライアント データ サービス クラスのインスタンスに変換します。これらのクラスは、DataServiceQuery が属している DataServiceContext によって追跡されます。

一部のシナリオでは、クエリによって返されるフィード内の数だけでなく、エンティティ セット内のエンティティの総数がわかると便利です。DataServiceQueryIncludeTotalCount() メソッドを呼び出して、セット内のエンティティの総数をクエリ結果に含めるように要求します。この場合、返される QueryOperationResponseTotalCount() プロパティでセット内のエンティティの総数が返されます。QueryOperationResponse は、LoadCompleted() イベントを処理する際に受信する LoadCompletedEventArgs から取得できます。

DataServiceQuery クラスを使用してクエリを作成する際には、AddQueryOption(String, Object) メソッドを使用して OData によってサポートされる他のクエリ オプションをクエリに追加することができます。

LINQ クエリ

DataServiceQuery クラスは IQueryable を実装するため、Windows Phone 用 OData クライアント ライブラリは、エンティティ セット データに対する LINQ クエリを、データ サービス リソースに対して評価されたクエリ式を表す URI に変換できます。たとえば、次の LINQ クエリは、Order テキスト ボックスでユーザーが指定した CustomerID プロパティ値によってフィルター処理された customerId エンティティのコレクションを含むフィードを返します。

// Define a query that returns orders for a given customer. 
    var query = from orderByCustomer in context.Orders 
        where orderByCustomer.Customer.CustomerID == this.customerId.Text 
        select orderByCustomer; 

遅延コンテンツの読み込み

既定で、OData ではクエリが返すデータの量が制限されます。ただし、必要な場合には、データ サービスから、関連するエンティティ、ページング応答データ、およびバイナリ データ ストリームなどの、追加データを明示的に読み込むことができます。クエリを実行すると、アドレス指定されたエンティティ セットのエンティティのみが返されます。

たとえば、CustomersOrders との間にリレーションシップが存在する場合でも、Northwind データ サービスに対するクエリが Orders エンティティを返す場合、既定では関連する Customers エンティティは返されません。関連するエンティティは元のクエリで読み込むか (一括読み込み)、またはエンティティごとに読み込む (明示的読み込み) ことができます。

関連するエンティティを明示的に読み込むには、ナビゲーション プロパティによって返される DataServiceCollectionLoadAsync() メソッド、または DataServiceContext クラスの BeginLoadProperty(Object, String, AsyncCallback, Object) メソッドおよび EndLoadProperty(IAsyncResult) メソッドを呼び出す必要があります。これは、関連するエンティティを読み込むエンティティごとに 1 回だけ行います。これらのメソッドに対する呼び出しごとに、データ サービスへの新しい要求が生成されます。一括読み込みに関連するエントリを実行するには、DataServiceQueryExpand(String) メソッドを使用して、生成されるクエリ URI に $expand クエリ オプションを追加する必要があります。これはすべての関連データを 1 つのリクエストで読み込むため、返されるペイロードが大幅に大きくなります。

重要:重要:

関連するエンティティを読み込むパターンについて判断する際には、メッセージ サイズとデータ サービスへの要求回数の関係がパフォーマンスにどのような影響を及ぼすかを考慮します。

次の LINQ クエリは、選択した顧客に属する Order オブジェクトおよび Order_Details オブジェクトの一括読み込みの例です。


// Define a query that returns orders along with order details for a given customer.
var query = from orderByCustomer in context.Orders.Expand("Order_Details")
            where orderByCustomer.Customer.CustomerID == this.customerId.Text
            select orderByCustomer;

データ サービスでページングが有効になっている場合に、返されたエントリ数がページング制限を超えると、後続のデータ ページをデータ サービスから明示的に読み込む必要があります。ページングがいつ発生するかを事前に特定することはできないため、アプリでページングされた OData フィードを適切に処理できるようにすることをお勧めします。ページングされた応答を読み込むには、現在の DataServiceQueryContinuation トークンを使用して BeginLoadProperty(Object, String, AsyncCallback, Object) メソッドを呼び出す必要があります。DataServiceCollection クラスを使用する場合は、代わりに LoadNextPartialSetAsync() メソッドを呼び出すのと同じ方法で、LoadAsync() メソッドを呼び出すことができます。この読み込みパターンの例については、「Windows Phone 8 の OData サービスの使用方法」を参照してください。

バイナリ データの操作

OData は、属するエンティティから独立したバイナリ データにアクセスするための機構を定義します。これにより、OData サービスは、大きなバイナリ データをメディア リンク エントリに属するメディア リソースとして公開できます。Windows Phone 用 OData ライアント ライブラリは、OData サービスからのメディア リソースをバイナリ ストリームとして使用できます。このバイナリ ストリームにアクセスするには、メディア リンク エントリであるエンティティを追跡する DataServiceContext インスタンスで BeginGetReadStream(Object, DataServiceRequestArgs, AsyncCallback, Object) メソッドを呼び出します。この非同期メソッドは、DataServiceContextEndGetReadStream(IAsyncResult) メソッドが呼び出されたときに、DataServiceStreamResponse オブジェクトを返します。この方法はメディア リソースをストリームとして返す場合に使用します。このストリームは、メディア リソースを分離ストレージに保存する際に使用できます。

メディア リソースを UI の Image コントロールにバインドする場合は、代わりに GetReadStreamUri(Object) メソッドを呼び出して、バインド時にイメージを作成するのに使用するメディア リソースの edit-media URI を取得することができます。データ バインドをサポートするには、バインディングに使用する拡張プロパティの getter またはバインディングで使用する値コンバーターのいずれかで GetReadStreamUri(Object) メソッドを呼び出します。詳細については、「メディア リソース ストリームの XAML コントロールへのバインド」を参照してください。

OData サービスに新しいメディア リソースを作成するには、最初に新しいエンティティ インスタンスを作成してから、SetSaveStream(Object, Stream, Boolean, String, String) メソッドを呼び出して、新しいメディア リンク エントリ インスタンスのメディア リソース データを含む Stream オブジェクトを提供します。クライアントは POST 要求を送信して、BeginSaveChanges(AsyncCallback, Object) メソッドおよび EndSaveChanges(IAsyncResult) メソッドが呼び出された後で、新しいメディア リソースを挿入します。詳細については、「Windows Phone 7 アプリケーションからのメディア リソース ストリームへのアクセス」を参照してください。

クエリの射影

射影は、エンティティの特定のプロパティのみが応答で返されるように指定することで、クエリによって返される OData フィード内のデータ量を削減するメカニズムを提供します。詳細については、「OData: Select システム クエリ オプション ($select)」を参照してください。LINQ クエリに射影句を追加するには、select 句を使用します (Visual Basic の Select)。返されたエンティティ データは、クライアントでエンティティ型または非エンティティ型に射影できます。非エンティティ型に対する変更をデータ サービスに保存することはできません。たとえば、次の LINQ クエリは、Customer データを新しい CustomerAddress エンティティ型に射影します。

var query = from c in context.Customers
            where c.Country == "Germany"
            select new CustomerAddress
            {
                CustomerID = c.CustomerID,
                Address = c.Address,
                City = c.City,
                PostalCode = c.PostalCode,
                Country = c.Country
            };
注意注意:

射影された型に加えられた更新を保存する際に、データ サービスでデータが失われる可能性があります。詳細については、WCF Data Services クライアント ドキュメントの「射影に関する考慮事項」を参照してください。

クライアントでの CustomerAddress 型の定義などの詳細な例については、「方法: データ サービスのクエリ結果を射影する」を参照してください。

OData クライアントの変更を手動で追跡するには、DataServiceContext クラスで AddObject(String, Object)UpdateObject(Object)、および DeleteObject(Object) メソッドを使用します。これらのメソッドによってクライアントは、エンティティの追加と削除のほか、プロパティの値に対する変更やエンティティ インスタンス間のリレーションシップに対する変更を追跡することができます。

プロキシ クラスが生成されると、DataServiceContext クラス内の各エンティティごとに AddTo メソッドが作成されます。これらのメソッドを使用して、エンティティ セットに新しいエンティティ インスタンスを追加し、その "追加" という操作をコンテキストに報告します。DataServiceContext クラスの BeginSaveChanges(AsyncCallback, Object) メソッドおよび EndSaveChanges(IAsyncResult) メソッドを呼び出すと、追跡された変更が、データ サービスに非同期的に送り返されます。

メモメモ:

DataServiceCollection オブジェクトを使用してデータをコントロールにバインドすると、変更内容が DataServiceContext インスタンスに自動的に報告されるため、コレクション内のオブジェクトに変更内容を報告する必要はありません。

次の例は、BeginSaveChanges(SaveChangesOptions, AsyncCallback, Object) メソッドおよび EndSaveChanges(IAsyncResult) メソッドを呼び出して、更新を Northwind データ サービスに非同期送信する方法を示しています。

private void saveChanges_Click(object sender, RoutedEventArgs e)
{
    // Start the saving changes operation.
    svcContext.BeginSaveChanges(SaveChangesOptions.Batch, 
        OnChangesSaved, svcContext);
}

private void OnChangesSaved(IAsyncResult result)
{
    // Use the Dispatcher to ensure that the 
    // asynchronous call returns in the correct thread.
    Dispatcher.BeginInvoke(() =>
        {
            svcContext = result.AsyncState as NorthwindEntities;

            try
            {
                // Complete the save changes operation and display the response.
                WriteOperationResponse(svcContext.EndSaveChanges(result));
            }
            catch (DataServiceRequestException ex)
            {
                // Display the error from the response.
                WriteOperationResponse(ex.Response);
            }
            catch (InvalidOperationException ex)
            {
                messageTextBlock.Text = ex.Message;
            }
            finally
            {
                // Set the order in the grid.
                ordersGrid.SelectedItem = currentOrder;
            }
        }
    );
}

メモメモ:

OData の Web サイトで公開されている Northwind サンプル データ サービスは、読み取り専用です。変更を保存しようとすると、エラーが返されます。このコード例を正常に実行するには、専用の Northwind サンプル データ サービスを作成する必要があります。これを行うには、「方法: Northwind データ サービスを作成する」の手順を実行します。

通常、ユーザーがアプリから離れると、アプリは休止状態になります。この状態では、アプリはメモリ内に保持されているため、ユーザーがアプリに戻るとすぐに再開できます。この高速なアプリ切り替えは、自動的に有効になります。ただし、休止状態のときにアプリが終了される可能性はあります。アプリのライフ サイクルを通して発生するこのような状態の変化を処理するように、アプリを設計することが重要です。詳細については、「Windows Phone 8 のアプリのアクティブ化および非アクティブ化」を参照してください。終了の発生時にアプリの状態が失われないようにするため、アプリ ライフ サイクル中に発生するイベントのハンドラーを実装することができます。これらのハンドラーで、アプリがアクティブ状態と非アクティブ状態との間を遷移する場合に、DataServiceContext クラスおよび DataServiceCollection インスタンスの状態を保存して復元することができます。これら 2 つのオブジェクトがシリアル化される場合は、まだデータ サービスに送信されていないメディア リソース ストリームなど、追跡されるエンティティ オブジェクトや関連するエンティティ オブジェクトもすべてシリアル化されます。入れ子にされたコレクション間の関係も、コレクション自体が復元されるときに復元されます。この動作によって、実行が終了した場合でも、ユーザーにはアプリの実行が継続されているように見えます。

Windows Phone の OData クライアントには、これらの状態遷移を管理するのに役立つ DataServiceState クラスが含まれています。これらの状態管理イベントは、メインのアプリの分離コード ページで処理されます。Windows Phone の状態の変化と、状態の変化ごとの DataServiceState クラスの使用方法を次の表に示します。

ページを非アクティブにする場合またはページから移動する場合は、DataServiceState クラスで Serialize(DataServiceContext, Dictionary<String, Object>) メソッドを呼び出して、DataServiceContext インスタンスを渡します。オプションで、関連するコレクションのグラフを含む DataServiceCollection インスタンスを格納する Dictionary<TKey, TValue> オブジェクトを渡すことができます。次の例は、シリアル化するコンテキストとページの状態に格納されるコレクション データを示しています。

// Define a dictionary to hold an existing DataServiceCollection<Customer>. 
var collections = new Dictionary<string, object>();
collections.Add("Customers", customers);

// Serialize the data service data into the state dictionary.      
this.State["DataServiceState"] = 
   DataServiceState.Serialize(context, collections);

シリアル化され格納されている DataServiceState が State ディクショナリから取得されます。ページをアクティブにする場合またはページに移動する場合は、DataServiceState インスタンスで Deserialize(String) メソッドを呼び出します。格納されている DataServiceContext インスタンスと、指定された DataServiceCollection インスタンスを含む任意の Dictionary<TKey, TValue> オブジェクトが返されます。次の例は、格納されているデータ サービスの状態データの逆シリアル化を示しています。

object storedState;
DataServiceState state;

// Get the serialized data service state.
if (this.State.TryGetValue("DataServiceState", out storedState))
{
    // Deserialize the DataServiceState object.
    state = DataServiceState.Deserialize(storedState as string);

    // Set the context from the stored object.
    context = (NorthwindEntities)state.Context;

    // Set the binding collections from the stored collection.
    customers = state.RootCollections["Customers"] as DataServiceCollection<Customer>;
}

ヒントヒント:

追跡対象のエンティティ オブジェクトを直接 State ディクショナリに格納しようとすると、シリアル化中にエラーが発生する可能性があります。個別の追跡対象オブジェクトの状態を維持する必要がある場合は、代わりにエンティティの URI を格納します。TryGetUri(Object, Uri) メソッドを呼び出すことによってエンティティ オブジェクトの URI を取得することができます。その後で、格納された URI を TryGetEntity``1(Uri, UMP) メソッドに渡すことによって、復元された DataServiceContext からオブジェクトを取得することができます。

詳細については、「Windows Phone 8 の OData クライアントの状態を保持する方法」を参照してください。

アプリで MVVM デザイン パターンを実装する場合は、ビュー モデル自体でデータ サービスの状態のシリアル化と逆シリアル化を行うことをお勧めします。詳細については、「チュートリアル: Windows Phone 8 用の MVVM で OData を使用する」を参照してください。

Windows Phone の OData クライアント ライブラリでは、要求を行うときにデータ サービスに提供される資格情報を指定できます。これを行うには、NetworkCredential などの ICredentials インターフェイスを実装するオブジェクトを DataServiceContextCredentials() プロパティに割り当てます。

セキュリティに関するメモセキュリティに関するメモ:

ユーザー資格情報は実行中に要求し、キャッシュしないようにします。資格情報は常に安全に保存する必要があります。

次のコード行では、この DataServiceContext インスタンスを使用してデータ サービスに対して行う要求の資格情報を設定します。

context.Credentials = 
    new NetworkCredential(userName, password, domain);

この資格情報のプロパティは、基本、ダイジェスト、および Windows 認証などの、パスワードベースの認証方式をサポートします。

セキュリティに関するメモセキュリティに関するメモ:

基本認証およびダイジェスト認証で送信されるデータは暗号化されないため、データが外部の者に見られる可能性があります。また、基本認証の資格情報 (ユーザー名とパスワード) は平文で送信されるため、傍受される可能性があります。

また、OAuth 2.0 などの他のクレームベースの認証メカニズムを使用して、Windows Phone アプリを認証することもできます。このようなクレームベースの認証方式を使用する場合は、特定のクレーム提供元が必要とするヘッダーを手動で設定する必要があります。要求ヘッダーにアクセスして変更するには、DataServiceContext によって発生した SendingRequest() イベントを処理する必要があります。「OAuth 2.0 で保護された OData サービスへの接続」は、Windows Phone アプリが Windows Azure AppFabric で Access Control Services (ACS) を使用して認証を行う場合にこの処理を行う方法を示しています。OData サービスでの認証に関する一般的な説明については、「WCF Data Services のセキュリティ」を参照してください。

Windows Phone の OData クライアント ライブラリは、Atom 形式の HTTP メッセージを使用して OData サービスと通信します。Atom は XML ベースの形式であるため、メッセージには、他の OData でサポートされている形式の JSON (JavaScript Object Notation) 形式を使用してフォーマットされた同じメッセージよりも多くの帯域幅を必要とします。Windows Phone の OData クライアント ライブラリは JSON 形式をサポートしていませんが、gzip 圧縮などの HTTP 圧縮スキームを使用して、必要なネットワーク帯域幅を減らすことができます。Accept-Encoding HTTP メッセージ ヘッダーを設定して、データ サービスで応答を圧縮するように要求するか、または POST 要求が圧縮されていることを指示します。このヘッダーの値は、要求された圧縮スキームの名前に設定されます。

重要:重要:

メッセージの圧縮により、データ サービスと Windows Phone デバイス間の通信のネットワーク使用率が低減します。ただし、データ サービスとデバイスの両方で、追加の処理リソースを消費することにもなります。この追加の処理は、通信の全体のパフォーマンスに影響し、デバイスのバッテリー残量に影響することがあります。

クライアントは次のイベントを提供し、処理時に、圧縮をサポートする OData サービスへの要求を圧縮し、応答を展開することができます。

ReadingResponse()

このイベントは、データ サービスから応答を受信した後でメッセージが読み取られる前に発生します。このイベントを処理して、応答が圧縮されているかどうかを判断し、必要に応じて応答を展開します。

WritingRequest()

このイベントは、HTTP 要求が生成される前に発生します。このイベントを処理して、圧縮された応答を要求するか、メッセージ本文を圧縮するように Accept-Encoding ヘッダーを設定します。

これらのイベントを処理する場合、提供された ReadingWritingHttpMessageEventArgs からメッセージ ヘッダーとメッセージ本文の両方にアクセスできます。メッセージ ヘッダーは、Headers() プロパティによって返された文字列値のディクショナリからアクセスします。メッセージ本文は、Content() プロパティからストリームとしてアクセスします。詳細については、「OData の圧縮」を参照してください。

重要:重要:

Windows Phone SDK には圧縮のサポートが含まれていません。圧縮を実装できるようにするには、Windows Phone をサポートするサードパーティ製圧縮ライブラリを使用する必要があります。さらに、OData サービスをホストする Web サーバーで、要求された圧縮スキームを有効にする必要もあります。

表示: