Sdílet prostřednictvím


Využívání datových kanálů OData z pracovního postupu

Datové služby WCF je součástí rozhraní .NET Framework, která umožňuje vytvářet služby, které používají protokol OData (Open Data Protocol) k zveřejnění a využívání dat přes web nebo intranet pomocí sémantiky reprezentačního přenosu stavu (REST). OData zveřejňuje data jako prostředky, které jsou adresovatelné identifikátory URI. Každá aplikace může pracovat s datovou službou založenou na OData, pokud může odeslat požadavek HTTP a zpracovat datový kanál OData, který vrací datová služba. Kromě toho Datové služby WCF zahrnuje klientské knihovny, které poskytují bohatší programovací prostředí při využívání datových kanálů OData z aplikací rozhraní .NET Framework. Toto téma obsahuje přehled o využívání datového kanálu OData v pracovním postupu s klientskými knihovnami bez použití.

Použití ukázkové služby Northwind OData

Příklady v tomto tématu používají ukázkovou datovou službu Northwind umístěnou na https://services.odata.org/Northwind/Northwind.svc/adrese . Tato služba je poskytována jako součást sady OData SDK a poskytuje přístup jen pro čtení k ukázkové databázi Northwind. Pokud potřebujete přístup k zápisu nebo pokud je požadovaná místní datová služba WCF, můžete postupovat podle kroků v rychlém startu Datové služby WCF a vytvořit místní službu OData, která poskytuje přístup k databázi Northwind. Pokud budete postupovat podle tohoto rychlého startu, nahraďte místní identifikátor URI za identifikátor uvedený v ukázkovém kódu v tomto tématu.

Využívání datového kanálu OData pomocí klientských knihoven

Datové služby WCF zahrnuje klientské knihovny, které umožňují snadněji využívat datový kanál OData z rozhraní .NET Framework a klientských aplikací. Tyto knihovny zjednodušují odesílání a přijímání zpráv HTTP. Přeloží také datovou část zprávy do objektů CLR, které představují data entity. Klientské knihovny obsahují dvě základní třídy DataServiceContext a DataServiceQuery<TElement>. Tyto třídy umožňují dotazovat datovou službu a pak pracovat s vrácenými daty entity jako objekty CLR. Tato část popisuje dva přístupy k vytváření aktivit, které používají klientské knihovny.

Přidání odkazu na službu WCF Data

K vygenerování klientských knihoven Northwind můžete použít dialogové okno Přidat odkaz na službu Service Reference v sadě Visual Studio 2012 a přidat odkaz na službu Northwind OData.

Snímek obrazovky s dialogovým oknem Přidat odkaz na službu

Všimněte si, že služba neobsahuje žádné operace služby a v seznamu Služeb jsou položky představující entity vystavené datovou službou Northwind. Po přidání odkazu na službu se pro tyto entity vygenerují třídy, které lze použít v klientském kódu. Příklady v tomto tématu používají tyto třídy a NorthwindEntities třídu k provádění dotazů.

Poznámka:

Další informace naleznete v tématu Generování klientské knihovny datové služby (Datové služby WCF).

Použití asynchronních metod

Pokud chcete vyřešit možné problémy s latencí, ke kterým může dojít při přístupu k prostředkům přes web, doporučujeme přistupovat k Datové služby WCF asynchronně. Klientské knihovny Datové služby WCF zahrnují asynchronní metody pro vyvolání dotazů a WF (Windows Workflow Foundation) poskytuje AsyncCodeActivity třídu pro vytváření asynchronních aktivit. AsyncCodeActivity odvozené aktivity lze zapsat tak, aby využívaly třídy rozhraní .NET Framework, které mají asynchronní metody, nebo kód, který se má spustit asynchronně, lze vložit do metody a vyvolat pomocí delegáta. Tato část obsahuje dva příklady AsyncCodeActivity odvozené aktivity– jednu, která používá asynchronní metody Datové služby WCF klientských knihoven a jednu, která používá delegáta.

Poznámka:

Další informace naleznete v tématu Asynchronní operace (Datové služby WCF) a Vytváření asynchronních aktivit.

Použití asynchronních metod klientské knihovny

