Chiamata di operazioni e azioni di servizio (WCF Data Services)
In OData (Open Data Protocol) vengono definite sia le operazioni che le azioni di servizio per un servizio dati. Come altre risorse del servizio dati, le operazioni e le azioni di servizio vengono indirizzate tramite URI. Le azioni e le operazioni di servizio possono restituire raccolte di tipi di entità, singole istanze del tipo di entità e tipi primitivi, ad esempio integer e string, nonché null (Nothing in Visual Basic). A differenza delle operazioni, le azioni di servizio possono essere associate alle risorse del modello dati e devono essere chiamate tramite una richiesta POST HTTP a causa degli effetti collaterali che hanno sul sistema. Per ulteriori informazioni, vedere Operazioni del servizio (WCF Data Services) e Utilizzo di azioni OData per implementare il comportamento lato server.
Sia le operazioni che le azioni di servizio sono esposte nei metadati restituiti da un servizio dati che implementa OData. Nei metadati vengono entrambe rappresentate come elementi FunctionImport. In caso di generazione di un oggetto DataServiceContext fortemente tipizzato, questo elemento viene ignorato dagli strumenti di aggiunta riferimento al servizio e DataSvcUtil.exe. Per questo motivo, nel contesto non sarà disponibile alcun metodo che consenta di chiamare direttamente un'operazione di servizio. Per chiamare operazioni di servizio, è tuttavia possibile procedere in uno dei due modi seguenti utilizzando il client WCF Data Services:
Chiamare il metodo Execute<TElement>(Uri) su DataServiceContext, specificando l'URI dell'operazione di servizio. Questo è il metodo consigliato per chiamare tutte le operazioni e le azioni di servizio. Per le azioni o le operazioni di servizio chiamate tramite una richiesta POST HTTP, chiamare l'overload del metodo Execute<TElement>(Uri, String, Boolean, array<OperationParameter[]) che accetta un parametro httpMethod e fornire il valore POST. È inoltre possibile fornire uno o più parametri durante l'esecuzione passando una raccolta di valori del parametro OperationParameter a operationParameters quando viene chiamato questo metodo. Quando si chiama un'azione di servizio, vengono forniti solo i parametri non di associazione in questo modo.
Utilizzare il metodo CreateQuery<T>(String) su DataServiceContext per creare un oggetto DataServiceQuery<TElement>. Quando si chiama CreateQuery<T>(String), il nome dell'operazione di servizio viene fornito al parametro entitySetName. Questo metodo restituisce un oggetto DataServiceQuery<TElement> che chiama l'operazione di servizio in caso di enumerazione o quando viene chiamato il metodo Execute(). Questo metodo viene utilizzato per chiamare operazioni di servizio GET che restituiscono una raccolta. Tramite il metodo AddQueryOption(String, Object) è possibile fornire un singolo parametro. L'oggetto DataServiceQuery<TElement> restituito da questo metodo può essere ulteriormente composto come qualsiasi oggetto query. Per ulteriori informazioni, vedere Esecuzione di query sul servizio dati (WCF Data Services). Questo metodo non può essere utilizzato per chiamare azioni di servizio.
Considerazioni relative alla chiamata di operazioni e azioni di servizio
Le considerazioni seguenti si applicano quando si utilizza il client WCF Data Services per chiamare operazioni di servizio.
Quando si accede al servizio dati in modo asincrono, è necessario utilizzare i metodi asincroni equivalenti BeginExecute<TElement>(Uri, AsyncCallback, Object)/EndExecute<TElement>(IAsyncResult) su DataServiceContext o i metodi BeginExecute(AsyncCallback, Object)/EndExecute(IAsyncResult) su DataServiceQuery<TElement>.
Si consiglia di creare un metodo di estensione nella classe parziale DataServiceContext fortemente tipizzata, generato tramite gli strumenti, che utilizzi il metodo CreateQuery<T>(String) o Execute<TElement>(Uri) per chiamare un'operazione di servizio. In questo modo è possibile chiamare operazioni di servizio direttamente dal contesto. Per ulteriori informazioni, vedere il post di blog Operazioni di servizio e client di WCF Data Services.
Quando si utilizza CreateQuery<T>(String) per chiamare un'operazione di servizio, nella libreria client vengono utilizzati automaticamente caratteri di escape forniti al metodo AddQueryOption(String, Object) eseguendo la codifica percentuale di caratteri riservati, ad esempio la e commerciale (&), ed eseguendo l'escape delle virgolette singole nelle stringhe. Quando tuttavia si utilizza uno dei metodi Execute per chiamare un'operazione di servizio, è necessario utilizzare caratteri di escape per qualsiasi valore stringa fornito dall'utente. Per le virgolette singole negli URI vengono utilizzati come caratteri di escape coppie di virgolette singole.
A differenza di un'operazione di servizio, le azioni di servizio non possono essere ulteriormente composte. Ciò significa che non è possibile eseguire operazioni di query aggiuntive sul lato del servizio dopo aver chiamato l'azione di servizio. Per ulteriori informazioni, vedere Utilizzo di azioni OData per implementare il comportamento lato server.
Esempi di chiamata di operazioni di servizio
In questa sezione sono contenuti gli esempi seguenti relativi alle modalità di chiamata delle operazioni di servizio tramite la libreria client WCF Data Services:
Chiamata di Execute<T> per restituire una raccolta di entità
Nell'esempio seguente viene chiamata un'operazione di servizio denominata GetOrdersByCity che accetta un parametro stringa city e restituisce un oggetto 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);
}
In questo esempio l'operazione di servizio restituisce una raccolta di oggetti Order con oggetti Order_Detail correlati.
Utilizzo di CreateQuery<T> per restituire una raccolta di entità
Nell'esempio seguente viene utilizzato CreateQuery<T>(String) per restituire un oggetto DataServiceQuery<TElement> utilizzato per chiamare la stessa operazione di servizio 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);
}
In questo esempio il metodo AddQueryOption(String, Object) viene utilizzato per aggiungere il parametro alla query, mentre il metodo Expand(String) viene utilizzato per includere oggetti Order_Details correlati nei risultati.
Chiamata di Execute<T> per restituire un'entità singola
Nell'esempio seguente viene chiamata un'operazione di servizio denominata GetNewestOrder che restituisce solo un'entità Order singola:
' 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);
}
In questo esempio il metodo FirstOrDefault<TSource>(IEnumerable<TSource>) viene utilizzato per richiedere solo un'entità Order singola durante l'esecuzione.
Chiamata di Execute<T> per restituire una raccolta di valori primitivi
Nell'esempio seguente viene chiamata un'operazione di servizio che restituisce una raccolta di valori stringa:
' 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);
}
Chiamata di Execute<T> per restituire un valore primitivo singolo
Nell'esempio seguente viene chiamata un'operazione di servizio che restituisce un valore stringa singolo:
' 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);
}
In questo esempio il metodo FirstOrDefault<TSource>(IEnumerable<TSource>) viene utilizzato per richiedere solo un valore Integer singolo durante l'esecuzione.
Chiamata di un'operazione di servizio che non restituisce dati
Nell'esempio seguente viene chiamata un'operazione di servizio che non restituisce dati:
' 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);
}
Poiché non vengono restituiti dati, il valore dell'esecuzione non viene assegnato. L'unica indicazione relativa all'esito positivo della richiesta è la mancata generazione di un'eccezione DataServiceQueryException.
Chiamata di un'operazione di servizio in modo asincrono
Nell'esempio seguente viene chiamata un'operazione di servizio in modo asincrono tramite BeginExecute<TElement>(Uri, AsyncCallback, Object) e 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);
}
}
Poiché non vengono restituiti dati, il valore restituito dell'esecuzione non viene assegnato. L'unica indicazione relativa all'esito positivo della richiesta è la mancata generazione di un'eccezione DataServiceQueryException.
Nell'esempio seguente viene chiamata la stessa operazione di servizio in modo asincrono tramite 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);
}
}