Chiamata di un servizio OData da un client .NET (C#)
di Mike Wasson
Scaricare il progetto completato
Questa esercitazione illustra come chiamare un servizio OData da un'applicazione client C#.
Versioni software usate nell'esercitazione
- Visual Studio 2013 (funziona con Visual Studio 2012)
- Libreria client WCF Data Services
- API Web 2. Il servizio OData di esempio viene compilato usando l'API Web 2, ma l'applicazione client non dipende dall'API Web.
In questa esercitazione verrà illustrata la creazione di un'applicazione client che chiama un servizio OData. Il servizio OData espone le entità seguenti:
Product
Supplier
ProductRating
Gli articoli seguenti descrivono come implementare il servizio OData nell'API Web. Non è tuttavia necessario leggerli per comprendere questa esercitazione.
- Creazione di un endpoint OData nell'API Web 2
- Relazioni entità OData nell'API Web 2
- Azioni OData nell'API Web 2
Generare il proxy del servizio
Il primo passaggio consiste nel generare un proxy di servizio. Il proxy del servizio è una classe .NET che definisce i metodi per l'accesso al servizio OData. Il proxy converte le chiamate al metodo in richieste HTTP.
Iniziare aprendo il progetto di servizio OData in Visual Studio. Premere CTRL+F5 per eseguire il servizio in locale in IIS Express. Si noti l'indirizzo locale, incluso il numero di porta assegnato da Visual Studio. È necessario questo indirizzo quando si crea il proxy.
Aprire quindi un'altra istanza di Visual Studio e creare un progetto di applicazione console. L'applicazione console sarà l'applicazione client OData. È anche possibile aggiungere il progetto alla stessa soluzione del servizio.
Nota
I passaggi rimanenti fanno riferimento al progetto console.
In Esplora soluzioni fare clic con il pulsante destro del mouse su Riferimenti e scegliere Aggiungi riferimento al servizio.
Nella finestra di dialogo Aggiungi riferimento al servizio digitare l'indirizzo del servizio OData:
http://localhost:port/odata
dove la porta è il numero di porta.
Per Spazio dei nomi digitare "ProductService". Questa opzione definisce lo spazio dei nomi della classe proxy.
Fare clic su Vai. Visual Studio legge il documento dei metadati OData per individuare le entità nel servizio.
Fare clic su OK per aggiungere la classe proxy al progetto.
Creare un'istanza della classe proxy del servizio
Main
All'interno del metodo creare una nuova istanza della classe proxy, come indicato di seguito:
using System;
using System.Data.Services.Client;
using System.Linq;
namespace Client
{
class Program
{
static void Main(string[] args)
{
Uri uri = new Uri("http://localhost:1234/odata/");
var container = new ProductService.Container(uri);
// ...
}
}
}
Usare di nuovo il numero di porta effettivo in cui è in esecuzione il servizio. Quando si distribuisce il servizio, si userà l'URI del servizio live. Non è necessario aggiornare il proxy.
Il codice seguente aggiunge un gestore eventi che stampa gli URI della richiesta nella finestra della console. Questo passaggio non è obbligatorio, ma è interessante vedere gli URI per ogni query.
container.SendingRequest2 += (s, e) =>
{
Console.WriteLine("{0} {1}", e.RequestMessage.Method, e.RequestMessage.Url);
};
Eseguire query sul servizio
Il codice seguente ottiene l'elenco di prodotti dal servizio OData.
class Program
{
static void DisplayProduct(ProductService.Product product)
{
Console.WriteLine("{0} {1} {2}", product.Name, product.Price, product.Category);
}
// Get an entire entity set.
static void ListAllProducts(ProductService.Container container)
{
foreach (var p in container.Products)
{
DisplayProduct(p);
}
}
static void Main(string[] args)
{
Uri uri = new Uri("http://localhost:18285/odata/");
var container = new ProductService.Container(uri);
container.SendingRequest2 += (s, e) =>
{
Console.WriteLine("{0} {1}", e.RequestMessage.Method, e.RequestMessage.Url);
};
// Get the list of products
ListAllProducts(container);
}
}
Si noti che non è necessario scrivere codice per inviare la richiesta HTTP o analizzare la risposta. La classe proxy esegue questa operazione automaticamente quando si enumera la Container.Products
raccolta nel ciclo foreach .
Quando si esegue l'applicazione, l'output dovrebbe essere simile al seguente:
GET http://localhost:60868/odata/Products
Hat 15.00 Apparel
Scarf 12.00 Apparel
Socks 5.00 Apparel
Yo-yo 4.95 Toys
Puzzle 8.00 Toys
Per ottenere un'entità in base all'ID, usare una where
clausola.
// Get a single entity.
static void ListProductById(ProductService.Container container, int id)
{
var product = container.Products.Where(p => p.ID == id).SingleOrDefault();
if (product != null)
{
DisplayProduct(product);
}
}
Per il resto di questo argomento, non visualizzerò l'intera Main
funzione, solo il codice necessario per chiamare il servizio.
Applica opzioni di query
OData definisce le opzioni di query che possono essere usate per filtrare, ordinare, visualizzare dati di pagina e così via. Nel proxy del servizio è possibile applicare queste opzioni usando varie espressioni LINQ.
In questa sezione verranno illustrati brevi esempi. Per altre informazioni, vedere l'argomento CONSIDERAZIONI LINQ (WCF Data Services) in MSDN.
Filtro ($filter)
Per filtrare, usare una where
clausola. Nell'esempio seguente viene filtrato in base alla categoria di prodotti.
// Use the $filter option.
static void ListProductsInCategory(ProductService.Container container, string category)
{
var products =
from p in container.Products
where p.Category == category
select p;
foreach (var p in products)
{
DisplayProduct(p);
}
}
Questo codice corrisponde alla query OData seguente.
GET http://localhost/odata/Products()?$filter=Category eq 'apparel'
Si noti che il proxy converte la clausola in un'espressione where
OData $filter
.
Ordinamento ($orderby)
Per ordinare, usare una orderby
clausola. L'esempio seguente ordina per prezzo, dal più alto al più basso.
// Use the $orderby option
static void ListProductsSorted(ProductService.Container container)
{
// Sort by price, highest to lowest.
var products =
from p in container.Products
orderby p.Price descending
select p;
foreach (var p in products)
{
DisplayProduct(p);
}
}
Ecco la richiesta OData corrispondente.
GET http://localhost/odata/Products()?$orderby=Price desc
paging Client-Side ($skip e $top)
Per set di entità di grandi dimensioni, il client potrebbe voler limitare il numero di risultati. Ad esempio, un client potrebbe visualizzare 10 voci alla volta. Viene chiamato paging lato client. È disponibile anche il paging lato server, in cui il server limita il numero di risultati. Per eseguire il paging lato client, usare i metodi LINQ Skip e Take . L'esempio seguente ignora i primi 40 risultati e accetta il successivo 10.
// Use $skip and $top options.
static void ListProductsPaged(ProductService.Container container)
{
var products =
(from p in container.Products
orderby p.Price descending
select p).Skip(40).Take(10);
foreach (var p in products)
{
DisplayProduct(p);
}
}
Ecco la richiesta OData corrispondente:
GET http://localhost/odata/Products()?$orderby=Price desc&$skip=40&$top=10
Selezionare ($select) e Espandi ($expand)
Per includere entità correlate, usare il DataServiceQuery<t>.Expand
metodo . Ad esempio, per includere l'oggetto Supplier
per ogni Product
oggetto :
// Use the $expand option.
static void ListProductsAndSupplier(ProductService.Container container)
{
var products = container.Products.Expand(p => p.Supplier);
foreach (var p in products)
{
Console.WriteLine("{0}\t{1}\t{2}", p.Name, p.Price, p.Supplier.Name);
}
}
Ecco la richiesta OData corrispondente:
GET http://localhost/odata/Products()?$expand=Supplier
Per modificare la forma della risposta, usare la clausola di selezione LINQ. L'esempio seguente ottiene solo il nome di ogni prodotto, senza altre proprietà.
// Use the $select option.
static void ListProductNames(ProductService.Container container)
{
var products = from p in container.Products select new { Name = p.Name };
foreach (var p in products)
{
Console.WriteLine(p.Name);
}
}
Ecco la richiesta OData corrispondente:
GET http://localhost/odata/Products()?$select=Name
Una clausola select può includere entità correlate. In questo caso, non chiamare Espandi; il proxy include automaticamente l'espansione in questo caso. L'esempio seguente ottiene il nome e il fornitore di ogni prodotto.
// Use $expand and $select options
static void ListProductNameSupplier(ProductService.Container container)
{
var products =
from p in container.Products
select new
{
Name = p.Name,
Supplier = p.Supplier.Name
};
foreach (var p in products)
{
Console.WriteLine("{0}\t{1}", p.Name, p.Supplier);
}
}
Ecco la richiesta OData corrispondente. Si noti che include l'opzione $expand .
GET http://localhost/odata/Products()?$expand=Supplier&$select=Name,Supplier/Name
Per altre informazioni su $select e $expand, vedere Uso di $select, $expand e $value nell'API Web 2.
Aggiungere una nuova entità
Per aggiungere una nuova entità a un set di entità, chiamare AddToEntitySet
, dove EntitySet è il nome del set di entità. Ad esempio, AddToProducts
aggiunge un nuovo Product
al set di Products
entità. Quando si genera il proxy, WCF Data Services crea automaticamente questi metodi AddTo fortemente tipizzato.
// Add an entity.
static void AddProduct(ProductService.Container container, ProductService.Product product)
{
container.AddToProducts(product);
var serviceResponse = container.SaveChanges();
foreach (var operationResponse in serviceResponse)
{
Console.WriteLine(operationResponse.StatusCode);
}
}
Per aggiungere un collegamento tra due entità, usare i metodi AddLink e SetLink . Il codice seguente aggiunge un nuovo fornitore e un nuovo prodotto e quindi crea collegamenti tra di essi.
// Add entities with links.
static void AddProductWithSupplier(ProductService.Container container,
ProductService.Product product, ProductService.Supplier supplier)
{
container.AddToSuppliers(supplier);
container.AddToProducts(product);
container.AddLink(supplier, "Products", product);
container.SetLink(product, "Supplier", supplier);
var serviceResponse = container.SaveChanges();
foreach (var operationResponse in serviceResponse)
{
Console.WriteLine(operationResponse.StatusCode);
}
}
Utilizzare AddLink quando la proprietà di navigazione è un insieme. In questo esempio viene aggiunto un prodotto alla Products
raccolta sul fornitore.
Usare SetLink quando la proprietà di navigazione è una singola entità. In questo esempio viene impostata la Supplier
proprietà sul prodotto.
Aggiornamento/patch
Per aggiornare un'entità, chiamare il metodo UpdateObject .
static void UpdatePrice(ProductService.Container container, int id, decimal price)
{
var product = container.Products.Where(p => p.ID == id).SingleOrDefault();
if (product != null)
{
product.Price = price;
container.UpdateObject(product);
container.SaveChanges(SaveChangesOptions.PatchOnUpdate);
}
}
L'aggiornamento viene eseguito quando si chiama SaveChanges. Per impostazione predefinita, WCF invia una richiesta HTTP MERGE. L'opzione PatchOnUpdate indica a WCF di inviare invece una PATCH HTTP.
Nota
Perché PATCH e MERGE? La specifica HTTP 1.1 originale (RCF 2616) non ha definito alcun metodo HTTP con semantica di "aggiornamento parziale". Per supportare gli aggiornamenti parziali, la specifica OData ha definito il metodo MERGE. Nel 2010 RFC 5789 ha definito il metodo PATCH per gli aggiornamenti parziali. È possibile leggere alcune delle cronologie in questo post di blog sul blog di WCF Data Services. Attualmente PATCH è preferibile rispetto a MERGE. Il controller OData creato dallo scaffolding dell'API Web supporta entrambi i metodi.
Se si vuole sostituire l'intera entità (semantica PUT), specificare l'opzione ReplaceOnUpdate . In questo modo WCF invia una richiesta HTTP PUT.
container.SaveChanges(SaveChangesOptions.ReplaceOnUpdate);
Eliminare un'entità
Per eliminare un'entità, chiamare DeleteObject.
static void DeleteProduct(ProductService.Container container, int id)
{
var product = container.Products.Where(p => p.ID == id).SingleOrDefault();
if (product != null)
{
container.DeleteObject(product);
container.SaveChanges();
}
}
Richiamare un'azione OData
In OData le azioni sono un modo per aggiungere comportamenti lato server che non sono facilmente definiti come operazioni CRUD sulle entità.
Anche se il documento dei metadati OData descrive le azioni, la classe proxy non crea metodi fortemente tipizzato per tali azioni. È comunque possibile richiamare un'azione OData usando il metodo Execute generico. Tuttavia, sarà necessario conoscere i tipi di dati dei parametri e il valore restituito.
Ad esempio, l'azione accetta il RateProduct
parametro "Rating" di tipo Int32
e restituisce un oggetto double
. Nel codice seguente viene illustrato come richiamare questa azione.
int rating = 2;
Uri actionUri = new Uri(uri, "Products(5)/RateProduct");
var averageRating = container.Execute<double>(
actionUri, "POST", true, new BodyOperationParameter("Rating", rating)).First();
Per altre informazioni, vedereChiamata di operazioni e azioni del servizio.
Un'opzione consiste nell'estendere la classe Container per fornire un metodo fortemente tipizzato che richiama l'azione:
namespace ProductServiceClient.ProductService
{
public partial class Container
{
public double RateProduct(int productID, int rating)
{
Uri actionUri = new Uri(this.BaseUri,
String.Format("Products({0})/RateProduct", productID)
);
return this.Execute<double>(actionUri,
"POST", true, new BodyOperationParameter("Rating", rating)).First();
}
}
}