Llamar a operaciones y acciones de servicio (WCF Data Services)
El Open Data Protocol (OData) define tanto operaciones de servicio como acciones de servicio para un servicio de datos. Las operaciones de servicio y las acciones de servicio se direccionan como los demás recursos del servicio de datos, mediante los URI. Una operación de servicio y las acciones de servicio pueden devolver colecciones de tipos de entidad, instancias de tipo de entidad único, tipos primitivos como entero y cadena y null (Nothing en Visual Basic). A diferencia de las operaciones de servicio, las acciones de servicio pueden estar enlazadas a recursos del modelo de datos y se les debe llamar mediante una solicitud HTTP POST porque tienen efectos secundarios en el sistema. Para obtener más información, vea Operaciones de servicio (WCF Data Services) y Usar acciones OData para implementar el comportamiento del lado servidor.
Tanto las operaciones como las acciones de servicio se exponen en los metadatos devueltos por un servicio de datos que implementa OData. En los metadatos, ambas se representan como elementos FunctionImport. Al generar el DataServiceContext fuertemente tipado, las herramientas Agregar referencia de servicio y DataSvcUtil.exe omiten este elemento. Por esto, no encontrará un método en el contexto que se pueda usar para llamar directamente a una operación de servicio. Sin embargo, todavía puede usar el cliente de Servicios de datos de Microsoft WCF para llamar a operaciones de servicio de una de estas dos maneras:
Llamando al método Execute<TElement>(Uri) en el DataServiceContext, proporcionando el URI de la operación de servicio. Se recomienda usar este método para llamar a todas las operaciones de servicio y las acciones de servicio. Para las acciones de servicio o las operaciones de servicio a las que se llama mediante una solicitud HTTP POST, llame a la sobrecarga del método Execute<TElement>(Uri, String, Boolean, array<OperationParameter[]) que toma un parámetro httpMethod y proporcione un valor de POST. También puede proporcionar uno o varios parámetros durante la ejecución si pasa una colección OperationParameter de valores de parámetro a operationParameters cuando llame a este método. Al llamar a una acción de servicio, solo se proporcionan de esta forma parámetros sin enlace.
Usando el método CreateQuery<T>(String) en DataServiceContext para crear un objeto DataServiceQuery<TElement>. Al llamar a CreateQuery<T>(String), el nombre de la operación de servicio se proporciona al parámetro entitySetName. Este método devuelve un objeto DataServiceQuery<TElement> que llama a la operación de servicio cuando se enumera o cuando se llama al método Execute(). Este método se emplea para llamar a operaciones de servicio GET que devuelven una colección. Se puede proporcionar un parámetro único usando el método AddQueryOption(String, Object). El objeto DataServiceQuery<TElement> devuelto por este método se puede componer más como cualquier objeto de consulta. Para obtener más información, vea Consultar el servicio de datos (WCF Data Services). Este método no se puede usar para llamar a acciones de servicio.
Consideraciones para llamar a operaciones y acciones de servicio
Se aplican las siguientes consideraciones cuando se usa el cliente Servicios de datos de Microsoft WCF para llamar a operaciones de servicio.
Al tener acceso de forma asincrónica al servicio de datos, debe usar los métodos BeginExecute<TElement>(Uri, AsyncCallback, Object)/EndExecute<TElement>(IAsyncResult) asincrónicos equivalentes en DataServiceContext o los métodos BeginExecute(AsyncCallback, Object)/EndExecute(IAsyncResult) en DataServiceQuery<TElement>.
Considere la posibilidad de crear un método de extensión en la clase parcial DataServiceContext fuertemente tipada, generada por las herramientas, que use el método CreateQuery<T>(String) o Execute<TElement>(Uri) para llamar a una operación de servicio. Esto le permite llamar directamente a operaciones de servicio desde el contexto. Para obtener más información, vea la entrada de blog Operaciones de servicio y el cliente de WCF Data Services.
Cuando se usa CreateQuery<T>(String) para llamar a una operación de servicio, la biblioteca cliente establece automáticamente secuencias de escape para los caracteres proporcionados a AddQueryOption(String, Object) realizando una codificación porcentual de los caracteres reservados, como el carácter de Y comercial (&) y estableciendo secuencias de escape para las comillas simples en cadenas. Sin embargo, al llamar a uno de los métodos Execute para llamar a una operación de servicio, debe recordar hacerlo usando caracteres de escape para cualquier valor de cadena proporcionado por el usuario. Las comillas simples de los URI se pasan como pares de comillas simples.
A diferencia de las operaciones de servicio, las acciones de servicio no se pueden componer más. Esto significa que después de llamar a la acción de servicio no puede realizar ninguna operación de consulta del lado servicio adicional. Para obtener más información, vea Usar acciones OData para implementar el comportamiento del lado servidor.
Ejemplos de llamada a operaciones de servicio
Esta sección contiene los siguientes ejemplos de cómo llamar a operaciones de servicio mediante la biblioteca cliente de Servicios de datos de Microsoft WCF:
Llamar a Execute<T> para devolver una colección de entidades
En el ejemplo siguiente se llama una operación de servicio denominada GetOrdersByCity, que toma un parámetro de cadena de city y devuelve IQueryable<T>:
' Define the service operation query parameter.
Dim city As String = "London"
' Define the query URI to access the service operation with specific
' query options relative to the service URI.
Dim queryString As String = String.Format("GetOrdersByCity?city='{0}'", city) _
& "&$orderby=ShippedDate desc" _
& "&$expand=Order_Details"
' Create the DataServiceContext using the service URI.
Dim context As NorthwindEntities = New NorthwindEntities(svcUri2)
Try
' Execute the service operation that returns all orders for the specified city.
Dim results = context.Execute(Of Order)(New Uri(queryString, UriKind.Relative))
' Write out order information.
For Each o As Order In results
Console.WriteLine(String.Format("Order ID: {0}", o.OrderID))
For Each item As Order_Detail In o.Order_Details
Console.WriteLine(String.Format(vbTab & "Item: {0}, quantity: {1}", _
item.ProductID, item.Quantity))
Next
Next
Catch ex As DataServiceQueryException
Dim response As QueryOperationResponse = ex.Response
Console.WriteLine(response.Error.Message)
End Try
// Define the service operation query parameter.
string city = "London";
// Define the query URI to access the service operation with specific
// query options relative to the service URI.
string queryString = string.Format("GetOrdersByCity?city='{0}'", city)
+ "&$orderby=ShippedDate desc"
+ "&$expand=Order_Details";
// Create the DataServiceContext using the service URI.
NorthwindEntities context = new NorthwindEntities(svcUri2);
try
{
// Execute the service operation that returns all orders for the specified city.
var results = context.Execute<Order>(new Uri(queryString, UriKind.Relative));
// Write out order information.
foreach (Order o in results)
{
Console.WriteLine(string.Format("Order ID: {0}", o.OrderID));
foreach (Order_Detail item in o.Order_Details)
{
Console.WriteLine(String.Format("\tItem: {0}, quantity: {1}",
item.ProductID, item.Quantity));
}
}
}
catch (DataServiceQueryException ex)
{
QueryOperationResponse response = ex.Response;
Console.WriteLine(response.Error.Message);
}
En este ejemplo, la operación de servicio devuelve una colección de objetos Order con objetos Order_Detail relacionados.
Usar CreateQuery<T> para devolver una colección de entidades
En el ejemplo siguiente se usa CreateQuery<T>(String) para devolver un DataServiceQuery<TElement> que se emplea para llamar a la misma operación de servicio GetOrdersByCity:
' Define the service operation query parameter.
Dim city As String = "London"
' Create the DataServiceContext using the service URI.
Dim context As NorthwindEntities = New NorthwindEntities(svcUri2)
' Use the CreateQuery method to create a query that accessess
' the service operation passing a single parameter.
Dim query = context.CreateQuery(Of Order)("GetOrdersByCity") _
.AddQueryOption("city", String.Format("'{0}'", city)).Expand("Order_Details")
Try
' The query is executed during enumeration.
For Each o As Order In query
Console.WriteLine(String.Format("Order ID: {0}", o.OrderID))
For Each item As Order_Detail In o.Order_Details
Console.WriteLine(String.Format(vbTab & "Item: {0}, quantity: {1}", _
item.ProductID, item.Quantity))
Next
Next
Catch ex As DataServiceQueryException
Dim response As QueryOperationResponse = ex.Response
Console.WriteLine(response.Error.Message)
End Try
// Define the service operation query parameter.
string city = "London";
// Create the DataServiceContext using the service URI.
NorthwindEntities context = new NorthwindEntities(svcUri2);
// Use the CreateQuery method to create a query that accessess
// the service operation passing a single parameter.
var query = context.CreateQuery<Order>("GetOrdersByCity")
.AddQueryOption("city", string.Format("'{0}'", city))
.Expand("Order_Details");
try
{
// The query is executed during enumeration.
foreach (Order o in query)
{
Console.WriteLine(string.Format("Order ID: {0}", o.OrderID));
foreach (Order_Detail item in o.Order_Details)
{
Console.WriteLine(String.Format("\tItem: {0}, quantity: {1}",
item.ProductID, item.Quantity));
}
}
}
catch (DataServiceQueryException ex)
{
QueryOperationResponse response = ex.Response;
Console.WriteLine(response.Error.Message);
}
En este ejemplo, el método AddQueryOption(String, Object) se usa para agregar el parámetro a la consulta y el método Expand(String) se usa para incluir objetos Order_Details relacionados en los resultados.
Llamar a Execute<T> para devolver una sola entidad
En el ejemplo siguiente se llama a una operación de servicio denominada GetNewestOrder que solo devuelve una única entidad Order:
' Define the query URI to access the service operation,
' relative to the service URI.
Dim queryString As String = "GetNewestOrder"
' Create the DataServiceContext using the service URI.
Dim context As NorthwindEntities = New NorthwindEntities(svcUri2)
Try
' Execute a service operation that returns only the newest single order.
Dim o As Order = _
context.Execute(Of Order)( _
New Uri(queryString, UriKind.Relative)).FirstOrDefault()
' Write out order information.
Console.WriteLine(String.Format("Order ID: {0}", o.OrderID))
Console.WriteLine(String.Format("Order date: {0}", o.OrderDate))
Catch ex As DataServiceQueryException
Dim response As QueryOperationResponse = ex.Response
Console.WriteLine(response.Error.Message)
End Try
// Define the query URI to access the service operation,
// relative to the service URI.
string queryString = "GetNewestOrder";
// Create the DataServiceContext using the service URI.
NorthwindEntities context = new NorthwindEntities(svcUri2);
try
{
// Execute a service operation that returns only the newest single order.
Order order
= (context.Execute<Order>(new Uri(queryString, UriKind.Relative)))
.FirstOrDefault();
// Write out order information.
Console.WriteLine(string.Format("Order ID: {0}", order.OrderID));
Console.WriteLine(string.Format("Order date: {0}", order.OrderDate));
}
catch (DataServiceQueryException ex)
{
QueryOperationResponse response = ex.Response;
Console.WriteLine(response.Error.Message);
}
En este ejemplo, el método FirstOrDefault<TSource>(IEnumerable<TSource>) se usa para solicitar solo una única entidad Order en la ejecución.
Llamar a Execute<T> para devolver una colección de valores primitivos
En el ejemplo siguiente se llama a una operación de servicio que devuelve una colección de valores de cadena:
' Define the query URI to access the service operation,
' relative to the service URI.
Dim queryString As String = "GetCustomerNames"
'Create the DataServiceContext using the service URI.
Dim context As NorthwindEntities = New NorthwindEntities(svcUri2)
Try
' Execute a service operation that returns a collection of customer names.
Dim customerNames As IEnumerable(Of String) _
= context.Execute(Of String)(New Uri(queryString, UriKind.Relative))
For Each name As String In customerNames
' Write out customer information.
Console.WriteLine(name)
Next
Catch ex As DataServiceQueryException
Dim response As QueryOperationResponse = ex.Response
Console.WriteLine(response.Error.Message)
End Try
// Define the query URI to access the service operation,
// relative to the service URI.
string queryString = "GetCustomerNames";
// Create the DataServiceContext using the service URI.
NorthwindEntities context = new NorthwindEntities(svcUri2);
try
{
// Execute a service operation that returns a collection of customer names
IEnumerable<string> customerNames
= context.Execute<string>(new Uri(queryString, UriKind.Relative));
foreach (string name in customerNames)
{
// Write out customer information.
Console.WriteLine(name);
}
}
catch (DataServiceQueryException ex)
{
QueryOperationResponse response = ex.Response;
Console.WriteLine(response.Error.Message);
}
Llamar a Execute<T> para devolver un único valor primitivo
En el ejemplo siguiente se llama a una operación de servicio que devuelve un único valor de cadena:
' Define the query URI to access the service operation,
' relative to the service URI.
Dim queryString As String = "CountOpenOrders"
' Create the DataServiceContext using the service URI.
Dim context As NorthwindEntities = New NorthwindEntities(svcUri2)
Try
' Execute a service operation that returns the integer
' count of open orders.
Dim numOrders As Integer = context.Execute(Of Integer)( _
New Uri(queryString, UriKind.Relative)).FirstOrDefault()
' Write out the number of open orders.
Console.WriteLine(String.Format("Open orders as of {0}: {1}",
DateTime.Today.Date, numOrders))
Catch ex As DataServiceQueryException
Dim response As QueryOperationResponse = ex.Response
Console.WriteLine(response.Error.Message)
End Try
// Define the query URI to access the service operation,
// relative to the service URI.
string queryString = "CountOpenOrders";
// Create the DataServiceContext using the service URI.
NorthwindEntities context = new NorthwindEntities(svcUri2);
try
{
// Execute a service operation that returns the integer
// count of open orders.
int numOrders
= (context.Execute<int>(new Uri(queryString, UriKind.Relative)))
.FirstOrDefault();
// Write out the number of open orders.
Console.WriteLine(string.Format("Open orders as of {0}: {1}",
DateTime.Today.Date, numOrders));
}
catch (DataServiceQueryException ex)
{
QueryOperationResponse response = ex.Response;
Console.WriteLine(response.Error.Message);
}
De nuevo en este ejemplo, el método FirstOrDefault<TSource>(IEnumerable<TSource>) se emplea para solicitar solo un único valor entero en la ejecución.
Llamar una operación de servicio que no devuelve datos
En el ejemplo siguiente se llama a una operación de servicio que no devuelve ningún dato:
' Define the query URI to access the service operation,
' relative to the service URI.
Dim queryString As String = "ReturnsNoData"
' Create the DataServiceContext using the service URI.
Dim context As NorthwindEntities = New NorthwindEntities(svcUri2)
Try
' Execute a service operation that returns void.
context.Execute(Of String)( _
New Uri(queryString, UriKind.Relative))
Catch ex As DataServiceQueryException
Dim response As QueryOperationResponse = ex.Response
Console.WriteLine(response.Error.Message)
End Try
// Define the query URI to access the service operation,
// relative to the service URI.
string queryString = "ReturnsNoData";
// Create the DataServiceContext using the service URI.
NorthwindEntities context = new NorthwindEntities(svcUri2);
try
{
// Execute a service operation that returns void.
context.Execute<string>(new Uri(queryString, UriKind.Relative));
}
catch (DataServiceQueryException ex)
{
QueryOperationResponse response = ex.Response;
Console.WriteLine(response.Error.Message);
}
Como no se devuelven datos, no se asigna el valor de la ejecución. La única indicación de que la solicitud se ha realizado correctamente es que no se genera DataServiceQueryException.
Llamar una operación de servicio de forma asincrónica
En el ejemplo siguiente se llama de forma asincrónica a una operación de servicio llamando a BeginExecute<TElement>(Uri, AsyncCallback, Object) y EndExecute<TElement>(IAsyncResult):
' Define the service operation query parameter.
Dim city As String = "London"
' Define the query URI to access the service operation with specific
' query options relative to the service URI.
Dim queryString As String = String.Format("GetOrdersByCity?city='{0}'", city) _
& "&$orderby=ShippedDate desc" _
& "&$expand=Order_Details"
' Create the DataServiceContext using the service URI.
Dim context As NorthwindEntities = New NorthwindEntities(svcUri2)
' Define the delegate to callback into the process
Dim callback As AsyncCallback = AddressOf OnAsyncExecutionComplete
' Execute the service operation that returns
' all orders for the specified city.
Dim results = context.BeginExecute(Of Order)( _
New Uri(queryString, UriKind.Relative), _
callback, context)
// Define the service operation query parameter.
string city = "London";
// Define the query URI to access the service operation with specific
// query options relative to the service URI.
string queryString = string.Format("GetOrdersByCity?city='{0}'", city)
+ "&$orderby=ShippedDate desc"
+ "&$expand=Order_Details";
// Create the DataServiceContext using the service URI.
NorthwindEntities context = new NorthwindEntities(svcUri2);
// Execute the service operation that returns
// all orders for the specified city.
var results = context.BeginExecute<Order>(
new Uri(queryString, UriKind.Relative),
OnAsyncExecutionComplete, context);
Private Shared Sub OnAsyncExecutionComplete(ByVal result As IAsyncResult)
' Get the context back from the stored state.
Dim context = TryCast(result.AsyncState, NorthwindEntities)
Try
' Complete the exection and write out the results.
For Each o As Order In context.EndExecute(Of Order)(result)
Console.WriteLine(String.Format("Order ID: {0}", o.OrderID))
For Each item As Order_Detail In o.Order_Details
Console.WriteLine(String.Format(vbTab & "Item: {0}, quantity: {1}", _
item.ProductID, item.Quantity))
Next
Next
Catch ex As DataServiceQueryException
Dim response As QueryOperationResponse = ex.Response
Console.WriteLine(response.Error.Message)
End Try
End Sub
private static void OnAsyncExecutionComplete(IAsyncResult result)
{
// Get the context back from the stored state.
var context = result.AsyncState as NorthwindEntities;
try
{
// Complete the exection and write out the results.
foreach (Order o in context.EndExecute<Order>(result))
{
Console.WriteLine(string.Format("Order ID: {0}", o.OrderID));
foreach (Order_Detail item in o.Order_Details)
{
Console.WriteLine(String.Format("\tItem: {0}, quantity: {1}",
item.ProductID, item.Quantity));
}
}
}
catch (DataServiceQueryException ex)
{
QueryOperationResponse response = ex.Response;
Console.WriteLine(response.Error.Message);
}
}
Como no se devuelven datos, no se asigna el valor devuelto por la ejecución. La única indicación de que la solicitud se ha realizado correctamente es que no se genera DataServiceQueryException.
En el ejemplo siguiente se llama de forma asincrónica a la misma operación de servicio mediante CreateQuery<T>(String):
' Define the service operation query parameter.
Dim city As String = "London"
' Create the DataServiceContext using the service URI.
Dim context As NorthwindEntities = New NorthwindEntities(svcUri2)
' Use the CreateQuery method to create a query that accessess
' the service operation passing a single parameter.
Dim query = context.CreateQuery(Of Order)("GetOrdersByCity") _
.AddQueryOption("city", String.Format("'{0}'", city)) _
.Expand("Order_Details")
' Define the delegate to callback into the process
Dim callback As AsyncCallback = AddressOf OnAsyncQueryExecutionComplete
' Execute the service operation that returns
' all orders for the specified city.
Dim results = _
query.BeginExecute(callback, query)
// Define the service operation query parameter.
string city = "London";
// Create the DataServiceContext using the service URI.
NorthwindEntities context = new NorthwindEntities(svcUri2);
// Use the CreateQuery method to create a query that accessess
// the service operation passing a single parameter.
var query = context.CreateQuery<Order>("GetOrdersByCity")
.AddQueryOption("city", string.Format("'{0}'", city))
.Expand("Order_Details");
// Execute the service operation that returns
// all orders for the specified city.
var results =
query.BeginExecute(OnAsyncQueryExecutionComplete, query);
Private Shared Sub OnAsyncQueryExecutionComplete(ByVal result As IAsyncResult)
' Get the query back from the stored state.
Dim query = TryCast(result.AsyncState, DataServiceQuery(Of Order))
Try
' Complete the exection and write out the results.
For Each o As Order In query.EndExecute(result)
Console.WriteLine(String.Format("Order ID: {0}", o.OrderID))
For Each item As Order_Detail In o.Order_Details
Console.WriteLine(String.Format(vbTab & "Item: {0}, quantity: {1}", _
item.ProductID, item.Quantity))
Next
Next
Catch ex As DataServiceQueryException
Dim response As QueryOperationResponse = ex.Response
Console.WriteLine(response.Error.Message)
End Try
End Sub
private static void OnAsyncQueryExecutionComplete(IAsyncResult result)
{
// Get the query back from the stored state.
var query = result.AsyncState as DataServiceQuery<Order>;
try
{
// Complete the exection and write out the results.
foreach (Order o in query.EndExecute(result))
{
Console.WriteLine(string.Format("Order ID: {0}", o.OrderID));
foreach (Order_Detail item in o.Order_Details)
{
Console.WriteLine(String.Format("\tItem: {0}, quantity: {1}",
item.ProductID, item.Quantity));
}
}
}
catch (DataServiceQueryException ex)
{
QueryOperationResponse response = ex.Response;
Console.WriteLine(response.Error.Message);
}
}