服务操作(WCF 数据服务)
使用 WCF 数据服务 可以在数据服务中定义服务操作,以便在服务器上公开方法。 与其他数据服务资源一样,服务操作也是通过 URI 进行寻址。 使用服务操作可以在数据服务中公开业务逻辑;例如,实现验证逻辑,应用基于角色的安全性或公开专用查询功能。 服务操作是添加到数据服务类的方法,派生自 DataService<T>。像所有其他数据服务资源,您可以提供服务操作方法的参数。例如,以下服务操作的 URI(基于快速入门数据服务)将值 London 传递至 city 参数:
https://localhost:12345/Northwind.svc/GetOrdersByCity?city='London'
此服务操作的定义如下所示:
<WebGet()> _
Public Function GetOrdersByCity(ByVal city As String) As IQueryable(Of Order)
[WebGet]
public IQueryable<Order> GetOrdersByCity(string city)
使用 DataService<T> 的 CurrentDataSource 可以直接访问数据服务正在使用的数据源。 有关更多信息,请参见如何:定义服务操作(WCF 数据服务)。
有关如何调用从 .NET Framework 客户端应用程序调用服务操作的信息,请参阅调用服务操作和动作 (WCF Data Services)。
服务操作要求
在数据服务中定义服务操作需满足下列要求。 如果某一方法不符合这些要求,则不会将该方法公开为数据服务的服务操作。
操作必须是作为数据服务类成员的公共实例方法。
操作方法只能接受输入参数。 在邮件正文中发送的数据服务无法访问的数据。
如果定义了参数,则每个参数的类型必须为基元类型。 任何非基元类型的数据,必须序列化,并传递到一个字符串参数。
方法必须返回下列值之一:
void(在 Visual Basic 中为 Nothing)
数据服务所公开的数据模型中的实体类型。
整数或字符串等基元类。
若要支持查询选项(如排序、分页和筛选),服务操作方法应返回 IQueryable<T>。 只返回 IEnumerable<T> 的操作拒绝对包含查询选项的服务操作的请求。
若要支持使用导航属性访问相关实体,服务操作必须返回 IQueryable<T>。
必须用 [WebGet] 或 [WebInvoke] 特性为方法添加批注。
[WebGet] 使您能够通过使用 GET 请求调用方法。
[WebInvoke(Method = "POST")] 使您能够通过使用 POST 请求调用方法。 其他 WebInvokeAttribute 方法不受支持。
可以用 SingleResultAttribute 为服务操作添加批注,其指定方法的返回值是一个实体而不是一个实体集。 这一区别确定了生成的响应结果串行化以及用 URI 表示其他导航属性遍历的方式。 例如,当使用 AtomPub 序列化时,单个资源类型实例将表示为一个 entry 元素,而单个实例集将表示为一个 feed 元素。
对服务操作进行寻址
通过将方法名称放在 URI 的第一个路径段中,可以对服务操作进行寻址。 例如,下面的 URI 访问GetOrdersByState运算,其返回 IQueryable<T> 集合(属于 Orders 对象)。
https://localhost:12345/Northwind.svc/GetOrdersByState?state='CA'&includeItems=true
当调用服务操作时,参数作为查询选项提供。 以前的服务操作接受字符串参数 state 和布尔值参数 includeItems,它指示是否包括有关 Order_Detail 对象在响应中。
以下是有效的服务操作的返回类型:
有效的返回类型 |
URI 规则 |
---|---|
void(在 Visual Basic 中为 Nothing) -或- 实体类型 - 或 - 基元类型 |
URI 必须是作为服务操作名称的单个路径段。 不允许使用查询选项。 |
URI 必须是作为服务操作名称的单个路径段。 由于结果类型不是 IQueryable<T> 类型,因此不允许使用查询选项。 |
|
除了作为服务操作名称的路径之外,还允许使用查询路径段。 也允许使用查询选项。 |
可以将其他路径段或查询选项添加到该 URI,具体取决于服务操作的返回类型。 例如,以下 URI 访问 GetOrdersByCity 操作,该操作返回 Orders 对象的 IQueryable<T> 集合(按 RequiredDate 的降序顺序排序)以及相关的 Order_Details 对象:
https://localhost:12345/Northwind.svc/GetOrdersByCity?city='London'&$expand=Order_Details&$orderby=RequiredDate desc
服务操作访问控制
服务操作的服务范围可见性由 IDataServiceConfiguration 类的 SetServiceOperationAccessRule 方法控制,其方式与使用 SetEntitySetAccessRule 方法控制实体集可见性的方式大致相同。 例如,数据服务定义中的以下代码行用于访问 CustomersByCity 服务操作。
config.SetServiceOperationAccessRule( _
"GetOrdersByCity", ServiceOperationRights.AllRead)
config.SetServiceOperationAccessRule(
"GetOrdersByCity", ServiceOperationRights.AllRead);
备注
如果服务操作的返回类型已通过限制访问基础实体集的方式被隐藏,则该服务操作将不能用于客户端应用程序。
有关更多信息,请参见如何:定义服务操作(WCF 数据服务)。
提出异常
我们建议您使用 DataServiceException 类提出数据服务执行中出现的异常。 这是因为数据服务运行时知道如何正确地将此异常对象的属性映射到 HTTP 响应消息。 当你提出 DataServiceException 在服务操作中,返回的异常会包装在 TargetInvocationException 中。 若要返回基 DataServiceException 而不包含 TargetInvocationException,您必须重写 HandleException(HandleExceptionArgs)法在 DataService<T> 中,提取 DataServiceException 自 TargetInvocationException,并将其作为顶级的错误返回,如以下示例中所示:
' Override to manage returned exceptions.
Protected Overrides Sub HandleException(args As HandleExceptionArgs)
' Handle exceptions raised in service operations.
If args.Exception.GetType() = GetType(TargetInvocationException) _
AndAlso args.Exception.InnerException IsNot Nothing Then
If args.Exception.InnerException.GetType() = GetType(DataServiceException) Then
' Unpack the DataServiceException.
args.Exception = _
TryCast(args.Exception.InnerException, DataServiceException)
Else
' Return a new DataServiceException as "400: bad request."
args.Exception = _
New DataServiceException(400, args.Exception.InnerException.Message)
End If
End If
End Sub
// Override to manage returned exceptions.
protected override void HandleException(HandleExceptionArgs args)
{
// Handle exceptions raised in service operations.
if (args.Exception.GetType() ==
typeof(TargetInvocationException)
&& args.Exception.InnerException != null)
{
if (args.Exception.InnerException.GetType()
== typeof(DataServiceException))
{
// Unpack the DataServiceException.
args.Exception = args.Exception.InnerException as DataServiceException;
}
else
{
// Return a new DataServiceException as "400: bad request."
args.Exception =
new DataServiceException(400,
args.Exception.InnerException.Message);
}
}
}