Třída DataServiceQuery<TElement> poskytuje BeginExecute a EndExecute metody pro asynchronní dotazování služby OData. Tyto metody lze volat z BeginExecute odvozené AsyncCodeActivity třídy a EndExecute přepsání. AsyncCodeActivity BeginExecute Po vrácení přepsání může pracovní postup přejít nečinně (ale neuchovávat) a po dokončení EndExecute asynchronní práce je vyvolán modulem runtime.

V následujícím příkladu je definována OrdersByCustomer aktivita, která má dva vstupní argumenty. Argument CustomerId představuje zákazníka, který identifikuje, které objednávky se mají vrátit, a ServiceUri argument představuje identifikátor URI služby OData, který se má dotazovat. Vzhledem k tomu, že aktivita je odvozena z AsyncCodeActivity<IEnumerable<Order>> tohoto argumentu je také Result výstupní argument, který se používá k vrácení výsledků dotazu. Přepsání BeginExecute vytvoří dotaz LINQ, který vybere všechny objednávky zadaného zákazníka. Tento dotaz je zadán jako UserState předaný AsyncCodeActivityContexta pak je volána metoda dotazu BeginExecute . Všimněte si, že zpětné volání a stav předávané do dotazu BeginExecute jsou ty, které se předávají metodě aktivity BeginExecute . Po dokončení provádění dotazu se vyvolá metoda aktivity EndExecute . Dotaz se načte z UserStatea pak se volá metoda dotazu EndExecute . Tato metoda vrátí zadaný IEnumerable<T> typ entity; v tomto případě Order. Vzhledem k tomu IEnumerable<Order> , že je obecný typ AsyncCodeActivity<TResult>, je IEnumerable nastaven jako Result OutArgument<T> aktivita.

class OrdersByCustomer : AsyncCodeActivity<IEnumerable<Order>>
{
    [RequiredArgument]
    public InArgument<string> CustomerId { get; set; }

    [RequiredArgument]
    public InArgument<string> ServiceUri { get; set; }

    protected override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state)
    {
        NorthwindEntities dataContext = new NorthwindEntities(new Uri(ServiceUri.Get(context)));

        // Define a LINQ query that returns Orders and
        // Order_Details for a specific customer.
        DataServiceQuery<Order> ordersQuery = (DataServiceQuery<Order>)
            from o in dataContext.Orders.Expand("Order_Details")
            where o.Customer.CustomerID == CustomerId.Get(context)
            select o;

        // Specify the query as the UserState for the AsyncCodeActivityContext
        context.UserState = ordersQuery;

        // The callback and state used here are the ones passed into
        // the BeginExecute of this activity.
        return ordersQuery.BeginExecute(callback, state);
    }

    protected override IEnumerable<Order> EndExecute(AsyncCodeActivityContext context, IAsyncResult result)
    {
        // Get the DataServiceQuery from the context.UserState
        DataServiceQuery<Order> ordersQuery = context.UserState as DataServiceQuery<Order>;

        // Return an IEnumerable of the query results.
        return ordersQuery.EndExecute(result);
    }
}

V následujícím příkladu OrdersByCustomer aktivita načte seznam objednávek pro zadaného zákazníka a pak ForEach<T> aktivita vypíše vrácené objednávky a zapíše datum každé objednávky do konzoly.

Variable<IEnumerable<Order>> orders = new Variable<IEnumerable<Order>>();
DelegateInArgument<Order> order = new DelegateInArgument<Order>();

Activity wf = new Sequence
{
    Variables = { orders },
    Activities =
    {
        new WriteLine
        {
            Text = "Calling WCF Data Service..."
        },
        new OrdersByCustomer
        {
            ServiceUri = "http://services.odata.org/Northwind/Northwind.svc/",
            CustomerId = "ALFKI",
            Result = orders
        },
        new ForEach<Order>
        {
            Values = orders,
            Body = new ActivityAction<Order>
            {
                Argument = order,
                Handler = new WriteLine
                {
                    Text = new InArgument<string>((env) => string.Format("{0:d}", order.Get(env).OrderDate))
                }
            }
        }
    }
};

WorkflowInvoker.Invoke(wf);

