导出 (0) 打印
全部展开
信息
您所需的主题如下所示。但此主题未包含在此库中。

Windows Phone 8 的 OData 客户端

2014/6/18

适用于:Windows Phone 8 和 Windows Phone Silverlight 8.1 | Windows Phone OS 7.1

开放数据协议 (OData) 基于实体和关系模型,使您能够以具象状态传输 (REST) 资源的样式访问数据。通过使用 Windows Phone 的 OData 客户端库,Windows Phone 应用可以使用标准 HTTP 协议来执行查询,甚至创建、更新和删除数据服务中的数据。Windows Phone 的 OData 客户端不是 Windows Phone SDK 的一部分,您必须从 Visual Studio 的 WCF 数据服务工具中单独下载。客户端可以生成对任何支持 OData v3 协议的服务的 HTTP 请求,并且可以将响应源中的数据转换为对象。有关 OData 和可通过使用 Windows Phone OData 客户端库访问的现有数据服务的更多信息,请参见 OData 网站

客户端库的两个主类是 DataServiceContextDataServiceCollectionDataServiceContext 类封装对特定数据服务执行的操作。基于 OData 的服务是无状态的。但是,DataServiceContext 类在与数据服务的交互之间维持客户端上实体的状态。这使得客户端能够支持更改跟踪和标识管理之类的功能。对于异步访问在 .NET Framework 所含的 WCF 数据服务客户端中提供的 OData 服务,Windows Phone 的 OData 客户端库提供了相同的功能。

本主题中的以下各节介绍了如何使用客户端库来访问 OData 服务:

您可以使用 Visual Studio 中的“添加服务引用”对话框添加对公开 OData 源的任何服务的引用。有关更多信息,请参见如何使用 Windows Phone 8 的 OData 服务。还可以在命令提示处通过使用 DataSvcUtil.exe 工具生成客户端代理类。

DataServiceCollection 类(从 ObservableCollection<T> 继承)表示在将项添加到集合或者从集合中删除时提供通知的动态数据集合。通过这些通知,DataServiceContext 可以自动跟踪更改,而不必显式调用更改跟踪方法。

通过查询,可确定 DataServiceCollection 实例将包含哪些数据对象。查询既可以使用 DataServiceQuery 类撰写为语言集成查询 (LINQ) 查询,也可以采用统一资源标识符 (URI) 的形式提供。有关 OData 中的 URI 查询语法的更多信息,请参见 OData:URI 约定。然后,将该查询指定为 DataServiceCollection 类中 LoadAsync(IQueryable<UTP>) 方法的参数。此方法在执行时,可返回被反序列化为集合中的数据对象的 OData 源。

DataServiceCollection 类的 LoadAsync(IQueryable<UTP>) 方法将确保结果封送到正确的线程中,这样您就无需使用调度程序对象。在从数据服务接收响应后,将引发 LoadCompleted() 事件。

说明注意:

您可以通过调用 CancelAsyncLoad() 方法取消活动的 LoadAsync() 操作。

在您使用 DataServiceCollection 的实例进行数据绑定时,客户端确保 DataServiceContext 跟踪的对象保持与绑定的 UI 元素中的数据同步。您不需要手动报告绑定集合中实体的变化。

提示提示:

我们建议为您的数据应用采用模型视图查看模型 (MVVM) 设计模式,在此设计模式中,将根据数据服务返回的模型生成模型。通过使用此方法,可以在 ViewModel 类中创建 DataServiceContext,并公开任何所需 DataServiceCollection 实例或将数据绑定到 Windows Phone 控件所需的其他数据结构。有关 MVVM 模式的更多常规信息,请参见实现面向 Windows Phone 8 的模型视图查看模型模式

在 Windows Phone 应用中,针对数据服务的所有操作都是异步的。您可以通过对 DataServiceContext 类使用方法对(分别以 BeginEnd 开头),执行异步操作。Begin 方法注册一个委托,在操作完成时服务将调用该委托。End 方法应在注册为处理来自已完成操作的回调的委托中调用。

说明注意:

当使用 DataServiceCollection 类时,将会自动递交异步操作和封送。当直接使用异步操作时,您必须使用 Dispatcher 类的 BeginInvoke 方法来将响应操作正确封送回应用的主应用线程(UI 线程)。

当您调用 End 方法来完成异步操作时,必须从您用于开始该操作的相同 DataServiceContext 实例上完成该异步操作。每个 Begin 方法都采用可将状态对象传递到回调的参数。使用 IAsyncResult 界面检索此状态对象,该界面与回调一起提供并且用于调用相应的 End 方法来完成异步操作。

例如,如果提供 DataServiceContext 实例作为 state 参数,则当您对实例调用 BeginExecute``1(DataServiceQueryContinuation<UMP>, AsyncCallback, Object) 方法时,相同的 DataServiceContext 实例将作为 IAsyncResult 参数返回。然后,使用 DataServiceContext 的实例来调用 EndExecute``1(IAsyncResult) 方法以便完成查询操作。有关更多信息,请参见异步操作(WCF 数据服务)

查询资源

Windows Phone 的 OData 客户端库使您能够通过使用熟悉的编程模式(包括使用语言集成查询 (LINQ))对 OData 服务执行查询。当执行 DataServiceQuery 或查询 URI,通常在调用 LoadAsync(IQueryable<UTP>) 方法时,客户端库将查询或 URI 转换为 HTTP GET 请求消息。客户端库接收相应的响应消息,并将返回源中的条目转换为客户端数据服务类的实例。这些类由 DataServiceQuery 所属的 DataServiceContext 进行跟踪。

在某些情形中,这有助于获知实体集中实体的总数,而不仅仅是查询返回的源中的数量。调用 DataServiceQuery 上的 IncludeTotalCount() 方法来请求查询结果中应该包含该集中的实体总数。在此情况下,所返回 QueryOperationResponseTotalCount() 属性返回该集中的实体总数。可以从处理 LoadCompleted() 事件时接收的 LoadCompletedEventArgs 中获取 QueryOperationResponse

当您使用 DataServiceQuery 类撰写查询时,还可以使用 AddQueryOption(String, Object) 方法向查询添加 OData 支持的任何其他查询选项。

LINQ 查询

因为 DataServiceQuery 类实现 IQueryable,所以 Windows Phone 的 OData 客户端库可以将针对实体集数据的 LINQ 查询转换为表示针对数据服务资源而评估的查询表达式的 URI。例如,以下 LINQ 查询返回的源包含按用户在 customerId 文本框中提供的 CustomerID 属性值筛选的 Order 实体集合。

// 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 会限制查询返回的数据量。但是,如果需要,可从数据服务显式加载其他数据,包括相关实体、分页响应数据和二进制数据流。当执行查询时,仅返回寻址实体集中的实体。

例如,当针对 Northwind 数据服务的查询返回 Customers 实体时,即使 CustomersOrders 间存在关系,默认情况下也不会返回相关的 Orders 实体,相关实体可以使用原始查询加载(预先加载)或以每个实体为基础加载(显式加载)。

若要显式加载相关实体,必须针对导航属性返回的 DataServiceCollection 调用 LoadAsync() 方法,或者针对 DataServiceContext 类调用 BeginLoadProperty(Object, String, AsyncCallback, Object)EndLoadProperty(IAsyncResult) 方法。为您希望加载相关实体的每个实体执行一次该操作。这些方法的每次调用都会对数据服务发出一个新请求。若要执行预先加载相关项,必须针对 DataServiceQuery 使用 Expand(String) 方法,这样会将 $expand 查询选项追加到生成的查询 URI。这将会加载单个请求中的所有相关数据,但是会返回一个非常大的负载。

重要说明重要说明:

当决定加载相关实体的模式时,请考虑消息大小和数据服务请求数量间的性能折中。

下列 LINQ 查询显示属于选定客户的 OrderOrder_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 类时,您可以使用调用 LoadAsync() 方法的相同方式调用 LoadNextPartialSetAsync() 方法。有关此加载模式的示例,请参阅如何使用 Windows Phone 8 的 OData 服务

