Udostępnij za pośrednictwem


Korzystanie ze źródeł danych OData z przepływu pracy

Usługi danych programu WCF jest składnikiem programu .NET Framework, który umożliwia tworzenie usług korzystających z protokołu Open Data Protocol (OData) w celu uwidocznienia i używania danych za pośrednictwem sieci Web lub intranetu przy użyciu semantyki transferu stanu reprezentacji (REST). OData uwidacznia dane jako zasoby, które są adresowane przez identyfikatory URI. Każda aplikacja może wchodzić w interakcje z usługą danych opartą na protokole OData, jeśli może wysyłać żądanie HTTP i przetwarzać źródło danych OData zwracane przez usługę danych. Ponadto Usługi danych programu WCF zawiera biblioteki klienckie, które zapewniają bogatsze środowisko programowania podczas korzystania ze źródeł danych OData z aplikacji .NET Framework. Ten temat zawiera omówienie korzystania ze źródła danych OData w przepływie pracy z bibliotekami klienta i bez korzystania z tych bibliotek.

Korzystanie z przykładowej usługi Northwind OData

W przykładach w tym temacie użyto przykładowej usługi danych Northwind znajdującej się w lokalizacji https://services.odata.org/Northwind/Northwind.svc/. Ta usługa jest udostępniana jako część zestawu OData SDK i zapewnia dostęp tylko do odczytu do przykładowej bazy danych Northwind. Jeśli jest wymagany dostęp do zapisu lub jeśli jest wymagana lokalna usługa danych WCF, możesz wykonać kroki opisane w przewodniku Szybki start Usługi danych programu WCF, aby utworzyć lokalną usługę OData, która zapewnia dostęp do bazy danych Northwind. W przypadku korzystania z przewodnika Szybki start zastąp lokalny identyfikator URI podany w przykładowym kodzie w tym temacie.

Korzystanie ze źródła danych OData przy użyciu bibliotek klienckich

Usługi danych programu WCF zawiera biblioteki klienckie, które umożliwiają łatwiejsze korzystanie ze źródła danych OData z programu .NET Framework i aplikacji klienckich. Te biblioteki upraszczają wysyłanie i odbieranie komunikatów HTTP. Tłumaczą również ładunek komunikatu na obiekty CLR reprezentujące dane jednostki. Biblioteki klienckie zawierają dwie podstawowe klasy DataServiceContext i DataServiceQuery<TElement>. Te klasy umożliwiają wykonywanie zapytań względem usługi danych, a następnie pracę z zwracanymi danymi jednostki jako obiektami CLR. W tej sekcji opisano dwa podejścia do tworzenia działań korzystających z bibliotek klienckich.

Dodawanie odwołania do usługi danych WCF

Aby wygenerować biblioteki klienta Northwind, możesz użyć okna dialogowego Dodawanie odwołania do usługi Service Reference w programie Visual Studio 2012, aby dodać odwołanie do usługi Northwind OData.

Zrzut ekranu przedstawiający okno dialogowe Dodawanie odwołania do usługi.

Należy pamiętać, że nie ma żadnych operacji usługi uwidocznionych przez usługę, a na liście Usługi znajdują się elementy reprezentujące jednostki uwidocznione przez usługę danych Northwind. Po dodaniu odwołania do usługi klasy będą generowane dla tych jednostek i mogą być używane w kodzie klienta. Przykłady w tym temacie używają tych klas i NorthwindEntities klasy do wykonywania zapytań.

Używanie metod asynchronicznych

Aby rozwiązać możliwe problemy z opóźnieniami, które mogą wystąpić podczas uzyskiwania dostępu do zasobów za pośrednictwem sieci Web, zalecamy uzyskanie dostępu Usługi danych programu WCF asynchronicznie. Biblioteki klienckie Usługi danych programu WCF obejmują metody asynchroniczne do wywoływania zapytań, a program Windows Workflow Foundation (WF) udostępnia klasę AsyncCodeActivity do tworzenia działań asynchronicznych. AsyncCodeActivity Działania pochodne można napisać, aby korzystać z klas programu .NET Framework, które mają metody asynchroniczne, lub kod, który ma być wykonywany asynchronicznie, można umieścić w metodzie i wywoływane przy użyciu delegata. W tej sekcji przedstawiono dwa przykłady działania pochodnegoAsyncCodeActivity: jedno, które korzysta z metod asynchronicznych bibliotek klienta Usługi danych programu WCF i tych, które używają delegata.

Uwaga

Aby uzyskać więcej informacji, zobacz Operacje asynchroniczne (Usługi danych programu WCF) i Tworzenie działań asynchronicznych.

Używanie metod asynchronicznych biblioteki klienta