Při vyvolání tohoto pracovního postupu se do konzoly zapíšou následující data:

Calling WCF Data Service...
8/25/1997
10/3/1997
10/13/1997
1/15/1998
3/16/1998
4/9/1998

Poznámka:

Pokud nelze navázat připojení k serveru OData, zobrazí se výjimka podobná následující výjimce:

Neošetřená výjimka: System.InvalidOperationException: Při zpracování tohoto požadavku došlo k chybě. >--- System.Net.WebException: Nejde se připojit ke vzdálenému serveru ---> System.Net.Sockets.SocketException: Pokus o připojení selhal, protože připojená strana po určité době neodpověděla správně nebo se nepodařilo navázat připojení, protože připojený hostitel neodpovídal.

Pokud je požadováno jakékoli další zpracování dat vrácených dotazem, můžete je provést v přepsání EndExecute aktivity. Obě BeginExecute a EndExecute jsou vyvolány pomocí vlákna pracovního postupu a jakýkoli kód v těchto přepsání se nespustí asynchronně. Pokud je další zpracování rozsáhlé nebo dlouhotrvající nebo jsou výsledky dotazu stránkované, měli byste zvážit přístup probíraný v další části, který používá delegáta ke spuštění dotazu a asynchronnímu zpracování.

Použití delegáta

Kromě vyvolání asynchronní metody třídy AsyncCodeActivityrozhraní .NET Framework může aktivita založená na -také definovat asynchronní logiku v jedné z jejích metod. Tato metoda je určena pomocí delegáta v přepsání BeginExecute aktivity. Když metoda vrátí, modul runtime vyvolá přepsání EndExecute aktivity. Při volání služby OData z pracovního postupu lze tuto metodu použít k dotazování služby a poskytnout jakékoli další zpracování.

V následujícím příkladu je definována ListCustomers aktivita. Tato aktivita se dotazuje ukázkové datové služby Northwind a vrátí List<Customer> hodnotu, která obsahuje všechny zákazníky v databázi Northwind. Asynchronní práce se provádí metodou GetCustomers . Tato metoda dotazuje službu pro všechny zákazníky a pak je zkopíruje do .List<Customer> Pak zkontroluje, jestli jsou výsledky stránkované. Pokud ano, služba se dotazuje na další stránku výsledků, přidá je do seznamu a pokračuje, dokud se nenačtou všechna zákaznická data.

Poznámka:

Další informace o stránkování v Datové služby WCF naleznete v tématu Postupy: Načtení stránkovaných výsledků (Datové služby WCF).

Po přidání všech zákazníků se seznam vrátí. Metoda GetCustomers je určena v přepsání BeginExecute aktivity. Vzhledem k tomu, že metoda má návratovou hodnotu, Func<string, List<Customer>> vytvoří se k určení metody.

Poznámka:

Pokud metoda, která provádí asynchronní práci nemá návratovou hodnotu, Action použije se místo Func<TResult>. Příklady vytvoření asynchronního příkladu pomocí obou přístupů najdete v tématu Vytváření asynchronních aktivit.

Toto Func<TResult> je přiřazeno k a UserStatepak BeginInvoke je volána. Vzhledem k tomu, že metoda, která se má vyvolat, nemá přístup k prostředí argumentů aktivity, je hodnota ServiceUri argumentu předána jako první parametr spolu s zpětné volání a stav, které byly předány BeginExecute. Když GetCustomers se vrátí, modul runtime vyvolá EndExecute. Kód načte EndExecute delegáta z UserStatevolání EndInvokea vrátí výsledek, což je seznam zákazníků vrácených z GetCustomers metody.

class ListCustomers : AsyncCodeActivity<List<Customer>>
{
    [RequiredArgument]
    public InArgument<string> ServiceUri { get; set; }

