Partilhar via


Consumindo feeds OData de um fluxo de trabalho

WCF Data Services é um componente do .NET Framework que permite criar serviços que usam o Open Data Protocol (OData) para expor e consumir dados pela Web ou intranet usando a semântica de transferência de estado representacional (REST). OData expõe dados como recursos que são endereçáveis por URIs. Qualquer aplicativo pode interagir com um serviço de dados baseado em OData se puder enviar uma solicitação HTTP e processar o feed OData retornado por um serviço de dados. Além disso, o WCF Data Services inclui bibliotecas de cliente que fornecem uma experiência de programação mais rica quando você consome feeds OData de aplicativos .NET Framework. Este tópico fornece uma visão geral do consumo de um feed OData em um fluxo de trabalho com e sem o uso das bibliotecas de cliente.

Usando o exemplo de serviço Northwind OData

Os exemplos neste tópico usam o serviço de dados Northwind de exemplo localizado em https://services.odata.org/Northwind/Northwind.svc/. Este serviço é fornecido como parte do OData SDK e fornece acesso somente leitura ao banco de dados Northwind de exemplo. Se o acesso de gravação for desejado ou se um WCF Data Service local for desejado, você poderá seguir as etapas do Guia de início rápido do WCF Data Services para criar um serviço OData local que forneça acesso ao banco de dados Northwind. Se você seguir o início rápido, substitua o URI local pelo fornecido no código de exemplo neste tópico.

Consumindo um feed OData usando as bibliotecas de cliente

O WCF Data Services inclui bibliotecas de cliente que permitem que você consuma mais facilmente um feed OData do .NET Framework e de aplicativos cliente. Essas bibliotecas simplificam o envio e o recebimento de mensagens HTTP. Eles também convertem a carga útil da mensagem em objetos CLR que representam dados de entidade. As bibliotecas de cliente apresentam as duas classes DataServiceContext principais e DataServiceQuery<TElement>. Essas classes permitem que você consulte um serviço de dados e, em seguida, trabalhe com os dados de entidade retornados como objetos CLR. Esta seção aborda duas abordagens para criar atividades que usam as bibliotecas cliente.

Adicionando uma referência de serviço ao serviço WCF Data

Para gerar as bibliotecas de cliente Northwind, você pode usar a caixa de diálogo Adicionar referência de serviço no Visual Studio 2012 para adicionar uma referência ao serviço Northwind OData.

Captura de tela que mostra a caixa de diálogo Adicionar Referência de Serviço.

Observe que não há operações de serviço expostas pelo serviço e, na lista Serviços , há itens que representam as entidades expostas pelo serviço de dados Northwind. Quando a referência de serviço é adicionada, as classes serão geradas para essas entidades e elas podem ser usadas no código do cliente. Os exemplos neste tópico usam essas classes e a NorthwindEntities classe para executar as consultas.

Usando métodos assíncronos

Para resolver possíveis problemas de latência que podem ocorrer ao acessar recursos pela Web, recomendamos acessar o WCF Data Services de forma assíncrona. As bibliotecas de cliente do WCF Data Services incluem métodos assíncronos para invocar consultas, e o Windows Workflow Foundation (WF) fornece a classe para a criação de atividades assíncronas AsyncCodeActivity . AsyncCodeActivity atividades derivadas podem ser escritas para aproveitar as classes do .NET Framework que têm métodos assíncronos, ou o código a ser executado de forma assíncrona pode ser colocado em um método e invocado usando um delegado. Esta seção fornece dois exemplos de uma AsyncCodeActivity atividade derivada: um que usa os métodos assíncronos das bibliotecas de cliente do WCF Data Services e outro que usa um delegado.

Nota

Para obter mais informações, consulte Operações assíncronas (WCF Data Services) e Criando atividades assíncronas.

Usando métodos assíncronos de biblioteca de cliente