Klasa DataServiceQuery<TElement> udostępnia BeginExecute metody i EndExecute służące do asynchronicznego wykonywania zapytań dotyczących usługi OData. Metody te mogą być wywoływane z BeginExecute przesłonięć i EndExecute klasy pochodnej AsyncCodeActivity . AsyncCodeActivity BeginExecute Po powrocie przesłonięcia przepływ pracy może przechodzić bezczynnie (ale nie utrwalać), a po zakończeniu EndExecute pracy asynchronicznej jest wywoływany przez środowisko uruchomieniowe.

W poniższym przykładzie zdefiniowano działanie, OrdersByCustomer które ma dwa argumenty wejściowe. Argument CustomerId reprezentuje klienta, który identyfikuje, które zamówienia mają zostać zwrócone, a ServiceUri argument reprezentuje identyfikator URI usługi OData do odpytowania. Ponieważ działanie pochodzi z AsyncCodeActivity<IEnumerable<Order>> tego elementu, jest również argumentem wyjściowym Result , który jest używany do zwracania wyników zapytania. Zastąpienie BeginExecute tworzy zapytanie LINQ, które wybiera wszystkie zamówienia określonego klienta. To zapytanie jest określane jako UserState przekazane AsyncCodeActivityContext, a następnie wywoływana BeginExecute jest metoda zapytania. Zwróć uwagę, że wywołanie zwrotne i stan przekazywany do zapytania BeginExecute to te, które są przekazywane do metody działania BeginExecute . Po zakończeniu wykonywania zapytania wywoływana EndExecute jest metoda działania. Zapytanie jest pobierane z UserStatemetody , a następnie wywoływana EndExecute jest metoda zapytania. Ta metoda zwraca typ IEnumerable<T> określonej jednostki; w tym przypadku Order. Ponieważ IEnumerable<Order> jest rodzajowym typem AsyncCodeActivity<TResult>, jest to IEnumerable ustawione jako Result OutArgument<T> działanie.

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

W poniższym przykładzie OrdersByCustomer działanie pobiera listę zamówień dla określonego klienta, a następnie ForEach<T> wylicza zwrócone zamówienia i zapisuje datę każdego zamówienia w konsoli.

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

Po wywołaniu tego przepływu pracy następujące dane są zapisywane w konsoli programu :

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

Uwaga

Jeśli nie można nawiązać połączenia z serwerem OData, otrzymasz wyjątek podobny do następującego wyjątku:

Nieobsługiwany wyjątek: System.InvalidOperationException: Wystąpił błąd podczas przetwarzania tego żądania. >--- System.Net.WebException: Nie można nawiązać połączenia z serwerem zdalnym ---> System.Net.Socket.SocketException: Próba połączenia nie powiodła się, ponieważ połączona strona nie odpowiedziała prawidłowo po upływie określonego czasu lub nawiązano połączenie nie powiodło się, ponieważ host połączony nie odpowiedział.

Jeśli wymagane jest dodatkowe przetwarzanie danych zwracanych przez zapytanie, można to zrobić w zastąpieniu działania EndExecute . Obie BeginExecute metody i EndExecute są wywoływane przy użyciu wątku przepływu pracy, a żaden kod w tych przesłonięciach nie jest uruchamiany asynchronicznie. Jeśli dodatkowe przetwarzanie jest obszerne lub długotrwałe lub wyniki zapytania są stronicowane, należy rozważyć podejście omówione w następnej sekcji, które używa delegata do wykonywania zapytania i wykonywania dodatkowego przetwarzania asynchronicznie.

Używanie delegata

Oprócz wywoływania metody asynchronicznej klasy AsyncCodeActivity.NET Framework działanie oparte na metodzie może również definiować logikę asynchroniczną w jednej z jej metod. Ta metoda jest określana przy użyciu delegata BeginExecute w zastąpieniu działania. Gdy metoda zwraca, środowisko uruchomieniowe wywołuje przesłonięcia działania EndExecute . Podczas wywoływania usługi OData z przepływu pracy ta metoda może służyć do wykonywania zapytań dotyczących usługi i dostarczania dodatkowych operacji przetwarzania.

W poniższym przykładzie zdefiniowano ListCustomers działanie. To działanie wysyła zapytanie do przykładowej usługi danych Northwind i zwraca element List<Customer> zawierający wszystkich klientów w bazie danych Northwind. Praca asynchroniczna jest wykonywana przez metodę GetCustomers . Ta metoda wysyła zapytanie do usługi dla wszystkich klientów, a następnie kopiuje je do klasy List<Customer>. Następnie sprawdza, czy wyniki są stronicowane. Jeśli tak, wysyła zapytanie do usługi o następną stronę wyników, dodaje je do listy i kontynuuje do momentu pobrania wszystkich danych klienta.

Uwaga

Aby uzyskać więcej informacji na temat stronicowania w Usługi danych programu WCF, zobacz Jak załadować wyniki stronicowane (Usługi danych programu WCF).

