Dela via


Förbrukning av OData-feeds från ett arbetsflöde

WCF Data Services är en komponent i .NET Framework som gör att du kan skapa tjänster som använder Open Data Protocol (OData) för att exponera och använda data via webben eller intranätet med hjälp av semantiken för representationstillståndsöverföring (REST). OData exponerar data som resurser som kan adresseras av URI:er. Alla program kan interagera med en OData-baserad datatjänst om det kan skicka en HTTP-begäran och bearbeta OData-flödet som en datatjänst returnerar. Dessutom innehåller WCF Data Services klientbibliotek som ger en bättre programmeringsupplevelse när du använder OData-feeds från .NET Framework-program. Det här avsnittet innehåller en översikt över hur du använder en OData-feed i ett arbetsflöde med och utan att använda klientbiblioteken.

Använda Northwind OData-exempeltjänsten

Exemplen i det här avsnittet använder northwind-exempeldatatjänsten som finns på https://services.odata.org/Northwind/Northwind.svc/. Den här tjänsten tillhandahålls som en del av OData SDK och ger skrivskyddad åtkomst till northwind-exempeldatabasen. Om du vill ha skrivåtkomst, eller om du vill ha en lokal WCF-datatjänst, kan du följa stegen i snabbstarten för WCF Data Services för att skapa en lokal OData-tjänst som ger åtkomst till Northwind-databasen. Om du följer snabbstarten ersätter du den lokala URI:n för den som anges i exempelkoden i det här avsnittet.

Att konsumera en OData-feed med klientbibliotekerna

WCF Data Services innehåller klientbibliotek som gör att du enklare kan använda ett OData-flöde från .NET Framework och klientprogram. De här biblioteken förenklar sändning och mottagning av HTTP-meddelanden. De översätter också meddelandets nyttolast till CLR-objekt som representerar entitetsdata. Klientbiblioteken har de två kärnklasserna DataServiceContext och DataServiceQuery<TElement>. Med de här klasserna kan du fråga en datatjänst och sedan arbeta med returnerade entitetsdata som CLR-objekt. Det här avsnittet beskriver två metoder för att skapa aktiviteter som använder klientbiblioteken.

Lägga till en tjänstreferens till WCF-datatjänsten

Om du vill generera Northwind-klientbiblioteken kan du använda dialogrutan Lägg till tjänstreferens i Visual Studio 2012 för att lägga till en referens till Northwind OData-tjänsten.

Skärmbild som visar dialogrutan Lägg till tjänstreferens.

Observera att det inte finns några tjänståtgärder som exponeras av tjänsten, och i listan Tjänster finns det objekt som representerar de entiteter som exponeras av Northwind-datatjänsten. När tjänstreferensen läggs till genereras klasser för dessa entiteter och de kan användas i klientkoden. Exemplen i det här avsnittet använder dessa klasser samt klassen NorthwindEntities för att utföra frågorna.

Använda asynkrona metoder

För att åtgärda eventuella problem med svarstid som kan uppstå vid åtkomst till resurser via webben rekommenderar vi att du kommer åt WCF Data Services asynkront. WCF Data Services-klientbiblioteken innehåller asynkrona metoder för att anropa frågor, och Windows Workflow Foundation (WF) tillhandahåller AsyncCodeActivity klassen för redigering av asynkrona aktiviteter. AsyncCodeActivity härledda aktiviteter kan skrivas för att dra nytta av .NET Framework-klasser som har asynkrona metoder, eller så kan koden som ska köras asynkront placeras i en metod och anropas med hjälp av ett ombud. Det här avsnittet innehåller två exempel på en AsyncCodeActivity härledd aktivitet, ett som använder de asynkrona metoderna i WCF Data Services-klientbiblioteken och ett som använder ett ombud.

Använda asynkrona metoder för klientbibliotek

