Volání služby OData z klienta .NET (C#)

Mike Wasson

Stažení dokončeného projektu

Tento kurz ukazuje, jak volat službu OData z klientské aplikace jazyka C#.

Verze softwaru použité v kurzu

V tomto kurzu vás provedeme vytvořením klientské aplikace, která volá službu OData. Služba OData zveřejňuje následující entity:

  • Product
  • Supplier
  • ProductRating

Diagram znázorňující entity služby O Data a seznam jejich vlastností se spojovacími šipkami, které ukazují, jak spolu souvisí nebo spolupracují

Následující články popisují, jak implementovat službu OData ve webovém rozhraní API. (K pochopení tohoto kurzu je ale nemusíte číst.)

Vygenerování proxy služby

Prvním krokem je vygenerování proxy serveru služby. Proxy služby je třída .NET, která definuje metody pro přístup ke službě OData. Proxy překládá volání metod na požadavky HTTP.

Diagram znázorňující volání požadavku H T T P proxy služby spuštěná z aplikace, prostřednictvím proxy serveru služby a do služby O Data

Začněte otevřením projektu služby OData v sadě Visual Studio. Stisknutím kombinace kláves CTRL+F5 spusťte službu místně v IIS Express. Poznamenejte si místní adresu, včetně čísla portu, které sada Visual Studio přiřadí. Tuto adresu budete potřebovat při vytváření proxy serveru.

Dále otevřete další instanci sady Visual Studio a vytvořte projekt konzolové aplikace. Konzolová aplikace bude naše klientská aplikace OData. (Projekt můžete také přidat do stejného řešení jako služba.)

Poznámka

Zbývající kroky se týkají projektu konzoly.

V Průzkumník řešení klikněte pravým tlačítkem na Odkazy a vyberte Přidat odkaz na službu.

Snímek obrazovky s oknem průzkumníka řešení zobrazující nabídku v části Odkazy pro přidání nového odkazu na službu

V dialogovém okně Přidat odkaz na službu zadejte adresu služby OData:

http://localhost:port/odata

kde port je číslo portu.

Snímek obrazovky s oknem

Jako Obor názvů zadejte ProductService. Tato možnost definuje obor názvů třídy proxy.

Klikněte na Přejít. Visual Studio přečte dokument metadat OData, aby zjistil entity ve službě.

Snímek obrazovky s dialogovým oknem přidat odkaz na službu se zvýrazněnou službou kontejneru, aby se zobrazily operace, které v ní běží

Kliknutím na OK přidejte třídu proxy do projektu.

Snímek obrazovky s dialogovým oknem Průzkumníka řešení zobrazující nabídku v části

Vytvoření instance třídy proxy služby

Main V metodě vytvořte novou instanci třídy proxy následujícím způsobem:

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);

            // ...
        }
    }
}

Znovu použijte skutečné číslo portu, na kterém je vaše služba spuštěná. Při nasazování služby použijete identifikátor URI živé služby. Nemusíte aktualizovat proxy server.

Následující kód přidá obslužnou rutinu události, která vypíše identifikátory URI požadavku do okna konzoly. Tento krok není povinný, ale je zajímavé vidět identifikátory URI pro každý dotaz.

container.SendingRequest2 += (s, e) =>
{
    Console.WriteLine("{0} {1}", e.RequestMessage.Method, e.RequestMessage.Url);
};

Dotaz na službu

Následující kód získá seznam produktů ze služby 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);
    }
}

Všimněte si, že k odeslání požadavku HTTP nebo parsování odpovědi nemusíte psát žádný kód. Třída proxy to dělá automaticky při výčtu Container.Products kolekce ve smyčce foreach .

Po spuštění aplikace by výstup měl vypadat takto:

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

Pokud chcete získat entitu podle ID, použijte 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);
    }
}

Ve zbývající části tohoto tématu nebudu zobrazovat celou Main funkci, ale jenom kód potřebný k volání služby.

Použít možnosti dotazu

OData definuje možnosti dotazu , které se dají použít k filtrování, řazení, stránkování dat atd. V proxy serveru služby můžete tyto možnosti použít pomocí různých výrazů LINQ.

V této části ukážu stručné příklady. Další podrobnosti najdete v tématu Aspekty LINQ (WCF Data Services) na webu MSDN.

Filtrování ($filter)

K filtrování použijte klauzuli where . Následující příklad filtruje podle kategorie produktu.

// 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);
    }
}

Tento kód odpovídá následujícímu dotazu OData.

GET http://localhost/odata/Products()?$filter=Category eq 'apparel'

Všimněte si, že proxy převede klauzuli na where výraz OData $filter .

Řazení ($orderby)

K řazení použijte klauzuli orderby . Následující příklad seřadí podle ceny od nejvyšší po nejnižší.

// 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);
    }
}

Tady je odpovídající požadavek OData.