使用二进制数据

OData 定义了一种机制,用于访问独立于它所属实体的二进制数据。这样,OData 服务可以将大型二进制数据作为属于相关媒体链接项媒体资源公开。Windows Phone 的 OData 客户端库可以将 OData 服务中的媒体资源用作二进制流。若要访问二进制流,请对用于跟踪作为媒体链接项的实体的 DataServiceContext 实例调用 BeginGetReadStream(Object, DataServiceRequestArgs, AsyncCallback, Object) 方法。当对 DataServiceContext 调用 EndGetReadStream(IAsyncResult) 方法时,该异步方法返回 DataServiceStreamResponse 对象。当您希望返回媒体资源作为流时请执行此操作,在将媒体资源保存到独立存储时可能会使用流。

当您将媒体资源绑定到 UI 中的 Image 控件时,可以调用 GetReadStreamUri(Object) 方法来获取用于在绑定过程中创建图像的媒体资源的编辑-媒体 URI。为了支持数据绑定,可以在用于绑定的扩展属性的 getter 或绑定所使用的值转换器中调用 GetReadStreamUri(Object) 方法。有关更多信息,请参见内容发布将媒体资源流绑定到 XAML 控件

若要在 OData 服务中创建一个新的媒体资源,请首先创建一个新的实体实例,然后调用 SetSaveStream(Object, Stream, Boolean, String, String) 方法,提供包含新媒体链接项实例的媒体资源数据的 Stream 对象。在调用 BeginSaveChanges(AsyncCallback, Object)EndSaveChanges(IAsyncResult) 方法之后,客户端会发送 POST 请求以插入新的媒体资源中。有关更多信息,请参见从 Windows Phone 7 应用程序访问媒体资源流

查询投影

投影提供了一种机制,可以通过指定在响应中仅返回实体的特定属性来降低查询所返回 OData 源中的数据量。有关更多信息,请参阅 OData:系统查询选项 ($select)。您可以使用 Select 子句(Visual Basic 中的 Select)向 LINQ 查询添加投影子句。返回的实体数据可以被投影到客户端上的实体类型或非实体类型。对非实体类型所做的更改无法保存到数据服务。例如,下列 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 数据服务客户端文档中的投影注意事项

有关包含客户端上的 CustomerAddress 类型定义的完整示例,请参阅如何投影数据服务查询结果(WCF 数据服务/Silverlight)

针对 DataServiceContext 类使用 AddObject(String, Object)UpdateObject(Object)DeleteObject(Object) 方法可手动跟踪 OData 客户端上的更改。这些方法使客户端能够跟踪已添加和已删除的实体,以及您对属性值或者对实体实例之间的关系进行的更改。

在生成代理类时,会为 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 网站上公布的 Northwind 示例数据服务是只读的,尝试保存更改将会返回错误。若要成功执行此代码示例,必须创建自己的 Northwind 示例数据服务。为此,请完成主题如何创建 Northwind 数据中的步骤。

用户停止在应用中导航时,应用通常进入休眠状态。在休眠状态下,应用会保留在内存中,因此,当用户返回到应用时,应用几乎可以立即恢复。这种快速应用切换是自动启用的。但是,在应用处于休眠状态时,可能会终止应用。您一定要对您的应用进行设计,以便它能够处理这些出现在整个应用生命周期中的状态变更。有关详细信息,请参见 Windows Phone 8 的应用激活和停用。为了确保当发生终止情况时应用状态不会丢失,可以实现在应用生命周期内发生的事件的处理程序。在这些处理程序中,当应用在活动和非活动状态间转换时,可以保存和还原 DataServiceContext 类的状态,以及任何 DataServiceCollection 实例。当序列化这两个对象时,还会序列化所有跟踪或相关实体对象,包括尚未发送到数据服务的任何媒体资源流。当集合本身还原时,也会还原嵌套集合间的关系。此行为给用户带来的体验是:即使已经终止执行,应用仍继续运行。

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。在激活或导航至页面时,针对 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>;
}

提示提示:

如果尝试直接在状态字典中存储所跟踪的实体对象,则会导致在序列化过程中出错。如果您需要保持各个所跟踪对象的状态,请改为存储实体的 URI。通过调用 TryGetUri(Object, Uri) 方法,可以获取实体对象的 URI。随后,您便可以将存储的 URI 传递到 TryGetEntity``1(Uri, UMP) 方法,以便从还原的 DataServiceContext 中检索对象。

有关更多信息,请参阅如何保持 Windows Phone 8 OData 客户端的状态

当您为应用实现 MVVM 设计模式时,我们建议您序列化和反序列化视图模型自身中的数据服务状态。有关更多信息,请参阅演练:为 Windows Phone 8 将 MVVM 与 OData 结合使用

当发出请求时,可以使用 Windows Phone 的 OData 客户端库指定提供给数据服务的凭据。要做到这一点,您可以将实现 ICredentials 接口(如 NetworkCredential)的对象分配给 DataServiceContextCredentials() 属性。

安全说明安全说明:

用户凭据应仅在执行过程中请求,并且不应进行缓存。凭据必须妥善保存。

下面的代码行使用此 DataServiceContext 实例设置对数据服务所作请求的凭据。

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

凭据属性支持基于密码的身份验证模式(如基本、简要和 Windows 身份验证)。

安全说明安全说明:

使用基本和简要身份验证发送的数据不进行加密,因此对手可以查看到数据。此外,将会以明文形式发送基本身份验证凭据(用户名和密码),因此可能会被截获。

您还可以使用其他基于声明的身份验证机制(如 OAuth 2.0)对 Windows Phone 应用进行身份验证。当使用此类基于声明的身份验证方案时,必须手动设置特定声明提供程序要求的标头。您必须处理 DataServiceContext 引发的 SendingRequest() 事件,才能访问和修改请求标头。连接到 OAuth 2.0 保护的 OData 服务博客演示了如何针对使用 Windows Azure AppFabric 中的访问控制服务 (ACS) 进行身份验证的 Windows Phone 应用执行此操作。有关 OData 服务中身份验证的常规讨论的更多信息,请参见确保 WCF 数据服务的安全

Windows Phone 的 OData 客户端库使用 Atom 格式的 HTTP 消息与 OData 服务通信。因为 Atom 是一个基于 XML 的格式,这些消息所需的带宽多于使用 JavaScript 对象表示法 (JSON) 格式(OData 支持的其他格式)的相同消息。虽然 Windows Phone 的 OData 客户端库不支持 JSON 格式,但可以使用 HTTP 压缩方案(如 gzip 压缩)来降低网络带宽的要求。将 Accept-Encoding HTTP 消息标头设置为请求数据服务压缩响应,或设置为指示压缩 POST 请求。将此标头的值设置为请求压缩方案的名称。

重要说明重要说明:

消息压缩会减少数据服务和 Windows Phone 手机间通信的网络使用。但是,它还会使用数据服务和手机上的其他处理。此其他处理可能影响通信的整体性能,并影响手机的电池使用时间。

客户端提供了以下事件,在进行处理时,可以使用这些事件压缩对 OData 服务的请求,以及解压缩支持压缩的 OData 服务中的响应。

ReadingResponse()

在从数据服务中接收了响应后但在读取消息之前发生此事件。处理此事件以确定是否压缩了响应,并在需要时解压缩响应。

WritingRequest()

在生成 HTTP 请求前发生此事件。处理此事件以将 Accept-Encoding 标头设置为请求压缩响应或压缩消息正文。

在处理这些事件时,可以从提供的 ReadingWritingHttpMessageEventArgs 中访问消息标头和消息正文。消息标头从 Headers() 属性返回的字符串值字典中访问。消息正文从 Content() 属性中作为流访问。有关更多信息,请参见 OData 压缩上的博客。

重要说明重要说明:

Windows Phone SDK 不包含对压缩的支持。若要实现压缩,必须使用支持 Windows Phone 的第三方压缩库。还必须在托管 OData 服务的 Web 服务器上启用请求的压缩方案。

显示:
© 2015 Microsoft