A DataServiceQuery<TElement> classe fornece BeginExecute e EndExecute métodos para consultar um serviço OData de forma assíncrona. Esses métodos podem ser chamados a BeginExecute partir das substituições e EndExecute substituições de uma AsyncCodeActivity classe derivada. Quando a AsyncCodeActivity BeginExecute substituição retorna, o fluxo de trabalho pode ficar ocioso (mas não persistir) e, quando o trabalho assíncrono é concluído, EndExecute é invocado pelo tempo de execução.

No exemplo a seguir, é definida uma OrdersByCustomer atividade que tem dois argumentos de entrada. O CustomerId argumento representa o cliente que identifica quais ordens devem ser retornadas, e o ServiceUri argumento representa o URI do serviço OData a ser consultado. Como a atividade deriva de lá, também há um Result argumento de AsyncCodeActivity<IEnumerable<Order>> saída que é usado para retornar os resultados da consulta. A BeginExecute substituição cria uma consulta LINQ que seleciona todos os pedidos do cliente especificado. Essa consulta é especificada como a UserState do passado AsyncCodeActivityContexte, em seguida, o método da BeginExecute consulta é chamado. Observe que o retorno de chamada e o estado que são passados para a consulta são aqueles que são passados BeginExecute para o método da BeginExecute atividade. Quando a execução da consulta terminar, o método da EndExecute atividade será invocado. A consulta é recuperada do e, em UserStateseguida, o método da EndExecute consulta é chamado. Este método retorna um IEnumerable<T> do tipo de entidade especificado, neste caso Order. Uma vez que IEnumerable<Order> é o tipo genérico do AsyncCodeActivity<TResult>, este IEnumerable é definido como o Result OutArgument<T> da atividade.

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

No exemplo a seguir, a OrdersByCustomer atividade recupera uma lista de pedidos para o cliente especificado e, em seguida, uma ForEach<T> atividade enumera os pedidos retornados e grava a data de cada pedido no console.

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

Quando esse fluxo de trabalho é invocado, os seguintes dados são gravados no console:

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

Nota

Se uma conexão com o servidor OData não puder ser estabelecida, você obterá uma exceção semelhante à seguinte exceção:

Exceção não tratada: System.InvalidOperationException: Ocorreu um erro durante o processamento desta solicitação. >--- System.Net.WebException: Não é possível conectar-se ao servidor remoto ---> System.Net.Sockets.SocketException: uma tentativa de conexão falhou porque a parte conectada não respondeu corretamente após um período de tempo ou a conexão estabelecida falhou porque o host conectado não respondeu.

Se for necessário algum processamento adicional dos dados retornados pela consulta, ele poderá ser feito na substituição da EndExecute atividade. Ambos BeginExecute e EndExecute são invocados usando o thread de fluxo de trabalho, e qualquer código nessas substituições não é executado de forma assíncrona. Se o processamento adicional for extenso ou de longa execução, ou se os resultados da consulta forem paginados, você deve considerar a abordagem discutida na próxima seção, que usa um delegado para executar a consulta e executar o processamento adicional de forma assíncrona.

Usando um delegado

Além de invocar o método assíncrono de uma classe do .NET Framework, uma AsyncCodeActivityatividade baseada em -based também pode definir a lógica assíncrona em um de seus métodos. Esse método é especificado usando um delegado na substituição da BeginExecute atividade. Quando o método retorna, o tempo de execução invoca a substituição da EndExecute atividade. Ao chamar um serviço OData de um fluxo de trabalho, esse método pode ser usado para consultar o serviço e fornecer qualquer processamento adicional.

No exemplo a seguir, uma ListCustomers atividade é definida. Essa atividade consulta o serviço de dados Northwind de exemplo e retorna um List<Customer> que contém todos os clientes no banco de dados Northwind. O trabalho assíncrono GetCustomers é realizado pelo método. Esse método consulta o serviço para todos os clientes e, em seguida, copia-os para um List<Customer>arquivo . Em seguida, verifica se os resultados estão paginados. Em caso afirmativo, ele consulta o serviço para a próxima página de resultados, adiciona-os à lista e continua até que todos os dados do cliente tenham sido recuperados.

Nota

Para obter mais informações sobre paginação no WCF Data Services, consulte Como carregar resultados paginados (WCF Data Services).