GET http://localhost/odata/Products()?$orderby=Price desc

stránkování Client-Side ($skip a $top)

U velkých sad entit může klient chtít omezit počet výsledků. Klient může například zobrazit 10 položek najednou. To se označuje jako stránkování na straně klienta. (K dispozici je také stránkování na straně serveru, kde server omezuje počet výsledků.) Pokud chcete provést stránkování na straně klienta, použijte metody LINQ Skip a Take . Následující příklad přeskočí prvních 40 výsledků a vezme dalších 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);
    }
}

Tady je odpovídající požadavek OData:

GET http://localhost/odata/Products()?$orderby=Price desc&$skip=40&$top=10

Výběr ($select) a rozbalení ($expand)

Pokud chcete zahrnout související entity, použijte metodu DataServiceQuery<t>.Expand . Pokud chcete například pro každou z nich Productzahrnout 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);
    }
}

Tady je odpovídající požadavek OData:

GET http://localhost/odata/Products()?$expand=Supplier

Pokud chcete změnit tvar odpovědi, použijte klauzuli LINQ select . Následující příklad získá pouze název každého produktu bez dalších vlastností.

// 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);
    }
}

Tady je odpovídající požadavek OData:

GET http://localhost/odata/Products()?$select=Name

Klauzule select může obsahovat související entity. V takovém případě nevolejte Expand; proxy v tomto případě rozšíření automaticky zahrne. Následující příklad získá název a dodavatele každého 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);
    }
}

Tady je odpovídající požadavek OData. Všimněte si, že obsahuje možnost $expand .

GET http://localhost/odata/Products()?$expand=Supplier&$select=Name,Supplier/Name

Další informace o $select a $expand najdete v tématu Použití $select, $expand a $value ve webovém rozhraní API 2.

Přidání nové entity

Pokud chcete do sady entit přidat novou entitu, zavolejte AddToEntitySet, kde EntitySet je název sady entit. AddToProducts Například přidá nový Product do Products sady entit. Když vygenerujete proxy server, WCF Data Services automaticky vytvoří tyto metody AddTo silného typu.

// 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);
    }
}

Pokud chcete přidat propojení mezi dvě entity, použijte metody AddLink a SetLink . Následující kód přidá nového dodavatele a nový produkt a vytvoří mezi nimi propojení.

// 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);
    }
}

AddLink použijte, pokud je vlastnost navigace kolekce. V tomto příkladu přidáváme produkt do Products kolekce u dodavatele.

SetLink použijte, pokud je vlastnost navigace jedna entita. V tomto příkladu Supplier nastavujeme vlastnost produktu.

Aktualizace nebo oprava

Chcete-li aktualizovat entitu, zavolejte Metodu 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);
    }
}

Aktualizace se provede při volání SaveChanges. Ve výchozím nastavení wcf odešle požadavek HTTP MERGE. Možnost PatchOnUpdate informuje wcf, aby místo toho odeslala http PATCH.

Poznámka

Proč PATCH versus MERGE? Původní specifikace HTTP 1.1 (RCF 2616) nedefinovala žádnou metodu HTTP se sémantikou "částečné aktualizace". Pro podporu částečných aktualizací definovala specifikace OData metodu MERGE. V roce 2010 rfc 5789 definoval metodu PATCH pro částečné aktualizace. Některé z historie si můžete přečíst v tomto blogovém příspěvku na WCF Data Services Blogu. Dnes je patch upřednostňován před sloučením. Kontroler OData vytvořený generováním uživatelského rozhraní Web API podporuje obě metody.

Pokud chcete nahradit celou entitu (sémantiku PUT), zadejte možnost ReplaceOnUpdate . To způsobí, že WCF odešle požadavek HTTP PUT.

container.SaveChanges(SaveChangesOptions.ReplaceOnUpdate);

Odstranění entity

Pokud chcete odstranit entitu, zavolejte 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();
    }
}

Vyvolání akce OData

V OData jsou akce způsob, jak přidat chování na straně serveru, které není snadné definovat jako operace CRUD u entit.

Přestože dokument metadat OData popisuje akce, třída proxy pro ně nevytvoří žádné metody silného typu. Akci OData můžete přesto vyvolat pomocí obecné metody Execute . Budete ale muset znát datové typy parametrů a návratové hodnoty.

Akce například RateProduct přijme parametr s názvem Rating typu Int32 a vrátí doublehodnotu . Následující kód ukazuje, jak tuto akci vyvolat.

int rating = 2;
Uri actionUri = new Uri(uri, "Products(5)/RateProduct");
var averageRating = container.Execute<double>(
    actionUri, "POST", true, new BodyOperationParameter("Rating", rating)).First();

Další informace najdete v tématuVolání operací a akcí služby.

Jednou z možností je rozšířit třídu Container tak, aby poskytovala metodu silného typu, která vyvolá akci:

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();
        }
    }
}