Compartilhar via


Consumindo feeds OData de um fluxo de trabalho

O WCF Data Services é um componente do .NET Framework que permite que você crie serviços que usam o protocolo aberto de dados (OData) para exibir e receber dados sobre a Web ou intranet usando a semântica de transferência representacional de estado (RESTO). OData expõem dados como recursos que são endereçáveis por URIs. Qualquer aplicativo pode interagir com um serviço de dados com base OData- se pode enviar uma solicitação HTTP e processar o avanço de OData que um serviço de dados retorna. Além disso, o WCF Data Services inclui bibliotecas de cliente que fornecem uma experiência mais rica de programação quando você consome feeds OData de aplicativos do .NET Framework. Este tópico fornece uma visão geral de consumir um avanço de OData em um fluxo de trabalho com e sem o uso das bibliotecas de cliente.

Usando o serviço OData de exemplo Northwind

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

Consumir um feed OData usando as bibliotecas de cliente

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

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

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

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

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

Usando métodos assíncronos

Para corrigir os problemas possíveis de latência que podem ocorrer ao acessar recursos sobre a Web, nós seja recomendável acessar WCF Services Data de forma assíncrona. As bibliotecas de clientes do WCF Data Services incluem métodos assíncronos para invocar consultas, e o WF (Windows Workflow Foundation) fornece a classe AsyncCodeActivity para criar atividades assíncronas. Atividades derivadas de AsyncCodeActivity podem ser escritas para tirar proveito das classes do .NET Framework que possuem métodos assíncronos, ou o código a ser executado de maneira assíncrona pode ser colocado em um método e ser invocado usando um delegado. Esta seção fornece dois exemplos de uma atividade derivada AsyncCodeActivity ; um que usa os métodos assíncronos das bibliotecas de cliente de Data WCF Services e um que usa um representante.

Usando métodos assíncronos de biblioteca de clientes

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

No exemplo a seguir, uma atividade de OrdersByCustomer é definida que possui dois argumentos conectados. O argumento de CustomerId representa o cliente que identifica qual as regras para retornar, e o argumento de ServiceUri representa o URI de serviço de OData a ser consultado. Porque a atividade deriva de AsyncCodeActivity<IEnumerable<Order>> há também um argumento de saída de Result que é usado para retornar os resultados da consulta. A substituição de BeginExecute cria uma consulta LINQ que selecionar todos os pedidos do cliente especificado. Esta consulta é especificado como UserState de AsyncCodeActivityContextpassado, e o método de BeginExecute de consulta é então chamado. Observe que o retorno de chamada e indica que é passado em BeginExecute de consulta são aqueles que são passados ao método de BeginExecute de atividade. Quando a consulta terminou de executar, o método de EndExecute da atividade é chamado. A consulta é recuperada de UserState, e o método de EndExecute de consulta é então chamado. Esse método retorna IEnumerable<T> do tipo de entidade especificado; nesse caso Order. Como IEnumerable<Order> é o tipo genérico do AsyncCodeActivity<TResult>, esse 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 atividade de OrdersByCustomer recuperar uma lista de pedidos para o cliente especificado, e então uma atividade de ForEach<T> 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 é chamado, 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

Observação

Se uma conexão com o servidor de OData não pode ser estabelecida, você receberá uma exceção similar à seguinte exceção:

Exceção sem tratamento: System.InvalidOperationException: Ocorreu um erro ao processar a solicitação. ---> System.Net.WebException: Não é possível se conectar ao servidor remoto ---> System.Net.Sockets.SocketException: falha em uma tentativa de conexão porque a parte conectada não respondeu corretamente após um período ou houve falha na conexão estabelecida devido a uma falha na resposta do host conectado.

Se qualquer processamento adicional dos dados retornados pela consulta é necessário, pode ser feito em uma substituição de EndExecute de atividade. O BeginExecute e o EndExecute são chamados usando o segmento de fluxo de trabalho, e qualquer código nessas alternativas não funciona de forma assíncrona. Se o processamento adicional é abrangente ou longo, ou os resultados da consulta são paginados, você deve considerar a abordagem discutido na seção a seguir, que usa um representante para executar a consulta e executar 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 atividade baseada em AsyncCodeActivity também pode definir a lógica assíncrona em um de seus métodos. Este método é especificado usando um delegado em uma substituição de BeginExecute de atividade. Quando o método retorna, o runtime chama a substituição de EndExecute de atividade. Para chamar um serviço de OData de um fluxo de trabalho, esse método pode ser usado para ver o serviço e fornecer qualquer processamento adicional.

