Wywoływanie usługi protokołu OData z klienta .NET (C#)
Autor: Mike Wasson
Pobieranie ukończonego projektu
W tym samouczku pokazano, jak wywołać usługę OData z poziomu aplikacji klienckiej języka C#.
Wersje oprogramowania używane w samouczku
- Visual Studio 2013 (działa z programem Visual Studio 2012)
- Biblioteka klienta usług danych WCF
- Internetowy interfejs API 2. (Przykładowa usługa OData jest tworzona przy użyciu internetowego interfejsu API 2, ale aplikacja kliencka nie zależy od internetowego interfejsu API).
W tym samouczku omówimy tworzenie aplikacji klienckiej, która wywołuje usługę OData. Usługa OData uwidacznia następujące jednostki:
Product
Supplier
ProductRating
W poniższych artykułach opisano sposób implementowania usługi OData w internetowym interfejsie API. (Nie musisz ich jednak czytać, aby zrozumieć ten samouczek).
- Tworzenie punktu końcowego OData w internetowym interfejsie API 2
- Relacje jednostek OData w interfejsie Web API 2
- Akcje protokołu OData we wzorcu Web API 2
Generowanie serwera proxy usługi
Pierwszym krokiem jest wygenerowanie serwera proxy usługi. Serwer proxy usługi to klasa platformy .NET, która definiuje metody uzyskiwania dostępu do usługi OData. Serwer proxy tłumaczy wywołania metody na żądania HTTP.
Rozpocznij od otwarcia projektu usługi OData w programie Visual Studio. Naciśnij klawisze CTRL+F5, aby uruchomić usługę lokalnie w IIS Express. Zanotuj adres lokalny, w tym numer portu przypisany przez program Visual Studio. Ten adres będzie potrzebny podczas tworzenia serwera proxy.
Następnie otwórz kolejne wystąpienie programu Visual Studio i utwórz projekt aplikacji konsolowej. Aplikacja konsolowa będzie aplikacją kliencą OData. (Możesz również dodać projekt do tego samego rozwiązania co usługa).
Uwaga
Pozostałe kroki odwołują się do projektu konsoli.
W Eksplorator rozwiązań kliknij prawym przyciskiem myszy pozycję Odwołania i wybierz polecenie Dodaj odwołanie do usługi.
W oknie dialogowym Dodawanie odwołania do usługi wpisz adres usługi OData:
http://localhost:port/odata
gdzie port to numer portu.
W polu Przestrzeń nazw wpisz "ProductService". Ta opcja definiuje przestrzeń nazw klasy proxy.
Kliknij pozycję Przejdź. Program Visual Studio odczytuje dokument metadanych OData, aby odnaleźć jednostki w usłudze.
Kliknij przycisk OK , aby dodać klasę serwera proxy do projektu.
Tworzenie wystąpienia klasy serwera proxy usługi
Main
Wewnątrz metody utwórz nowe wystąpienie klasy serwera proxy w następujący sposób:
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);
// ...
}
}
}
Ponownie użyj rzeczywistego numeru portu, w którym jest uruchomiona usługa. Podczas wdrażania usługi użyjesz identyfikatora URI usługi na żywo. Nie musisz aktualizować serwera proxy.
Poniższy kod dodaje procedurę obsługi zdarzeń, która wyświetla identyfikatory URI żądań w oknie konsoli. Ten krok nie jest wymagany, ale warto zobaczyć identyfikatory URI dla każdego zapytania.
container.SendingRequest2 += (s, e) =>
{
Console.WriteLine("{0} {1}", e.RequestMessage.Method, e.RequestMessage.Url);
};
Wykonywanie zapytań względem usługi
Poniższy kod pobiera listę produktów z usługi 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);
}
}
Zwróć uwagę, że nie musisz pisać żadnego kodu, aby wysłać żądanie HTTP lub przeanalizować odpowiedź. Klasa serwera proxy wykonuje to automatycznie podczas wyliczania Container.Products
kolekcji w pętli foreach .
Po uruchomieniu aplikacji dane wyjściowe powinny wyglądać następująco:
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
Aby uzyskać jednostkę według identyfikatora, użyj klauzuli where
.
// 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);
}
}
W pozostałej części tego tematu nie pokażę całej Main
funkcji — tylko kod potrzebny do wywołania usługi.
Zastosuj opcje zapytania
OData definiuje opcje zapytania , które mogą służyć do filtrowania, sortowania, danych strony itd. Na serwerze proxy usługi można zastosować te opcje przy użyciu różnych wyrażeń LINQ.
W tej sekcji pokażę krótkie przykłady. Aby uzyskać więcej informacji, zobacz temat LINQ Considerations (Usługi danych programu WCF) w witrynie MSDN.
Filtrowanie ($filter)
Aby filtrować, użyj klauzuli where
. Poniższy przykład filtruje według kategorii produktów.
// 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);
}
}
Ten kod odpowiada następującemu zapytaniu OData.
GET http://localhost/odata/Products()?$filter=Category eq 'apparel'
Zwróć uwagę, że serwer proxy konwertuje klauzulę where
na wyrażenie OData $filter
.
Sortowanie ($orderby)
Aby posortować, użyj klauzuli orderby
. Poniższy przykład sortuje według ceny od najwyższej do najniższej.
// 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);
}
}
Oto odpowiednie żądanie OData.
GET http://localhost/odata/Products()?$orderby=Price desc
Client-Side stronicowanie ($skip i $top)
W przypadku dużych zestawów jednostek klient może chcieć ograniczyć liczbę wyników. Na przykład klient może wyświetlać 10 wpisów jednocześnie. Jest to nazywane stronicowaniem po stronie klienta. (Istnieje również stronicowanie po stronie serwera, gdzie serwer ogranicza liczbę wyników). Aby wykonać stronicowanie po stronie klienta, użyj metod POmiń i Take LINQ. Poniższy przykład pomija pierwsze 40 wyników i przyjmuje kolejne 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);
}
}
Oto odpowiednie żądanie OData:
GET http://localhost/odata/Products()?$orderby=Price desc&$skip=40&$top=10
Wybierz ($select) i rozwiń ($expand)
Aby uwzględnić powiązane jednostki, użyj DataServiceQuery<t>.Expand
metody . Aby na przykład uwzględnić element dla każdego Product
elementu Supplier
:
// 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);
}
}
Oto odpowiednie żądanie OData:
GET http://localhost/odata/Products()?$expand=Supplier
Aby zmienić kształt odpowiedzi, użyj klauzuli LINQ select . Poniższy przykład pobiera tylko nazwę każdego produktu bez innych właściwości.
// 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);
}
}
Oto odpowiednie żądanie OData:
GET http://localhost/odata/Products()?$select=Name
Klauzula select może zawierać powiązane jednostki. W takim przypadku nie należy wywoływać funkcji Expand; serwer proxy automatycznie uwzględnia rozszerzenie w tym przypadku. Poniższy przykład pobiera nazwę i dostawcę każdego produktu.
// 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);
}
}
Oto odpowiednie żądanie OData. Zwróć uwagę, że zawiera ona opcję $expand .
GET http://localhost/odata/Products()?$expand=Supplier&$select=Name,Supplier/Name
Aby uzyskać więcej informacji na temat $select i $expand, zobacz Using $select, $expand and $value in Web API 2 (Używanie $select, $expand i $value w internetowym interfejsie API 2).
Dodawanie nowej jednostki
Aby dodać nową jednostkę do zestawu jednostek, wywołaj metodę AddToEntitySet
, gdzie EntitySet jest nazwą zestawu jednostek. Na przykład AddToProducts
dodaje nowy Product
element do Products
zestawu jednostek. Podczas generowania serwera proxy Usługi danych programu WCF automatycznie tworzy te silnie typizowane metody AddTo.
// 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);
}
}
Aby dodać łącze między dwiema jednostkami, użyj metod AddLink i SetLink . Poniższy kod dodaje nowego dostawcę i nowego produktu, a następnie tworzy łącza między nimi.
// 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);
}
}
Użyj polecenia AddLink , gdy właściwość nawigacji jest kolekcją. W tym przykładzie dodajemy produkt do Products
kolekcji dostawcy.
Użyj polecenia SetLink , gdy właściwość nawigacji jest pojedynczą jednostką. W tym przykładzie ustawiamy Supplier
właściwość produktu.
Aktualizacja/poprawka
Aby zaktualizować jednostkę, wywołaj metodę 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);
}
}
Aktualizacja jest wykonywana po wywołaniu metody SaveChanges. Domyślnie program WCF wysyła żądanie SCALANIA HTTP. Opcja PatchOnUpdate informuje WCF, aby zamiast tego wysłać poprawkę HTTP.
Uwaga
Dlaczego FUNKCJA PATCH a SCALANIE? Oryginalna specyfikacja HTTP 1.1 (RCF 2616) nie zdefiniowała żadnej metody HTTP z semantykami "częściowej aktualizacji". Aby obsługiwać aktualizacje częściowe, specyfikacja OData zdefiniowała metodę MERGE. W 2010 r. RFC 5789 zdefiniował metodę PATCH dla częściowych aktualizacji. Niektóre z historii można przeczytać w tym wpisie w blogu na blogu Usługi danych programu WCF Blog. Obecnie funkcja PATCH jest preferowana w przypadku scalania. Kontroler OData utworzony przez szkielet internetowego interfejsu API obsługuje obie metody.
Jeśli chcesz zastąpić całą jednostkę (semantyka PUT), określ opcję ReplaceOnUpdate . Powoduje to wysłanie żądania HTTP PUT w programie WCF.
container.SaveChanges(SaveChangesOptions.ReplaceOnUpdate);
Usuwanie jednostki
Aby usunąć jednostkę, wywołaj metodę 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();
}
}
Wywoływanie akcji OData
W usłudze OData akcje są sposobem dodawania zachowań po stronie serwera, które nie są łatwo zdefiniowane jako operacje CRUD na jednostkach.
Chociaż dokument metadanych OData opisuje akcje, klasa serwera proxy nie tworzy dla nich żadnych silnie typiicznych metod. Nadal można wywołać akcję OData przy użyciu ogólnej metody Execute . Należy jednak znać typy danych parametrów i wartość zwracaną.
Na przykład akcja RateProduct
przyjmuje parametr o nazwie "Ocena" typu Int32
i zwraca wartość double
. Poniższy kod pokazuje, jak wywołać tę akcję.
int rating = 2;
Uri actionUri = new Uri(uri, "Products(5)/RateProduct");
var averageRating = container.Execute<double>(
actionUri, "POST", true, new BodyOperationParameter("Rating", rating)).First();
Aby uzyskać więcej informacji, zobaczWywoływanie operacji i akcji usługi.
Jedną z opcji jest rozszerzenie klasy Container w celu zapewnienia silnie typizowanej metody, która wywołuje akcję:
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();
}
}
}