Klassen DataServiceQuery<TElement> tillhandahåller BeginExecute och EndExecute metoder för att fråga en OData-tjänst asynkront. Dessa metoder kan anropas från BeginExecute och EndExecute i en AsyncCodeActivity härledd klass. När åsidosättningen AsyncCodeActivityBeginExecute återgår kan arbetsflödet gå in i viloläge (men inte sparas) och när det asynkrona arbetet har slutförts anropas EndExecute av systemet.

I följande exempel definieras en OrdersByCustomer aktivitet som har två indataargument. Argumentet CustomerId representerar den kund som identifierar vilka beställningar som ska returneras och ServiceUri argumentet representerar URI:n för OData-tjänsten som ska frågas. Eftersom aktiviteten härleds därifrån AsyncCodeActivity<IEnumerable<Order>> finns också ett Result utdataargument som används för att returnera resultatet av frågan. Åsidosättningen BeginExecute skapar en LINQ-fråga som väljer alla beställningar från den angivna kunden. Den här frågan specificeras som UserState för det passerade AsyncCodeActivityContext, och sedan kallas frågans BeginExecute-metod. Observera att återanropet och de tillstånd som skickas till förfrågans BeginExecute är de som skickas till aktivitetens BeginExecute metod. När frågan har körts anropas aktivitetens EndExecute metod. Frågan hämtas från UserStateoch sedan anropas frågans EndExecute metod. Den här metoden returnerar en IEnumerable<T> av den angivna entitetstypen. I det här fallet Order. Eftersom IEnumerable<Order> är den allmänna typen av AsyncCodeActivity<TResult>, anges detta IEnumerable som ResultOutArgument<T> aktivitetens.

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

I följande exempel OrdersByCustomer hämtar aktiviteten en lista med beställningar för den angivna kunden och sedan räknar en ForEach<T> aktivitet upp de returnerade beställningarna och skriver datumet för varje beställning till konsolen.

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

När det här arbetsflödet anropas skrivs följande data till konsolen:

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

Kommentar

Om det inte går att upprätta en anslutning till OData-servern får du ett undantag som liknar följande undantag:

Ohanterat undantag: System.InvalidOperationException: Ett fel uppstod när begäran bearbetades. >--- System.Net.WebException: Det gick inte att ansluta till fjärrservern ---> System.Net.Sockets.SocketException: Ett anslutningsförsök misslyckades eftersom den anslutna parten inte svarade korrekt efter en viss tid, eller så misslyckades anslutningen eftersom den anslutna värden inte svarade.

Om det krävs någon ytterligare bearbetning av data som returneras av frågan kan det göras i aktivitetens EndExecute override-metod. Både BeginExecute och EndExecute anropas med hjälp av arbetsflödestråden, och all kod i dessa åsidosättningar körs inte asynkront. Om den ytterligare bearbetningen är omfattande eller tidskrävande, eller om frågeresultaten är paginerade, bör du överväga den metod som beskrivs i nästa avsnitt, som använder en delegat för att köra frågan och utföra ytterligare bearbetning asynkront.

Använda ett ombud

Förutom att anropa den asynkrona metoden för en .NET Framework-klass kan en AsyncCodeActivity-baserad aktivitet också definiera den asynkrona logiken i någon av dess metoder. Den här metoden specificeras genom att använda en delegering i aktivitetens BeginExecute åsidosättning. När metoden returnerar, anropar runtime-miljön aktivitetens åsidosättning av EndExecute. När du anropar en OData-tjänst från ett arbetsflöde kan den här metoden användas för att fråga tjänsten och tillhandahålla ytterligare bearbetning.

I följande exempel definieras en ListCustomers aktivitet. Den här aktiviteten gör en förfrågan till Northwind-exempeldatatjänsten och returnerar en List<Customer> som innehåller alla kunder i databasen Northwind. Det asynkrona arbetet utförs med GetCustomers metoden . Den här metoden frågar efter tjänsten för alla kunder och kopierar dem sedan till en List<Customer>. Den kontrollerar sedan om resultaten är uppdelade i sidor. I så fall frågar tjänsten efter nästa resultatsida, lägger till dem i listan och fortsätter tills alla kunddata har hämtats.

Kommentar