Depois que todos os clientes forem adicionados, a lista será retornada. O GetCustomers método é especificado na substituição da BeginExecute atividade. Como o método tem um valor de retorno, um Func<string, List<Customer>> é criado para especificar o método.

Nota

Se o método que executa o trabalho assíncrono não tiver um valor de retorno, um Action será usado em vez de um Func<TResult>. Para obter exemplos de criação de um exemplo assíncrono usando ambas as abordagens, consulte Criando atividades assíncronas.

Isso Func<TResult> é atribuído ao UserState, e então BeginInvoke é chamado. Como o método a ser invocado não tem acesso ao ambiente de argumentos da atividade, o valor do argumento é passado como o primeiro parâmetro, juntamente com o retorno de ServiceUri chamada e o estado que foram passados para BeginExecute. Quando GetCustomers retorna, o tempo de execução invoca EndExecute. O código em EndExecute recupera o delegado do UserState, chama EndInvokee retorna o resultado, que é a lista de clientes retornados do GetCustomers método.

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

No exemplo a seguir, a ListCustomers atividade recupera uma lista de clientes e, em seguida, uma ForEach<T> atividade os enumera e grava o nome da empresa e o nome de contato de cada cliente no console.

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

Quando esse fluxo de trabalho é invocado, os dados a seguir são gravados no console. Como essa consulta retorna muitos clientes, apenas parte da saída é exibida aqui.

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

Consumindo um feed OData sem usar as bibliotecas de cliente

OData expõe dados como recursos que são endereçáveis por URIs. Quando você usa as bibliotecas de cliente, esses URIs são criados para você, mas você não precisa usar as bibliotecas de cliente. Se desejado, os serviços OData podem ser acessados diretamente sem usar as bibliotecas de cliente. Quando não estiver usando as bibliotecas de cliente, o local do serviço e os dados desejados são especificados pelo URI e os resultados são retornados na resposta à solicitação HTTP. Esses dados brutos podem então ser processados ou manipulados da maneira desejada. Uma maneira de recuperar os resultados de uma consulta OData é usando a WebClient classe. Neste exemplo, o nome de contato do cliente representado pela chave ALFKI é recuperado.

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

Quando esse código é executado, a seguinte saída é exibida no console:

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>

Em um fluxo de trabalho, o código deste exemplo pode ser incorporado à Execute substituição de uma CodeActivityatividade personalizada baseada em ativos, mas a mesma funcionalidade também pode ser realizada usando a InvokeMethod<TResult> atividade. A InvokeMethod<TResult> atividade permite que os autores do fluxo de trabalho invoquem métodos estáticos e de instância de uma classe e também tem uma opção para invocar o método especificado de forma assíncrona. No exemplo a seguir, uma InvokeMethod<TResult> atividade é configurada para chamar o DownloadString método da WebClient classe e retornar uma lista de clientes.

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> pode chamar métodos estáticos e de instância de uma classe. Como DownloadString é um método de instância da WebClient classe, uma nova instância da WebClient classe é especificada para o TargetObject. DownloadString é especificado como , MethodNameo URI que contém a consulta é especificado na Parameters coleção e o valor de retorno é atribuído ao Result valor. O RunAsynchronously valor é definido como true, o que significa que a invocação do método será executada de forma assíncrona em relação ao fluxo de trabalho. No exemplo a seguir, é construído um fluxo de trabalho que usa a InvokeMethod<TResult> atividade para consultar o serviço de dados Northwind de exemplo para obter uma lista de pedidos para um cliente específico e, em seguida, os dados retornados são gravados no console.

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

Quando esse fluxo de trabalho é invocado, a saída a seguir é exibida no console. Como essa consulta retorna várias ordens, apenas parte da saída é exibida aqui.

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

Este exemplo fornece um método que os autores de aplicativos de fluxo de trabalho podem usar para consumir os dados brutos retornados de um serviço OData. Para obter mais informações sobre como acessar o WCF Data Services usando URIs, consulte Acessando recursos de serviço de dados (WCF Data Services) e OData: convenções de URI.