Po dodaniu wszystkich klientów zostanie zwrócona lista. Metoda GetCustomers jest określona BeginExecute w zastąpieniu działania. Ponieważ metoda ma wartość zwracaną, Func<string, List<Customer>> tworzony jest element określający metodę .

Uwaga

Jeśli metoda wykonująca asynchroniczną pracę nie ma wartości zwracanej, Action zostanie użyta metoda zamiast Func<TResult>. Przykłady tworzenia przykładu asynchronicznego przy użyciu obu metod można znaleźć w temacie Creating Asynchronous Activities (Tworzenie działań asynchronicznych).

Jest to Func<TResult> przypisane do elementu UserState, a następnie BeginInvoke jest wywoływane. Ponieważ wywoływana metoda nie ma dostępu do środowiska argumentów działania, wartość argumentu ServiceUri jest przekazywana jako pierwszy parametr wraz z wywołaniem zwrotnym i stanem przekazanym do BeginExecutemetody . Po GetCustomers powrocie środowisko uruchomieniowe wywołuje metodę EndExecute. Kod w pliku EndExecute pobiera delegata z UserStatemetody , wywołuje EndInvokemetodę i zwraca wynik, który jest listą klientów zwróconych 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;
    }
}

W poniższym przykładzie ListCustomers działanie pobiera listę klientów, a następnie ForEach<T> wylicza je i zapisuje nazwę firmy i nazwę kontaktową każdego klienta w konsoli.

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

Po wywołaniu tego przepływu pracy następujące dane są zapisywane w konsoli programu . Ponieważ to zapytanie zwraca wielu klientów, w tym miejscu jest wyświetlana tylko część danych wyjściowych.

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
...

Korzystanie ze źródła danych OData bez używania bibliotek klienckich

OData uwidacznia dane jako zasoby, które są adresowane przez identyfikatory URI. W przypadku korzystania z bibliotek klienckich te identyfikatory URI są tworzone dla Ciebie, ale nie trzeba używać bibliotek klienckich. W razie potrzeby usługi OData mogą być dostępne bezpośrednio bez korzystania z bibliotek klienckich. Jeśli nie używasz bibliotek klienckich, lokalizacja usługi i żądane dane są określone przez identyfikator URI, a wyniki są zwracane w odpowiedzi na żądanie HTTP. Te nieprzetworzone dane można następnie przetworzyć lub manipulować nimi w żądany sposób. Jednym ze sposobów pobrania wyników zapytania OData jest użycie WebClient klasy . W tym przykładzie pobierana jest nazwa kontaktu klienta reprezentowana przez klucz 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 uruchomieniu tego kodu do konsoli zostaną wyświetlone następujące dane wyjściowe:

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>

W przepływie pracy kod z tego przykładu może zostać uwzględniony w Execute przesłonięcie działania niestandardowego opartego CodeActivityna bazie, ale można również wykonać te same funkcje za pomocą InvokeMethod<TResult> działania. Działanie InvokeMethod<TResult> umożliwia autorom przepływu pracy wywoływanie metod statycznych i wystąpień klasy, a także możliwość wywoływania określonej metody asynchronicznie. W poniższym przykładzie InvokeMethod<TResult> działanie jest skonfigurowane do wywoływania DownloadString metody WebClient klasy i zwracania listy klientów.

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> metoda może wywoływać metody statyczne i wystąpienia klasy. Ponieważ DownloadString jest metodą WebClient wystąpienia klasy, dla klasy określono TargetObjectnowe wystąpienie WebClient klasy . DownloadString Jest określony jako MethodName, identyfikator URI, który zawiera zapytanie jest określony w Parameters kolekcji, a wartość zwracana jest przypisana Result do wartości. Wartość jest ustawiona RunAsynchronously na true, co oznacza, że wywołanie metody będzie uruchamiane asynchronicznie w odniesieniu do przepływu pracy. W poniższym przykładzie tworzony jest przepływ pracy, który używa InvokeMethod<TResult> działania do wykonywania zapytań względem przykładowej usługi danych Northwind w celu wyświetlenia listy zamówień dla określonego klienta, a następnie zwracane dane są zapisywane w konsoli.

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

Po wywołaniu tego przepływu pracy następujące dane wyjściowe zostaną wyświetlone w konsoli programu . Ponieważ to zapytanie zwraca kilka zamówień, w tym miejscu jest wyświetlana tylko część danych wyjściowych.

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" />
...

W tym przykładzie przedstawiono jedną z metod, za pomocą których autorzy aplikacji przepływu pracy mogą używać nieprzetworzonych danych zwracanych z usługi OData. Aby uzyskać więcej informacji na temat uzyskiwania dostępu do Usługi danych programu WCF przy użyciu identyfikatorów URI, zobacz Uzyskiwanie dostępu do zasobów usługi danych (Usługi danych programu WCF) i Konwencje OData: URI.