Mer information om sidindelning i WCF Data Services finns under Så här: Läsa in sidresultat (WCF Data Services).

När alla kunder har lagts till returneras listan. Metoden GetCustomers specificeras i aktivitetens BeginExecute-överskrivning. Eftersom metoden har ett returvärde skapas en Func<string, List<Customer>> för att ange metoden.

Kommentar

Om metoden som utför det asynkrona arbetet inte har något returvärde används en Action i stället för en Func<TResult>. Exempel på hur du skapar ett asynkront exempel med båda metoderna finns i Skapa asynkrona aktiviteter.

Detta Func<TResult> tilldelas UserState, och sedan anropas BeginInvoke. Eftersom metoden som ska anropas inte har åtkomst till aktivitetens miljö med argument skickas argumentets ServiceUri värde som den första parametern tillsammans med motringningen och tillståndet som skickades till BeginExecute. När GetCustomers returnerar anropar körtiden EndExecute. Koden i EndExecute hämtar ombudet från UserState, anropar EndInvokeoch returnerar resultatet, som är listan över kunder som returneras från GetCustomers metoden.

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

I följande exempel ListCustomers hämtar aktiviteten en lista över kunder och sedan räknar en ForEach<T> aktivitet upp dem och skriver företagets namn och kontaktnamn för varje kund till konsolen.

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

När det här arbetsflödet anropas skrivs följande data till konsolen. Eftersom den här frågan returnerar många kunder visas endast en del av utdata här.

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

Använda en OData-feed utan att använda klientbiblioteken

OData exponerar data som resurser som kan adresseras av URI:er. När du använder klientbiblioteken skapas dessa URI:er åt dig, men du behöver inte använda klientbiblioteken. Om du vill kan OData-tjänster nås direkt utan att använda klientbiblioteken. När du inte använder klientbiblioteken anges tjänstens plats och önskade data av URI:n och resultaten returneras i svaret på HTTP-begäran. Dessa rådata kan sedan bearbetas eller manipuleras på önskat sätt. Ett sätt att hämta resultatet av en OData-fråga är att använda WebClient klassen . I det här exemplet hämtas kontaktnamnet för kunden som representeras av nyckeln 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{data}");

När den här koden körs visas följande utdata i konsolen:

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>

I ett arbetsflöde kan koden från det här exemplet integreras i överriden Execute av en CodeActivity-baserad anpassad aktivitet, men samma funktionalitet kan också utföras med hjälp av InvokeMethod<TResult>-aktiviteten. Aktiviteten InvokeMethod<TResult> gör det möjligt för arbetsflödesförfattare att anropa statiska metoder och instansmetoder för en klass och har även ett alternativ för att anropa den angivna metoden asynkront. I följande exempel konfigureras en InvokeMethod<TResult> aktivitet för att anropa DownloadString metoden för WebClient klassen och returnera en lista över kunder.

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> kan anropa både statiska metoder och instansmetoder för en klass. Eftersom DownloadString är en instansmetod för WebClient klassen anges en ny instans av WebClient klassen för TargetObject. DownloadString anges som MethodName, den URI som innehåller frågan anges i Parameters samlingen och returvärdet tilldelas värdet Result . Värdet RunAsynchronously är inställt på true, vilket innebär att metodanropet körs asynkront när det gäller arbetsflödet. I följande exempel konstrueras ett arbetsflöde som använder InvokeMethod<TResult> aktiviteten för att fråga Northwind-exempeldatatjänsten om en lista över beställningar för en specifik kund, och sedan skrivs de returnerade data till konsolen.

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

När det här arbetsflödet anropas visas följande utdata för konsolen. Eftersom den här frågan returnerar flera beställningar visas endast en del av utdata här.

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

Det här exemplet innehåller en metod som arbetsflödesprogramförfattare kan använda för att använda rådata som returneras från en OData-tjänst. Mer information om hur du kommer åt WCF Data Services med hjälp av URI:er finns i Komma åt datatjänstresurser (WCF Data Services) och OData: URI-konventioner.