    protected override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state)
    {
        // Create a delegate that references the method that implements
        // the asynchronous work. Assign the delegate to the UserState,
        // invoke the delegate, and return the resulting IAsyncResult.
        Func<string, List<Customer>> GetCustomersDelegate = new Func<string, List<Customer>>(GetCustomers);
        context.UserState = GetCustomersDelegate;
        return GetCustomersDelegate.BeginInvoke(ServiceUri.Get(context), callback, state);
    }

    protected override List<Customer> EndExecute(AsyncCodeActivityContext context, IAsyncResult result)
    {
        // Get the delegate from the UserState and call EndInvoke
        Func<string, List<Customer>> GetCustomersDelegate = (Func<string, List<Customer>>)context.UserState;
        return (List<Customer>)GetCustomersDelegate.EndInvoke(result);
    }

    List<Customer> GetCustomers(string serviceUri)
    {
        // Get all customers here and add them to a list. This method doesn't have access to the
        // activity's environment of arguments, so the Service Uri is passed in.

        // Create the DataServiceContext using the service URI.
        NorthwindEntities context = new NorthwindEntities(new Uri(serviceUri));

        // Return all customers.
        QueryOperationResponse<Customer> response =
            context.Customers.Execute() as QueryOperationResponse<Customer>;

        // Add them to the list.
        List<Customer> customers = new List<Customer>(response);

        // Is this the complete list or are the results paged?
        DataServiceQueryContinuation<Customer> token;
        while ((token = response.GetContinuation()) != null)
        {
            // Load the next page of results.
            response = context.Execute<Customer>(token) as QueryOperationResponse<Customer>;

            // Add the next page of customers to the list.
            customers.AddRange(response);
        }

        // Return the list of customers
        return customers;
    }
}

V následujícím příkladu aktivita ListCustomers načte seznam zákazníků a pak ForEach<T> je vypíše a zapíše název společnosti a kontaktní jméno každého zákazníka do konzoly.

Variable<List<Customer>> customers = new Variable<List<Customer>>();
DelegateInArgument<Customer> customer = new DelegateInArgument<Customer>();

Activity wf = new Sequence
{
    Variables = { customers },
    Activities =
    {
        new WriteLine
        {
            Text = "Calling WCF Data Service..."
        },
        new ListCustomers
        {
            ServiceUri = "http://services.odata.org/Northwind/Northwind.svc/",
            Result = customers
        },
        new ForEach<Customer>
        {
            Values = customers,
            Body = new ActivityAction<Customer>
            {
                Argument = customer,
                Handler = new WriteLine
                {
                    Text = new InArgument<string>((env) => string.Format("{0}, Contact: {1}",
                        customer.Get(env).CompanyName, customer.Get(env).ContactName))
                }
            }
        }
    }
};

WorkflowInvoker.Invoke(wf);

Při vyvolání tohoto pracovního postupu se do konzoly zapíšou následující data. Vzhledem k tomu, že tento dotaz vrací mnoho zákazníků, zobrazí se zde pouze část výstupu.

Calling WCF Data Service...
Alfreds Futterkiste, Contact: Maria Anders
Ana Trujillo Emparedados y helados, Contact: Ana Trujillo
Antonio Moreno Taquería, Contact: Antonio Moreno
Around the Horn, Contact: Thomas Hardy
Berglunds snabbköp, Contact: Christina Berglund
...

Využívání datového kanálu OData bez použití klientských knihoven

OData zveřejňuje data jako prostředky, které jsou adresovatelné identifikátory URI. Při použití klientských knihoven jsou tyto identifikátory URI vytvořeny za vás, ale nemusíte používat klientské knihovny. V případě potřeby je možné přistupovat ke službám OData přímo bez použití klientských knihoven. Pokud nepoužíváte klientské knihovny umístění služby a požadovaná data jsou určena identifikátorem URI a výsledky se vrátí v odpovědi na požadavek HTTP. Tato nezpracovaná data je pak možné zpracovat nebo manipulovat požadovaným způsobem. Jedním ze způsobů, jak načíst výsledky dotazu OData, je použití WebClient třídy. V tomto příkladu se načte jméno kontaktu zákazníka reprezentovaného klíčem ALFKI.

string uri = "http://services.odata.org/Northwind/Northwind.svc/Customers('ALFKI')/ContactName";
WebClient client = new WebClient();
string data = client.DownloadString(uri);
Console.WriteLine("Raw data returned:\n{0}", data);

Po spuštění tohoto kódu se v konzole zobrazí následující výstup:

Raw data returned:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<ContactName xmlns="http://schemas.microsoft.com/ado/2007/08/dataservices">Maria Anders</ContactName>

V pracovním postupu by kód z tohoto příkladu mohl být začleněn do Execute přepsání CodeActivityvlastní aktivity založené na základě, ale stejnou funkci lze provést také pomocí InvokeMethod<TResult> aktivity. Aktivita InvokeMethod<TResult> umožňuje autorům pracovních postupů vyvolat statické metody a metody instance třídy a má také možnost vyvolat zadanou metodu asynchronně. V následujícím příkladu je aktivita nakonfigurovaná tak, InvokeMethod<TResult> aby volala DownloadString metodu WebClient třídy a vrátila seznam zákazníků.

new InvokeMethod<string>
{
    TargetObject = new InArgument<WebClient>(new VisualBasicValue<WebClient>("New System.Net.WebClient()")),
    MethodName = "DownloadString",
    Parameters =
    {
        new InArgument<string>("http://services.odata.org/Northwind/Northwind.svc/Customers('ALFKI')/Orders")
    },
    Result = data,
    RunAsynchronously = true
},

InvokeMethod<TResult> může volat statické i instance metody třídy. Vzhledem k tomuDownloadString, že je metoda WebClient instance třídy, je pro třídu zadána TargetObjectnová instance WebClient třídy . DownloadString je zadána jako MethodNameidentifikátor URI, který obsahuje dotaz, je zadán v Parameters kolekci a návratová hodnota je přiřazena k hodnotě Result . Hodnota je nastavena RunAsynchronously na true, což znamená, že vyvolání metody se spustí asynchronně s ohledem na pracovní postup. V následujícím příkladu se vytvoří pracovní postup, který pomocí aktivity dotazuje InvokeMethod<TResult> ukázkovou datovou službu Northwind na seznam objednávek pro konkrétního zákazníka a vrácená data se zapíšou do konzoly.

Variable<string> data = new Variable<string>();

Activity wf = new Sequence
{
    Variables = { data },
    Activities =
    {
        new WriteLine
        {
            Text = "Calling WCF Data Service..."
        },
        new InvokeMethod<string>
        {
            TargetObject = new InArgument<WebClient>(new VisualBasicValue<WebClient>("New System.Net.WebClient()")),
            MethodName = "DownloadString",
            Parameters =
            {
                new InArgument<string>("http://services.odata.org/Northwind/Northwind.svc/Customers('ALFKI')/Orders")
            },
            Result = data,
            RunAsynchronously = true
        },
        new WriteLine
        {
            Text = new InArgument<string>(env => string.Format("Raw data returned:\n{0}", data.Get(env)))
        }
    }
};

WorkflowInvoker.Invoke(wf);

Při vyvolání tohoto pracovního postupu se v konzole zobrazí následující výstup. Vzhledem k tomu, že tento dotaz vrací několik objednávek, zobrazí se zde pouze část výstupu.

Calling WCF Data Service...
Raw data returned:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>*
<feed
xml:base="http://services.odata.org/Northwind/Northwind.svc/"
xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices"
xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"
xmlns="http://www.w3.org/2005/Atom">
<title type="text">Orders\</title>
<id>http://services.odata.org/Northwind/Northwind.svc/Customers('ALFKI')/Orders\</id>
<updated>2010-05-19T19:37:07Z\</updated>
<link rel="self" title="Orders" href="Orders" />
<entry>
<id>http://services.odata.org/Northwind/Northwind.svc/Orders(10643)\</id>
<title type="text">\</title>
<updated>2010-05-19T19:37:07Z\</updated>
<author>
<name />
</author>
<link rel="edit" title="Order" href="Orders(10643)" />
<link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/Customer" type="application/atom+xml;type=entry" title="Customer" href="Orders(10643)/Customer" />
...

Tento příklad poskytuje jednu metodu, kterou můžou autoři aplikací pracovního postupu použít k využívání nezpracovaných dat vrácených ze služby OData. Další informace o přístupu k Datové služby WCF pomocí identifikátorů URI najdete v tématech Přístup k prostředkům datové služby (Datové služby WCF) a OData: Konvence identifikátorů URI.