No exemplo a seguir, uma atividade de ListCustomers é definida. Esta atividade consulta o serviço de dados de exemplo Northwind e retorna List<Customer> que contém todos os clientes na base de dados Northwind. Assíncrono o trabalho é executado pelo método de GetCustomers . Este método consulta o serviço para todos os clientes, e copiar em List<Customer>. Então verifica para ver se os resultados são paginados. Em caso afirmativo, consulte o serviço para a próxima página de resultados, adicioná-los à lista, e continua-o até que todos os dados do cliente sejam recuperados.

Observação

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 são adicionados, a lista será retornada. O método de GetCustomers é especificado em uma substituição de BeginExecute de atividade. Desde que o método possui um valor de retorno, Func<string, List<Customer>> é criado para especificar o método.

Observação

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

Este Func<TResult> é atribuído a UserState, e BeginInvoke é então chamado. Desde que o método a ser chamado não tem acesso ao ambiente de atividade de argumentos, o valor do argumento de ServiceUri é passado como o primeiro parâmetro, juntamente com o retorno de chamada e indica que foram passados em BeginExecute. Quando GetCustomers retorna, o runtime chama EndExecute. O código em EndExecute recupera o representante de UserState, chama EndInvoke, e retorna o resultado, que é a lista de clientes retornados pelo método de GetCustomers .

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 atividade de ListCustomers recuperar uma lista de clientes, e então uma atividade de ForEach<T> enumera-os e escreve-ao nome do nome da empresa e de contatos 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 é chamado, os seguintes dados são gravados no console. Como esta consulta retorna vários clientes, somente a parte de saída é mostrada 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
...

Consumir um feed OData sem usar as bibliotecas de clientes

OData expõem dados como recursos que são endereçáveis por URIs. Quando você usa bibliotecas de cliente essas URIs são criados para você, mas você não tem que usar as bibliotecas de cliente. Se desejado, serviços de OData podem ser acessados diretamente sem usar as bibliotecas de cliente. Para não usar as bibliotecas de cliente o local do serviço e os dados desejados são especificados por um URI e os resultados são retornados em resposta a solicitação HTTP. Esses dados brutos podem ser processados ou manipulado na forma desejada. Uma maneira para recuperar os resultados de uma consulta de OData é usando a classe de WebClient . Nesse exemplo, o nome de contato para o 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 saída a seguir são exibidas 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 inserido na substituição de Execute de uma atividade personalizado com base em CodeActivity, mas a mesma funcionalidade também pode ser realizada usando a atividade de InvokeMethod<TResult> . A atividade de InvokeMethod<TResult> permite que autores de fluxo de trabalho para chamar métodos estáticos e de instância de uma classe, e também tem uma opção chamar o método de forma assíncrona especificado. No exemplo a seguir, uma atividade de InvokeMethod<TResult> é configurada para chamar o método de DownloadString da classe de WebClient 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. Desde que DownloadString é um método de instância da classe de WebClient , uma nova instância da classe WebClient é especificada para TargetObject. DownloadString é especificado como MethodName, o URI que contém a consulta é especificado na coleção de Parameters , e o valor de retorno é atribuído ao valor de Result . O valor de RunAsynchronously é definido como true, o que significa que a chamada ao método executará de forma assíncrona com relação ao fluxo de trabalho. No exemplo a seguir, um fluxo de trabalho é construído que usa a atividade de InvokeMethod<TResult> para ver o serviço de dados de exemplo Northwind para uma lista de pedidos para um cliente específico, e 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 é chamado, a seguinte saída é exibida no console. Como esta consulta retorna vários pedidos, somente a parte de saída é mostrada 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 aplicativo de fluxo de trabalho podem usar para receber os dados brutos retornados de um serviço de OData. Para saber mais sobre como acessar o WCF Data Services usando URIs, consulte Acessando recursos do serviço de dados (WCF Data Services) e OData: convenções de URI.