Share via


Introdução de um desenvolvedor ao Windows Communication Foundation 4

Aaron Skonnard

Original: novembro de 2009

Atualizado para RTM: abril de 2010

Visão geral

O .NET 4 vem com alguns novos recursos atraentes e melhorias bem-vindas na área do WCF (Windows Communication Foundation). Esses aprimoramentos do WCF se concentram principalmente em simplificar a experiência do desenvolvedor, habilitar mais cenários de comunicação e fornecer uma integração avançada com o WF (Windows Workflow Foundation), tornando os "serviços de fluxo de trabalho" um cidadão de primeira classe avançando.

A boa notícia é que a maioria das alterações do WCF 4 se concentra em facilitar os cenários comuns de hoje e tornar possíveis novos cenários de comunicação e estilos de desenvolvimento. Como resultado, mover suas soluções existentes do WCF para o .NET 4 será bastante simples em termos de migração. Em seguida, você simplesmente decide quais recursos do WCF 4 você deseja aproveitar em suas soluções daqui para frente. O restante deste artigo apresenta cada uma das novas áreas de recursos do WCF 4 e demonstra como elas funcionam.

Novidades no WCF 4

O WCF 4 vem com uma ampla variedade de recursos específicos, mas a Figura 1 descreve a main áreas de recursos nas quais nos concentraremos em todo o artigo abaixo. Essas áreas de recursos resumem a maioria das novidades no WCF 4 e destacam as oportunidades de nível superior oferecidas por esta versão do .NET Framework.

Figura 1: Áreas de recursos do WCF 4

Área de recurso Descrição

Configuração simplificada

Simplificação da seção de configuração do WCF por meio do suporte para pontos de extremidade padrão, configurações de associação e comportamento. Essas alterações possibilitam hospedar serviços sem configuração, simplificando muito a experiência do desenvolvedor para os cenários mais comuns do WCF.

Descoberta

Novo suporte de estrutura para comportamentos de descoberta de serviço gerenciado e ad hoc, que estão em conformidade com o protocolo de WS-Discovery padrão.

Serviço de roteamento

Novo suporte de estrutura para um serviço de roteamento configurável que você pode usar em suas soluções do WCF. Fornece recursos para roteamento baseado em conteúdo, ponte de protocolo e tratamento de erros.

Aprimoramentos do REST

Aprimoramentos nos Serviços WebHttp do WCF com alguns recursos e ferramentas adicionais que simplificam o desenvolvimento do serviço REST.

Serviços de fluxo de trabalho

Suporte à estrutura avançada para integrar o WCF ao WF para implementar serviços declarativos de fluxo de trabalho de longa execução. Esse novo modelo de programação oferece o melhor que ambas as estruturas têm a oferecer (WCF & WF).

Quando terminarmos de cobrir essas áreas de recursos main, discutiremos brevemente alguns dos recursos mais avançados do WCF de nível inferior que vêm com o .NET 4, incluindo itens como recursos aprimorados de resolução de tipos, suporte para filas com consumidores concorrentes ("contexto de recebimento"), suporte para dados binários não empacotados por meio de um codificador de fluxo de bytes e suporte para rastreamento baseado em ETW de alto desempenho.

Quando terminarmos, você verá que o WCF 4 se torna mais fácil de usar e fornece mais suporte interno para alguns dos cenários e estilos de desenvolvimento mais comuns de hoje.

Configuração simplificada

O modelo de programação unificado oferecido pelo WCF no 3.x é uma bênção e uma maldição – simplifica a lógica de serviço de escrita para uma variedade de cenários de comunicação diferentes, no entanto, também aumenta a complexidade no lado da configuração das coisas, pois fornece tantas opções de comunicação subjacentes diferentes que você é forçado a entender antes de começar.

A realidade é que a configuração do WCF geralmente se torna a área mais cara de usar o WCF na prática hoje e grande parte dessa complexidade chega à equipe de TI/operações que não está preparada para lidar com isso.

Dada essa realidade, quando você considera a complexidade da rede de usar o WCF 3.x, pode-se razoavelmente concluir que é mais difícil de usar do que seu antecessor ASP.NET serviços Web (ASMX). Com o ASMX, você conseguiu definir uma operação [WebMethod] e o runtime forneceu automaticamente uma configuração padrão para as comunicações subjacentes. Ao migrar para o WCF 3.x, por outro lado, os desenvolvedores precisam saber o suficiente sobre as várias opções de configuração do WCF para definir pelo menos um ponto de extremidade. E o número assustador de opções de configuração geralmente assusta alguns desenvolvedores.

Em um esforço para tornar a experiência geral do WCF tão fácil quanto o ASMX, o WCF 4 vem com um novo modelo de "configuração padrão" que elimina completamente a necessidade de qualquer configuração do WCF. Se você não fornecer nenhuma configuração do WCF para um serviço específico, o runtime do WCF 4 configurará automaticamente seu serviço com alguns pontos de extremidade padrão e configurações de associação/comportamento padrão. Isso torna muito mais fácil colocar um serviço WCF em funcionamento, especialmente para aqueles que não estão familiarizados com as várias opções de configuração do WCF e estão felizes em aceitar os padrões, pelo menos para começar.

Pontos de extremidade padrão

Com o WCF 3.x, se você tentar hospedar um serviço sem pontos de extremidade configurados, a instância do ServiceHost lançará uma exceção informando que você precisa configurar pelo menos um ponto de extremidade. Com o WCF 4, esse não é mais o caso porque o runtime adiciona automaticamente um ou mais "pontos de extremidade padrão" para você, tornando o serviço utilizável sem nenhuma configuração.

Veja como ele funciona. Quando o aplicativo host chama Open na instância do ServiceHost, ele compila a descrição do serviço interno do arquivo de configuração do aplicativo junto com qualquer coisa que o aplicativo host possa ter configurado explicitamente e, se o número de pontos de extremidade configurados ainda for zero, ele chamará AddDefaultEndpoints, um novo método público encontrado na classe ServiceHost. Esse método adiciona um ou mais pontos de extremidade à descrição do serviço com base nos endereços base do serviço (em cenários do IIS, esse é o endereço .svc). Como o método é público, você também pode chamá-lo diretamente em cenários de hospedagem personalizados.

Para ser preciso, a implementação de AddDefaultEndpoints adiciona um ponto de extremidade padrão por endereço base para cada contrato de serviço implementado pelo serviço. Por exemplo, se o serviço implementar dois contratos de serviço e você configurar o host com um único endereço base, AddDefaultEndpoints configurará o serviço com dois pontos de extremidade padrão (um para cada contrato de serviço). No entanto, se o serviço implementar dois contratos de serviço e o host estiver configurado com dois endereços base (um para HTTP e outro para TCP), AddDefaultEndpoints configurará o serviço com quatro pontos de extremidade padrão.

Vamos dar uma olhada em um exemplo completo para ilustrar se isso funciona. Suponha que você tenha os seguintes contratos de serviço WCF e a seguinte implementação de serviço:

[ServiceContract]

interface pública IHello

{

    [OperationContract]

    void SayHello(string name);

}

[ServiceContract]

interface pública IGoodbye

{

    [OperationContract]

    void SayGoodbye(string name);

}

classe pública GreetingService : serviço IHello, IGoodbye // implementa ambos os contratos

{

    public void SayHello(string name)

    {

        Console.WriteLine("Hello {0}", name);

    }

    public void SayGoodbye(string name)

    {

        Console.WriteLine("Goodbye {0}", name);

    }

}

Com o WCF 4, agora você pode usar ServiceHost para hospedar o serviço GreetingService sem nenhuma configuração de aplicativo. Ao usar o ServiceHost em cenários de hospedagem personalizados, você precisará especificar um ou mais endereços base a serem usados. O seguinte mostra como hospedar o GreetingService em um aplicativo de console e, novamente, você pode supor que não há nenhum arquivo app.config associado a este programa:

programa de classe

{

    static void Main(string[] args)

    {

        O host é configurado com dois endereços base, um para HTTP e outro para TCP

        Host ServiceHost = novo ServiceHost(typeof(GreetingService),

            new Uri("https://localhost:8080/greeting"),

            new Uri("net.tcp://localhost:8081/greeting"));

        Host. Open();

        foreach (ServiceEndpoint se no host. Description.Endpoints)

            Console.WriteLine("A: {0}, B: {1}, C: {2}",

                se. Endereço, se.Binding.Name, se.Contract.Name);

        Console.WriteLine("Pressione <Enter> para interromper o serviço.");

        Console.ReadLine();

        Host. Close();

    }

}

Este exemplo configura o ServiceHost com dois endereços base: um para HTTP e outro para TCP. Ao executar este programa, você verá quatro pontos de extremidade impressos na janela do console, conforme ilustrado na Figura 2. Você obterá dois para o endereço base HTTP, um por contrato e dois para o endereço base TCP, novamente um por contrato. Tudo isso é fornecido nos bastidores pela instância do ServiceHost.

Figura 2: Pontos de extremidade padrão exibidos na janela do console

Observe como o WCF opta por usar o BasicHttpBinding para os pontos de extremidade HTTP padrão e o NetTcpBinding para os pontos de extremidade TCP padrão. Mostrarei como alterar esses padrões em breve.

Lembre-se de que esse comportamento de ponto de extremidade padrão só é iniciado quando o serviço não foi configurado com nenhum ponto de extremidade. Se eu alterar o aplicativo de console para configurar o serviço com pelo menos um ponto de extremidade, você não verá mais nenhum desses pontos de extremidade padrão aparecer na saída. Para ilustrar isso, simplesmente adicionarei a seguinte linha de código que chama AddServiceEndpoint depois de construir a instância do ServiceHost:

...

Host ServiceHost = novo ServiceHost(typeof(GreetingService),

    new Uri("https://localhost:8080/greeting"),

    new Uri("net.tcp://localhost:8081/greeting"));

Host. AddServiceEndpoint(typeof(IHello), new WSHttpBinding(), "myendpoint");

...

Se você executar o aplicativo de console com essa linha de código inserida, observará que apenas um único ponto de extremidade agora aparece na saída – aquela que configuramos manualmente no código acima (consulte a Figura 3).

Figura 3: Saída do console depois de configurar o host com um único ponto de extremidade

No entanto, você sempre poderá chamar AddDefaultEndpoints por conta própria se ainda quiser adicionar o conjunto de pontos de extremidade padrão junto com o seu próprio. O exemplo de código a seguir ilustra como fazer isso:

...

Host ServiceHost = novo ServiceHost(typeof(GreetingService),

    new Uri("https://localhost:8080/greeting"),

    new Uri("net.tcp://localhost:8081/greeting"));

Host. AddServiceEndpoint(typeof(IHello), new WSHttpBinding(), "myendpoint");

Host. AddDefaultEndpoints();

...

Se você executar o aplicativo de console novamente com essa alteração, verá cinco pontos de extremidade exibidos na janela do console – aquele que configurei manualmente junto com os quatro pontos de extremidade padrão (consulte a Figura 4).

Figura 4: Saída do console após chamar AddDefaultEndpoints manualmente

Agora que entendemos o algoritmo e a mecânica para adicionar pontos de extremidade padrão aos serviços em runtime, a próxima pergunta é como o WCF decide qual associação usar para um endereço baseado em particular?

Mapeamento de protocolo padrão

A resposta para essa pergunta é simples. O WCF define um mapeamento de protocolo padrão entre esquemas de protocolo de transporte (por exemplo, http, net.tcp, net.pipe etc.) e as associações internas do WCF. O mapeamento de protocolo padrão é encontrado no arquivo .NET 4 machine.config.comments e tem esta aparência:

<system.serviceModel>

   <protocolMapping>

      <add scheme="http" binding="basicHttpBinding" bindingConfiguration="" />

      <add scheme="net.tcp" binding="netTcpBinding" bindingConfiguration=""/>

      <add scheme="net.pipe" binding="netNamedPipeBinding" bindingConfiguration=""/>

      <add scheme="net.msmq" binding="netMsmqBinding" bindingConfiguration=""/>

   </protocolMapping>

   ...

Você pode substituir esses mapeamentos no nível do computador adicionando esta seção a machine.config e modificando o mapeamento para cada esquema de protocolo. Ou, se você quiser substituí-lo somente dentro do escopo de um aplicativo, poderá substituir esta seção no arquivo de configuração do aplicativo/web.

Por exemplo, se sua organização estiver focada principalmente na criação de serviços RESTful com o WCF, talvez faça sentido alterar a associação padrão para o esquema de protocolo "http" para o WebHttpBinding. O exemplo a seguir ilustra como fazer isso em um arquivo de configuração de aplicativo:

<configuração>

  <system.serviceModel>

    <protocolMapping>

      <add scheme="http" binding="webHttpBinding"/>

    </protocolMapping>

  </system.serviceModel>

</Configuração>

Agora, se eu executar novamente o aplicativo de console mostrado anteriormente com esse app.config em vigor, os dois pontos de extremidade padrão baseados em HTTP agora mostrarão que estão usando o WebHttpBinding (consulte a Figura 5).

Figura 5: Saída do console após substituir o mapeamento de protocolo HTTP padrão

Depois que o WCF determina qual associação usar por meio da tabela de mapeamento de protocolo, ele usa a configuração de associação padrão ao configurar o ponto de extremidade padrão. Se você não estiver satisfeito com os padrões de associação internos, também poderá substituir a configuração padrão para uma associação específica.

Configurações de associação padrão

Cada associação do WCF vem com uma configuração padrão que é usada, a menos que seja explicitamente substituída pelo aplicativo host para um ponto de extremidade específico. Cada instância de associação que você usa sempre vem com os padrões internos, a menos que você opte por substituir aplicando uma configuração de associação explícita.

No WCF 3.x, você faz isso definindo uma configuração de associação nomeada que pode ser aplicada a definições de ponto de extremidade por meio do atributo bindingConfiguration. A mecânica de fazer isso corretamente é complicada e propensa a erros.  O arquivo de configuração a seguir mostra um exemplo típico:

<configuração>

  <system.serviceModel>

    <bindings>

      <basicHttpBinding>

        <binding name="BasicWithMtom" messageEncoding="Mtom"/>

      </basicHttpBinding>

    </Ligações>

    <services>

      <service name="GreetingService">

        <endpoint address="mtom" binding="basicHttpBinding"

                  bindingConfiguration="BasicWithMtom"

                  contract="IHello"/>

      </service>

    </Serviços>

  </system.serviceModel>

</Configuração>

No exemplo acima, a configuração de associação "BasicWithMtom" substitui os padrões para BasicHttpBinding alterando a codificação de mensagem para MTOM. No entanto, essa configuração de associação só entra em vigor quando você a aplica a um ponto de extremidade específico por meio do atributo "bindingConfiguration". Essa é a etapa que geralmente ilude a equipe de desenvolvedores e operações, causando problemas de configuração.

Com o WCF 4, agora você pode definir configurações de associação padrão simplesmente omitindo o nome da configuração de associação ao definir a nova configuração. Em seguida, o WCF usará essa configuração padrão para todos os pontos de extremidade que usam essa associação que não têm uma configuração de associação explícita definida neles.

Por exemplo, se adicionarmos o seguinte arquivo app.config ao aplicativo de console mostrado anteriormente, os dois pontos de extremidade HTTP padrão selecionarão essa configuração Padrão BasicHttpBinding, que habilita o MTOM:

<configuração>

  <system.serviceModel>

    <bindings>

      <basicHttpBinding>

        <binding messageEncoding="Mtom"/><-- observe que não há nenhum atributo de nome -->

      </basicHttpBinding>

    </Ligações>

  </system.serviceModel>

</Configuração>

É claro que você também pode adicionar essas configurações de associação padrão a machine.config se quiser que elas entrem em vigor em todos os serviços em execução no computador ou defina-as em um aplicativo por aplicativo adicionando as configurações de associação padrão no arquivo de configuração do aplicativo.

Esse recurso fornece um mecanismo simples para definir um conjunto padrão de padrões de associação que você pode usar em todos os seus serviços sem impor as complexidades das configurações de associação a outros desenvolvedores ou à equipe de TI/operações. Eles podem simplesmente escolher a associação apropriada e ter certeza de que a configuração padrão adequada será fornecida pelo ambiente de hospedagem.

Além das configurações de associação padrão, a outra coisa a ser considerada para seus serviços e pontos de extremidade é qual deve ser a configuração de comportamento padrão.

Configurações de comportamento padrão

O WCF 4 também possibilita definir configurações de comportamento padrão para serviços e pontos de extremidade, o que pode simplificar as coisas quando você deseja compartilhar uma configuração de comportamento padrão em todos os serviços ou pontos de extremidade em execução em um computador ou em uma solução.

No WCF 3.x, você precisa definir configurações de comportamento nomeadas que você aplica explicitamente a serviços e pontos de extremidade por meio do atributo "behaviorConfiguration". Com o WCF 4, você pode definir configurações de comportamento padrão omitindo o nome na definição de configuração. Se você adicionar esses comportamentos padrão a machine.config, eles serão aplicados a todos os serviços ou pontos de extremidade hospedados no computador. Se você adicioná-los a app.config, eles só entrarão em vigor dentro do escopo do aplicativo host. Aqui está um exemplo:

<configuração>

  <system.serviceModel>

    <behaviors>

      <serviceBehaviors>

        <comportamento><-- não observe nenhum atributo de nome -->

          <serviceMetadata httpGetEnabled="true"/>

        </Comportamento>

      </Servicebehaviors>

    </Comportamentos>

  </system.serviceModel>

</Configuração>

Este exemplo ativa os metadados de serviço para qualquer serviço que não venha com uma configuração de comportamento explícita. Se adicionarmos essa configuração de comportamento padrão ao arquivo app.config para o aplicativo de console mostrar anteriormente e executarmos o aplicativo novamente, poderemos navegar até o endereço HTTP base para recuperar a página de ajuda do serviço e a definição do WSDL do serviço (consulte a Figura 6).

Figura 6: Navegando até os metadados de serviço habilitados pela configuração de comportamento padrão

Outro novo recurso no WCF 4 é que as configurações de comportamento agora dão suporte a um modelo de herança. Se um aplicativo definir uma configuração de comportamento usando o mesmo nome que um já definido em machine.config, a configuração de comportamento específica do aplicativo será mesclada com a configuração de todo o computador, adicionando comportamentos adicionais à configuração de comportamento composto derivado.

Pontos de extremidade padrão

Relacionado aos pontos de extremidade padrão está outro novo recurso do WCF 4 conhecido como "pontos de extremidade padrão". Você pode pensar em um ponto de extremidade padrão como uma definição de ponto de extremidade pré-configurada comum incorporada à estrutura do WCF 4 que você pode simplesmente usar. Os pontos de extremidade padrão definem uma configuração de ponto de extremidade "padrão" que normalmente não é alterada, embora você possa, se precisar, como verá em breve.

A Figura 7 descreve os pontos de extremidade padrão fornecidos com o WCF 4. Elas fornecem definições de ponto de extremidade padrão para alguns dos recursos e cenários de comunicação mais comuns do WCF 4. Por exemplo, no caso de um ponto de extremidade MEX, você sempre precisará especificar IMetadataExchange para o contrato de serviço e provavelmente escolherá HTTP. Portanto, em vez de forçar você a sempre fazer isso manualmente, o WCF fornece uma definição de ponto de extremidade padrão para a troca de metdata chamada "mexEndpoint" que é fácil de usar.

Figura 7: Pontos de extremidade padrão no WCF 4

Nome do ponto de extremidade padrão Descrição

mexEndpoint

Define um ponto de extremidade padrão para MEX configurado com IMetadataExchange para o contrato de serviço, mexHttpBinding como a associação padrão (você pode alterar isso) e um endereço vazio.

dynamicEndpoint

Define um ponto de extremidade padrão configurado para usar a Descoberta do WCF em um aplicativo cliente WCF. Ao usar esse ponto de extremidade padrão, um endereço não é necessário porque, durante a primeira chamada, o cliente consultará um ponto de extremidade de serviço que corresponda ao contrato especificado e se conectará automaticamente a ele para você. Por padrão, a consulta de descoberta é enviada por UDP multicast, mas você pode especificar a associação de descoberta e os critérios de pesquisa a serem usados quando necessário.

discoveryEndpoint

Define um ponto de extremidade padrão pré-configurado para operações de descoberta em um aplicativo cliente. O usuário precisa especificar o endereço e a associação ao usar esse ponto de extremidade padrão.

udpDiscoveryEndpoint

Define um ponto de extremidade padrão pré-configurado para operações de descoberta em um aplicativo cliente usando a associação UDP em um endereço multicast. Deriva de DiscoveryEndpoint.

announcementEndpoint

Define um ponto de extremidade padrão pré-configurado para a funcionalidade de anúncio da descoberta. O usuário precisa especificar o endereço e a associação ao usar esse ponto de extremidade padrão.

udpAnnouncementEndpoint

Define um ponto de extremidade padrão pré-configurado para a funcionalidade de anúncio em uma associação UDP em um endereço multicast. Esse ponto de extremidade deriva de announcementEndpoint.

workflowControlEndpoint

Define um ponto de extremidade padrão para controlar a execução de instâncias de fluxo de trabalho (criar, executar, suspender, encerrar etc).

webHttpEndpoint

Define um ponto de extremidade padrão configurado com o WebHttpBinding e o WebHttpBehavior. Use para expor serviços REST.

webScriptEndpoint

Define um ponto de extremidade padrão configurado com o WebHttpBinding e o WebScriptEnablingBehavior. Use para expor os serviços do Ajax.

Você pode aproveitar qualquer um desses pontos de extremidade padrão em suas próprias configurações de serviço simplesmente referenciando-os por nome. O <elemento de ponto> de extremidade agora vem com um atributo "tipo" que você pode usar para especificar o nome de um ponto de extremidade padrão. Por exemplo, o exemplo a seguir configura o GreetingService com um ponto de extremidade MEX aproveitando a definição padrão de "mexEndpoint":

<configuração>

  <system.serviceModel>

    <services>

      <service name="GreetingService">

        <endpoint kind="basicHttpBinding" contract="IHello"/>

        <endpoint kind="mexEndpoint" address="mex" />

      </service>

    </Serviços>

  </system.serviceModel>

</Configuração>

Embora os pontos de extremidade padrão protejam você da maioria dos detalhes de configuração (por exemplo, com o mexEndpoint eu não precisei especificar a associação ou contrato), ainda pode haver momentos em que você deseja usá-los, mas precisa configurar as definições de ponto de extremidade padrão um pouco diferente. 

Quando você precisar fazer isso, poderá usar a <seção standardEndpoints> e substituir a configuração do ponto de extremidade para o ponto de extremidade padrão. Em seguida, você pode referenciar essa configuração ao definir um novo <ponto> de extremidade por meio do atributo endpointConfiguration, conforme ilustrado aqui:

<configuração>

  <system.serviceModel>

    <services>

      <service name="GreetingService">

        <endpoint binding="basicHttpBinding" contract="IHello"/>

        <endpoint kind="udpDiscoveryEndpoint" endpointConfiguration="D11"/>

      </service>

    </Serviços>

    <standardEndpoints>

      <udpDiscoveryEndpoint>

        <standardEndpoint name="D11" discoveryVersion="WSDiscovery11"/>

      </Udpdiscoveryendpoint>

    </standardEndpoints>

    <behaviors>

      <serviceBehaviors>

        <Comportamento>

          <serviceDiscovery/>

          <serviceMetadata httpGetEnabled="true"/>

        </Comportamento>

      </Servicebehaviors>

    </Comportamentos>

  </system.serviceModel>

</Configuração>

Este exemplo altera a versão de WS-Discovery padrão para o ponto de extremidade padrão chamado "udpDiscoveryEndpoint" (falaremos mais sobre a descoberta de serviço em breve).

Simplificando a hospedagem do IIS/ASP.NET

Considerando esses novos recursos para pontos de extremidade padrão, configurações de associação padrão e configurações de comportamento padrão, a hospedagem no IIS/ASP.NET torna-se muito mais fácil no WCF 4. ASP.NET desenvolvedores que estão acostumados a trabalhar com serviços ASMX agora podem definir serviços WCF que são tão simples quanto por natureza.

Na verdade, marcar o quão simples é a seguinte definição de serviço WCF:

<-- HelloWorld.svc -->

<%@ ServiceHost Language="C#" Debug="true" Service="HelloWorldService

    CodeBehind="~/App_Code/HelloWorldService.cs" %>

[ServiceContract]

classe pública HelloWorldService

{

    [OperationContract]

    cadeia de caracteres pública HelloWorld()

    {

        retornar "olá, mundo";

    }

}

Essa é a forma mais simples de definição de serviço do WCF porque não estamos usando uma definição de interface separada para definir o contrato de serviço e tudo é definido em um arquivo, HelloWorld.svc (observação: não estou recomendando essa abordagem, apenas observando que é possível desenhar uma comparação com o ASMX). Isso deve parecer muito com os serviços TÍPICOSMX, a principal diferença são os nomes de atributo que você usa na classe de serviço (por exemplo, [WebService] e [WebMethod]). Há definitivamente menos partes móveis.

Com os novos recursos do WCF 4 descritos na seção anterior, agora você pode navegar até HelloWorld.svc sem nenhuma configuração adicional do WCF e a lógica de ativação do WCF criará a instância do ServiceHost nos bastidores e a configurará com um único ponto de extremidade HTTP padrão. E se você adicionou um comportamento de serviço padrão ao arquivo machine.config habilitando metadados de serviço, verá a página de ajuda do WCF e o link para a definição do WSDL ao navegar até HelloWorld.svc (consulte a Figura 8).

Figura 8: página de ajuda HelloWorldService

Se você não tiver habilitado metadados de serviço em todo o computador, poderá habilitá-lo em seu aplicativo Web adicionando a seguinte configuração de comportamento padrão ao arquivo web.config:

...

<system.serviceModel>

  <behaviors>

    <serviceBehaviors>

      <comportamento><-- observe que não há nenhum atributo de nome -->

        <serviceMetadata httpGetEnabled="true"/>

      </Comportamento>

    </Servicebehaviors>

  </Comportamentos>

</system.serviceModel>

...

Você também pode alterar outras configurações padrão seguindo os procedimentos descritos nas seções anteriores. Por exemplo, você pode alterar o mapeamento de protocolo padrão, adicionar configurações de associação padrão ou configurações de comportamento padrão adicionais. Se o serviço implementar mais de um contrato de serviço, a instância do ServiceHost resultante será configurada com um ponto de extremidade HTTP por contrato.

Por exemplo, suponha que hospedamos nosso GreetingService (de antes) por meio do arquivo .svc mostrado aqui:

<-- GreetingService.svc -->

<%@ServiceHost Service="GreetingService"%>

Dada a nossa definição de GreetingService, na primeira vez que você navegar até GreetingService.svc, a lógica de ativação do WCF criará a instância do ServiceHost e adicionará dois pontos de extremidade HTTP padrão para o tipo GreetingService (um para cada contrato de serviço). Você pode verificar isso navegando até a definição do WSDL e encontrará dois <elementos de porta> dentro do elemento de <serviço> .

No geral, essas simplificações de configuração do WCF devem tornar muito mais fácil para os desenvolvedores ASP.NET colocar os serviços WCF em funcionamento em seus aplicativos Web, e isso aproxima muito o caso mais simples da experiência com a qual os desenvolvedores estavam acostumados com ASP.NET serviços Web.

Ativação sem arquivo

Embora os arquivos .svc facilitem a exposição de serviços do WCF, uma abordagem ainda mais fácil seria definir pontos de extremidade de ativação virtual em Web.config, removendo, assim, a necessidade de arquivos .svc completamente.

No WCF 4, você pode definir pontos de extremidade de ativação de serviço virtual que são mapeados para seus tipos de serviço em Web.config. Isso possibilita ativar os serviços do WCF sem a necessidade de manter arquivos .svc físicos (também conhecido como "ativação sem arquivo"). O exemplo a seguir mostra como configurar um ponto de extremidade de ativação:

<configuração>

  <system.serviceModel>

    <serviceHostingEnvironment>

      <serviceActivations>

        <add relativeAddress="Greeting.svc" service="GreetingService"/>

      </serviceActivations>

    </Servicehostingenvironment>

  </system.serviceModel>

</Configuração>

Com isso em vigor, agora é possível ativar o GreetingService usando um caminho relativo de "Greeting.svc" (em relação ao endereço base do aplicativo Web). Para ilustrar isso, criei um aplicativo IIS em meu computador chamado "GreetingSite", que atribuí ao pool de aplicativos "ASP.NET v4.0" e mapeei-o para o diretório do projeto GreetingService que contém o web.config mostrado acima. Agora, posso simplesmente navegar sem https://localhost/GreetingSite/Greeting.svc realmente ter um arquivo .svc físico no disco. A Figura 9 mostra a aparência disso no navegador.

Figura 9: Exemplo de ativação sem arquivo

Descoberta

O próximo recurso principal do WCF 4 que discutiremos é a descoberta de serviços. Em alguns ambientes especializados orientados a serviços, há serviços cujo local de runtime é dinâmico e em constante alteração. Por exemplo, considere ambientes em que diferentes tipos de dispositivos habilitados para serviço estão constantemente ingressando e deixando a rede como parte da solução de negócios geral. Lidar com essa realidade exige que os clientes descubram dinamicamente o local de runtime dos pontos de extremidade de serviço.

WS-Discovery é uma especificação OASIS que define um protocolo baseado em SOAP para descobrir dinamicamente o local dos pontos de extremidade de serviço em runtime. O protocolo permite que os clientes investiguem pontos de extremidade de serviço que correspondem a determinados critérios para recuperar uma lista de candidatos adequados. Um cliente pode escolher um ponto de extremidade específico na lista descoberta e usar seu endereço de ponto de extremidade de runtime atual.

WS-Discovery define dois modos principais de operação: modo ad hoc e modo gerenciado. No modo ad hoc, os clientes investigam serviços enviando mensagens multicast. A estrutura fornece mecanismo de multicast UDP para esse modo ad hoc. Os serviços que correspondem à investigação respondem diretamente ao cliente. Para minimizar a necessidade de sondagem do cliente, os serviços também podem "anunciar" a si mesmos ao ingressar ou sair da rede enviando uma mensagem multicast para clientes que podem estar "escutando". A descoberta ad hoc é limitada pelo protocolo usado para mensagens de multicast, no caso de UDP, somente os serviços que escutam na sub-rede local poderão receber as mensagens.

Com a descoberta de serviço gerenciado, você fornece um proxy de descoberta na rede que "gerencia" os pontos de extremidade de serviço detectáveis. Os clientes conversam diretamente com o proxy de descoberta para localizar serviços com base em critérios de investigação. O proxy de descoberta precisa de um repositório de serviços que possa corresponder à consulta. Como o proxy é preenchido com essas informações é um detalhe de implementação. Os proxies de descoberta podem ser facilmente conectados a um repositório de serviços existente, eles podem ser pré-configurados com uma lista de pontos de extremidade ou um proxy de descoberta pode até mesmo escutar anúncios para atualizar seu cache. No modo gerenciado, os comunicados podem ser unicast diretamente para um destinatário, potencialmente por um proxy de descoberta.

A estrutura do .NET 4.0 fornece as classes base necessárias para implementar seu próprio proxy de descoberta. As classes base abstraem os detalhes do protocolo de descoberta para que você possa simplesmente se concentrar na lógica que deseja que o proxy de descoberta contenha. Por exemplo, você só precisa definir o que o proxy de descoberta fará em resposta a uma mensagem de investigação, mensagens de anúncio e resolver mensagens.

O WCF 4 fornece uma implementação completa do protocolo WS-Discovery e fornece suporte para os modos de descoberta ad hoc e gerenciados. Vamos dar uma breve olhada em cada um deles abaixo.

Descoberta de Serviço Simples

A maneira mais fácil de habilitar a descoberta de serviço é por meio do modo ad hoc. O WCF facilita a habilitação da descoberta de serviço em seus aplicativos host de serviço fornecendo alguns pontos de extremidade de descoberta padrão e um comportamento de descoberta de serviço. Para configurar seu serviço para descoberta, basta adicionar o ponto de extremidade padrão "udpDiscoveryEndpoint" e habilitar o comportamento de <serviceDiscovery> no serviço.

Aqui está um exemplo completo ilustrando como fazer isso:

<configuração>

    <system.serviceModel>

      <services>

        <service name="CalculatorService">

          <endpoint binding="wsHttpBinding" contract="ICalculatorService" />

          <-- adicionar um ponto de extremidade de descoberta UDP padrão-->

          <endpoint name="udpDiscovery" kind="udpDiscoveryEndpoint"/>

        </service>

      </Serviços>

      <behaviors>

        <serviceBehaviors>

          <Comportamento>

            <serviceDiscovery/><-- habilitar o comportamento de descoberta de serviço -->

          </Comportamento>

        </Servicebehaviors>

      </Comportamentos>

    </system.serviceModel>

</Configuração>

Ao fazer isso, seu serviço se torna detectável por UDP na sub-rede local. Em seguida, os clientes podem aproveitar WS-Discovery em runtime para "descobrir" o endereço real do serviço em execução. O WCF 4 facilita para os clientes fazer isso por meio do ponto de extremidade padrão dynamicEndpoint.

Basta usar o ponto de extremidade do cliente existente que você estava usando para se conectar ao serviço, remover o endereço e adicionar uma marca kind="dynamicEndpoint".

<configuração>

    <system.serviceModel>

        <client>

          <Extremidade

              name="calculatorEndpoint"

              kind="dynamicEndpoint"

              binding="wsHttpBinding"

              contract="ICalculatorService">

          </Extremidade>

        </Cliente>

    </system.serviceModel>

</Configuração>

Quando a primeira chamada de serviço for feita, o cliente enviará uma consulta multicast em busca de serviços que correspondam ao contrato ICalculatorService e tentarão se conectar a um. Várias configurações permitem ajustar seu serach, ajustar as associações de descoberta e controlar o processo de descoberta. Você também pode fazer tudo isso programaticamente usando a classe DiscoveryClient.

O exemplo a seguir vai além mostrando como usar o UdpDiscoveryEndpoint programaticamente para descobrir um ponto de extremidade ICalculatorService e, em seguida, invocá-lo:

Criar DiscoveryClient

DiscoveryClient discoveryClient =

    new DiscoveryClient(new UdpDiscoveryEndpoint());

Localizar pontos de extremidade ICalculatorService no escopo especificado

FindCriteria findCriteria = new FindCriteria(typeof(ICalculatorService));

FindResponse findResponse = discoveryClient.Find(findCriteria);

Basta escolher o primeiro ponto de extremidade descoberto

EndpointAddress address = findResponse.Endpoints[0]. Endereço;

Criar o cliente de serviço de destino

CalculatorServiceClient client =

    new CalculatorServiceClient("calculatorEndpoint");

Conectar-se ao ponto de extremidade de serviço descoberto

Cliente. Endpoint.Address = address;

Console.WriteLine("Invocando CalculatorService em {0}", endereço);

Chame a operação Adicionar serviço.

double result = client. Add(100, 15.99);

Console.WriteLine("Add({0},{1}) = {2}", 100, 15,99, result);

Depois que o programa cliente tiver recuperado a coleção de pontos de extremidade descobertos, ele poderá usar um deles para realmente invocar o serviço de destino. A Figura 10 mostra a saída da execução do código do cliente mostrado acima, supondo que o serviço também esteja em execução ao mesmo tempo. Observação: neste exemplo, a operação Localizar no cliente de descoberta é síncrona; A descoberta também dá suporte para operações de localização assíncronas.

Figura 10: saída da execução do código do cliente de descoberta

Usando escopos ao descobrir pontos de extremidade

No exemplo anterior, o cliente simplesmente investigava serviços com base no tipo de contrato de serviço. Os clientes podem restringir os resultados da descoberta fornecendo informações de escopo adicionais ao enviar as investigações de descoberta. Vamos examinar um exemplo simples para ver como os "escopos" podem ser usados durante a descoberta.

Primeiro, o serviço precisa associar um ou mais escopos a cada ponto de extremidade que ele publicará para descoberta. O WCF 4 vem com um <comportamento endpointDiscovery> que você pode usar para definir um conjunto de escopos que você pode associar a uma definição de ponto de extremidade. O exemplo a seguir ilustra como associar dois escopos ao ponto de extremidade único definido no serviço:

<configuração>

    <system.serviceModel>

      <services>

        <service name="CalculatorService"

                 behaviorConfiguration="calculatorServiceBehavior">

          <endpoint binding="wsHttpBinding"

                    contract="ICalculatorService"

                    behaviorConfiguration="ep1Behavior" />

          <endpoint name="udpDiscovery" kind="udpDiscoveryEndpoint"/>

        </service>

      </Serviços>

      <behaviors>

        <serviceBehaviors>

          <behavior name="calculatorServiceBehavior">

            <serviceDiscovery/>

          </Comportamento>

        </Servicebehaviors>

        <endpointBehaviors>

          <behavior name="ep1Behavior">

            <endpointDiscovery>

               <-- escopos associados a esse comportamento de ponto de extremidade -->

              <scopes>

                <add scope="http://www.example.org/calculator"/>

                <add scope="ldap:///ou=engineering,o=exampleorg,c=us"/>

              </Escopos>

            </endpointDiscovery>

          </Comportamento>

        </endpointBehaviors>

      </Comportamentos>

    </system.serviceModel>

</Configuração>

Os clientes podem investigar pontos de extremidade de serviço com base em escopos específicos no runtime. Eles podem fazer isso adicionando uma lista de escopos de destino à instância findCriteria que você fornece à operação Localizar. O código a seguir ilustra como descobrir pontos de extremidade ICalculatorService correspondentes a um escopo LDAP específico:

...

Criar DiscoveryClient

DiscoveryClient discoveryClient = new DiscoveryClient("udpDiscoveryEndpoint");

Localizar pontos de extremidade ICalculatorService no escopo especificado

Escopo do URI = novo Uri("ldap:///ou=engineering,o=exampleorg,c=us");

FindCriteria findCriteria = new FindCriteria(typeof(ICalculatorService));

findCriteria.Scopes.Add(scope);

FindResponse findResponse = discoveryClient.Find(findCriteria);

...

Aproveitar escopos possibilita ajustar sua implementação de descoberta para que os clientes possam descobrir mais facilmente os pontos de extremidade de serviço específicos de interesse para eles. A descoberta também permite uma personalização adicional. Por exemplo, os serviços podem adicionar metadados XML personalizados a um ponto de extremidade. Essas informações são enviadas ao cliente em resposta à consulta do cliente.

Comunicados de Serviço

O WCF 4 também facilita a configuração de serviços para "anunciar" seus pontos de extremidade quando eles são iniciados. Isso permite que os clientes que estão "escutando" aprendam sobre novos pontos de extremidade de serviço à medida que ingressam na rede, reduzindo assim a quantidade de investigação (e mensagens multicast) executadas pelos clientes.

Você pode configurar um serviço com um ponto de extremidade de anúncio usando o <comportamento serviceDiscovery> . O <comportamento serviceDiscovery> permite que você defina uma coleção de pontos de extremidade de anúncio que serão expostos pelo serviço. Você pode usar o padrão "udpAnnouncementEndpoint" para a maioria dos casos.

Você também precisará configurar o serviço com um "udpDiscoveryEndpoint" padrão se quiser que ele responda às investigações de descoberta iniciadas pelos clientes. O exemplo a seguir mostra uma configuração típica:

<configuração>

  <system.serviceModel>

    <services>

      <service name="CalculatorService">

        <endpoint binding="wsHttpBinding" contract="ICalculatorService"/>

        <endpoint kind="udpDiscoveryEndpoint"/>

      </service>

    </Serviços>

    <behaviors>

      <serviceBehaviors>

        <Comportamento>

          <serviceDiscovery>

            <announcementEndpoints>

              <endpoint kind="udpAnnouncementEndpoint"/>

            </announcementEndpoints>

          </serviceDiscovery>

        </Comportamento>

      </Servicebehaviors>

    </Comportamentos>

  </system.serviceModel>

</Configuração>

Com essa configuração em vigor, o serviço anunciará a si mesmo quando estiver online e também anunciará quando estiver offline. Para aproveitar esses anúncios, você precisará projetar especificamente seus clientes para ouvi-los em runtime. Faça isso hospedando um serviço de anúncio no aplicativo cliente que implementa o protocolo de anúncio WS-Discovery.

O WCF 4 vem com uma classe chamada AnnouncementService projetada especificamente para essa finalidade. O AnnouncementService fornece dois manipuladores de eventos: OnlineAnnouncementReceived e OfflineAnnouncementReceived. Os aplicativos cliente podem simplesmente hospedar uma instância do AnnouncementService usando ServiceHost e registrar manipuladores de eventos para esses dois eventos.

Sempre que um serviço estiver online e se anunciar, o AnnouncementService hospedado pelo cliente receberá o anúncio "online" e OnlineAnnouncementReceived será acionado no cliente. Quando o serviço ficar offline, ele enviará um comunicado "offline" e OfflineAnnouncementReceived será acionado no cliente. O seguinte ilustra um aplicativo cliente de exemplo que hospeda o AnnouncementService e implementa manipuladores para os dois eventos de anúncio:

classe Cliente

{

    public static void Main()

    {

        Criar uma instância do AnnouncementService

        AnnouncementService announcementService = new AnnouncementService();

        Assinar os eventos de anúncio

        announcementService.OnlineAnnouncementReceived += OnOnlineEvent;

        announcementService.OfflineAnnouncementReceived += OnOfflineEvent;

        Criar ServiceHost para o AnnouncementService

        using (ServiceHost announcementServiceHost =

            new ServiceHost(announcementService))

        {

            Ouça os comunicados enviados por multicast UDP

            announcementServiceHost.AddServiceEndpoint(

                new UdpAnnouncementEndpoint());

            announcementServiceHost.Open();

            Console.WriteLine("Escutando comunicados de serviço.");

            Console.WriteLine();

            Console.WriteLine("Pressione <ENTER> para terminar.");

            Console.ReadLine();

        }

    }

    static void OnOnlineEvent(object sender, AnnouncementEventArgs e)

    {

        Console.WriteLine();

        Console.WriteLine("Recebeu um comunicado online de {0}:",

            e.EndpointDiscoveryMetadata.Address);

        PrintEndpointDiscoveryMetadata(e.EndpointDiscoveryMetadata);

    }

    static void OnOfflineEvent(object sender, AnnouncementEventArgs e)

    {

        Console.WriteLine();

        Console.WriteLine("Recebeu um comunicado offline de {0}:",

            e.EndpointDiscoveryMetadata.Address);

        PrintEndpointDiscoveryMetadata(e.EndpointDiscoveryMetadata);

    }

    ...

}

Figura 11: Escutando mensagens de anúncio de descoberta

Agora suponha que eu execute este programa cliente e deixe-o em funcionamento por um tempo. Posteriormente, executei algumas instâncias do aplicativo host de serviço. Quando cada um for iniciado, veremos uma mensagem de anúncio "online" aparecer na janela do console do cliente. Quando fechar cada um dos aplicativos host de serviço, veremos uma mensagem de anúncio "offline" aparecer na janela do console do cliente. A Figura 11 mostra a janela resultante do console do cliente depois de fazer o que acabei de descrever.

Lembre-se de que o modo de descoberta ad hoc só funciona em uma sub-rede local. Se você quiser usar WS-Discovery além dos limites da rede local, precisará recorrer ao modo de descoberta gerenciado. O WCF 4 também oferece suporte para a criação dos componentes de descoberta gerenciados necessários.

Descoberta de Serviço Gerenciado

A implementação do modo de descoberta gerenciada é um pouco mais envolvida do que o modo ad hoc porque exige que você implemente um serviço de proxy de descoberta. O serviço proxy de descoberta é o componente que manterá o controle de todos os pontos de extremidade de serviço disponíveis. Neste exemplo, usamos a funcionalidade de anúncio para atualizar o proxy de descoberta. Há muitas outras maneiras de fornecer um proxy de descoberta com as informações de descoberta relevantes, por exemplo, você pode conectar um banco de dados existente de pontos de extremidade e capturar dados de lá. Então, como você implementa um serviço de proxy de descoberta?

O WCF 4 vem com uma classe base chamada DiscoveryProxy da qual você pode derivar para implementar um serviço de proxy de descoberta. A Figura 12 mostra o início de uma implementação de serviço proxy de descoberta personalizada. Os exemplos do SDK do .NET 4 contêm uma implementação de exemplo completa para sua referência. Depois de concluir a implementação do serviço de proxy de descoberta, você precisará hospedá-lo em algum lugar.

Figura 12: Implementando um serviço de proxy de descoberta personalizado

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single,

    ConcurrencyMode = ConcurrencyMode.Multiple)]

classe pública MyDiscoveryProxy : DiscoveryProxyBase

{

    Repositório para armazenar EndpointDiscoveryMetadata.

    Um banco de dados ou um arquivo simples também pode ser usado.

    Dictionary<EndpointAddress, EndpointDiscoveryMetadata> onlineServices;

    public MyDiscoveryProxy()

    {

        this.onlineServices =

            new Dictionary<EndpointAddress, EndpointDiscoveryMetadata>();

    }

    OnBeginOnlineAnnouncement é chamado quando uma mensagem Hello é recebida por Proxy

    substituição protegida IAsyncResult OnBeginOnlineAnnouncement(

        DiscoveryMessageSequence messageSequence, EndpointDiscoveryMetadata

        endpointDiscoveryMetadata, retorno de chamada asyncCallback, estado do objeto)

    {

        Este. AddOnlineService(endpointDiscoveryMetadata);

        retornar novo OnOnlineAnnouncementAsyncResult(callback, state);

    }

    protected override void OnEndOnlineAnnouncement(IAsyncResult result)

    {

        OnOnlineAnnouncementAsyncResult.End(result);

    }

    OnBeginOfflineAnnouncement é chamado quando uma mensagem bye é recebida por Proxy

    substituição protegida IAsyncResult OnBeginOfflineAnnouncement(

        DiscoveryMessageSequence messageSequence, EndpointDiscoveryMetadata

        endpointDiscoveryMetadata, retorno de chamada asyncCallback, estado do objeto)

    {

        Este. RemoveOnlineService(endpointDiscoveryMetadata);

        retornar novo OnOfflineAnnouncementAsyncResult(callback, state);

    }

    protected override void OnEndOfflineAnnouncement(resultado IAsyncResult)

    {

        OnOfflineAnnouncementAsyncResult.End(result);

    }

    OnBeginFind é chamado quando uma mensagem de solicitação de investigação é recebida pelo Proxy

    IAsyncResult OnBeginFind de substituição protegida(

        FindRequestContext findRequestContext, AsyncCallback callback, object state)

    {

        Este. MatchFromOnlineService(findRequestContext);

        retornar novo OnFindAsyncResult(callback, state);

    }

    protected override void OnEndFind(IAsyncResult result)

    {

        OnFindAsyncResult.End(result);

    }

    ...

Para este exemplo, simplesmente hospedarei o serviço MyDiscoveryProxy em um aplicativo de console.  Configurarei o host com dois pontos de extremidade: um ponto de extremidade de descoberta e um ponto de extremidade de anúncio. O exemplo a seguir ilustra como hospedar corretamente o serviço MyDiscoveryProxy com ambos os pontos de extremidade:

programa de classe

{

    public static void Main()

    {

        Uri probeEndpointAddress = new Uri("net.tcp://localhost:8001/Probe");

        Comunicado de UriEndpointAddress =

            new Uri("net.tcp://localhost:9021/Announcement");

        ServiceHost proxyServiceHost = new ServiceHost(new MyDiscoveryProxy());

        DiscoveryEndpoint discoveryEndpoint = new DiscoveryEndpoint(

            new NetTcpBinding(), new EndpointAddress(probeEndpointAddress));

        discoveryEndpoint.IsSystemEndpoint = false;

        AnnouncementEndpoint announcementEndpoint = new AnnouncementEndpoint(

            new NetTcpBinding(), new EndpointAddress(announcementEndpointAddress));

        proxyServiceHost.AddServiceEndpoint(discoveryEndpoint);

        proxyServiceHost.AddServiceEndpoint(announcementEndpoint);

        proxyServiceHost.Open();

        Console.WriteLine("Serviço proxy iniciado.");

        Console.WriteLine();

        Console.WriteLine("Pressione <ENTER> para encerrar o serviço.");

        Console.WriteLine();

        Console.ReadLine();

        proxyServiceHost.Close();

    }

}

Depois de ter um serviço de proxy de descoberta em execução, você poderá configurar seus serviços para se anunciarem diretamente para o serviço de proxy de descoberta. Da mesma forma, você pode configurar seus aplicativos cliente para investigar o serviço proxy de descoberta diretamente (sem mais mensagens multicast).

Você configura o serviço para anunciar-se diretamente para o serviço de proxy de descoberta especificando o endereço de anúncio do proxy de descoberta ao criar o AnnouncementEndpoint dentro do aplicativo host de serviço. O exemplo a seguir mostra como fazer isso:

...

Uri baseAddress = new Uri("net.tcp://localhost:9002/CalculatorService/" +

    Guid.NewGuid(). ToString());

Uri announcementEndpointAddress = new Uri("net.tcp://localhost:9021/Announcement");

ServiceHost serviceHost = new ServiceHost(typeof(CalculatorService), baseAddress);

ServiceEndpoint netTcpEndpoint = serviceHost.AddServiceEndpoint(

    typeof(ICalculatorService), nova cadeia de caracteres NetTcpBinding(). Vazio);

Criar um ponto de extremidade de anúncio apontando para o serviço de proxy hospedado

AnnouncementEndpoint announcementEndpoint = new AnnouncementEndpoint(

    new NetTcpBinding(), new EndpointAddress(announcementEndpointAddress));

ServiceDiscoveryBehavior serviceDiscoveryBehavior = new ServiceDiscoveryBehavior();

serviceDiscoveryBehavior.AnnouncementEndpoints.Add(announcementEndpoint);

serviceHost.Description.Behaviors.Add(serviceDiscoveryBehavior);

serviceHost.Open();

...

Em seguida, você pode configurar seus aplicativos cliente para se comunicar diretamente com o serviço proxy de descoberta especificando o endereço de investigação do proxy de descoberta ao criar o DiscoveryEndpoint no aplicativo cliente. O exemplo a seguir ilustra uma maneira de fazer isso:

...

Crie um ponto de extremidade de descoberta que aponte para o serviço proxy.

Uri probeEndpointAddress = new Uri("net.tcp://localhost:8001/Probe");

DiscoveryEndpoint discoveryEndpoint = new DiscoveryEndpoint(

    new NetTcpBinding(), new EndpointAddress(probeEndpointAddress));

Criar DiscoveryClient usando o discoveryEndpoint criado anteriormente

DiscoveryClient discoveryClient = new DiscoveryClient(discoveryEndpoint);

Localizar pontos de extremidade ICalculatorService

FindResponse findResponse = discoveryClient.Find(

    new FindCriteria(typeof(ICalculatorService)));

...

Agora vamos examinar um exemplo completo. Primeiro, executarei o aplicativo proxy de descoberta para que o serviço de proxy de descoberta esteja disponível para uso. Em seguida, executarei uma instância do aplicativo host de serviço, que anunciará a si mesmo com o proxy de descoberta. Quando isso acontecer, veremos uma mensagem impressa na janela do console do aplicativo proxy de descoberta (consulte a Figura 13). Isso ilustra que o serviço se anunciou com êxito para o proxy de descoberta e o proxy de descoberta salvou as informações sobre o novo ponto de extremidade de serviço "online". Agora, se executarmos o código do cliente mostrado acima, ele investigará o proxy de descoberta diretamente e recuperará o endereço do ponto de extremidade do serviço de destino em execução no momento.

Figura 13: Saída do serviço proxy de descoberta em runtime

A beleza da descoberta de serviços gerenciados é que ele funciona entre limites de rede (é baseado em chamadas de serviço tradicionais) e reduz a necessidade de mensagens multicast em sua solução de descoberta. Além disso, como os clientes passam por um proxy de descoberta para procurar serviços, os próprios serviços não precisam estar em funcionamento o tempo todo para serem descobertos.

Uso avançado de proxy de descoberta

O modelo de programação do WCF oferece muita flexibilidade na implementação de um proxy de descoberta. Receber comunicados é uma maneira de preencher sua lista de serviços; no entanto, não é o único método. Por exemplo, se o ambiente já contiver um repositório de serviço, você poderá criar facilmente uma fachada de proxy de descoberta sobre esse repositório para tornar o repositório detectável em runtime.

Um proxy de descoberta pode ser configurado no modo ad hoc ou gerenciar. Ao operar no modo gerenciado, os clientes se comunicam com o proxy diretamente de maneira unicast usando anúncios, investigações e resoluções. O proxy também transmite a resposta de maneira unicast de volta para o remetente.

Se estiver operando no modo ad hoc, um proxy poderá escutar mensagens de descoberta multicast e responder diretamente ao remetente. Nesse modo ad hoc, um proxy também pode ser configurado especificamente para suprimir mensagens multicast. Ou seja, se um proxy receber uma mensagem multicast, ele informará o remetente de sua presença e informará o remetente para direcionar consultas adicionais no proxy, evitando assim mensagens multicast adicionais.

Para obter mais informações sobre esses cenários avançados de descoberta, consulte o WS-Discovery Primer em http://www.oasis-open.org/committees/download.php/32184/WS-D-primer-wd-04.docx.

Serviço de roteamento

Em alguns ambientes orientados a serviços, geralmente é útil aproveitar os serviços centralizados de "roteamento" que atuam como agentes ou gateways para os serviços de negócios reais espalhados pela organização. Isso separa os consumidores dos serviços de negócios reais e possibilita executar uma variedade de diferentes tipos de processamento intermediário dentro do nó de roteamento.

Por exemplo, alguns ambientes usam o roteamento para implementar um limite de segurança centralizado que todas as mensagens de entrada devem passar. Alguns usam técnicas de roteamento baseadas em conteúdo para determinar qual serviço de destino usar com base no conteúdo de uma mensagem de entrada específica. Outros usam o roteamento para implementar a ponte de protocolo, permitindo que os consumidores usem um conjunto de protocolos para se comunicar enquanto o roteador usa um conjunto diferente de protocolos para se comunicar com o serviço de destino. Também não é incomum usar o roteamento para várias técnicas de balanceamento de carga ou até mesmo de controle de versão de serviço.

Seja qual for o motivo, o padrão de "roteamento intermediário" é um requisito comum ao criar soluções SOA em larga escala hoje. No WCF 3.x, não havia suporte oficial para roteamento. Embora a estrutura tenha fornecido as APIs necessárias para implementar seus próprios serviços de roteamento, foi muito trabalho fazer isso corretamente. Vários artigos foram publicados na MSDN Magazine mostrando como fazer isso.

Como o roteamento é um requisito tão comum nos dias de hoje, o WCF 4 agora vem com um "serviço de roteamento" oficial na estrutura que você pode simplesmente hospedar e configurar em suas próprias soluções.

Noções básicas sobre o RoutingService

O WCF 4 vem com uma nova classe chamada RoutingService, que fornece uma implementação genérica de roteamento WCF para uso em seus aplicativos. O RoutingService pode lidar com mensagens de roteamento em qualquer protocolo com suporte do WCF usando uma variedade de diferentes padrões de mensagens, como mensagens unidirecionais, solicitações e duplex. Veja a seguir a definição da classe RoutingService:

[ServiceBehavior(AddressFilterMode = AddressFilterMode.Any,

    InstanceContextMode = InstanceContextMode.PerSession,

    UseSynchronizationContext = false, ValidateMustUnderstand = false),

 AspNetCompatibilityRequirements(RequirementsMode =

    AspNetCompatibilityRequirementsMode.Allowed)]

classe pública selada RoutingService : // contratos permitem padrões de comunicação diferentes

    ISimplexDatagramRouter, ISimplexSessionRouter, IRequestReplyRouter,

    IDuplexSessionRouter, IDisposable

{

    ... // implementação omitida

}

Como você pode ver, a classe RoutingService deriva de vários contratos de serviço para dar suporte a vários padrões de mensagens. Cada contrato de serviço fornece suporte para um padrão de mensagens diferente, incluindo suporte para comunicações baseadas em sessão, quando apropriado.

A finalidade do RoutingService é receber mensagens de entrada dos consumidores e "roteá-las" para um serviço downstream apropriado. O RouterService determina qual serviço de destino usar avaliando cada mensagem de entrada em relação a um conjunto de filtros de mensagem. Portanto, como desenvolvedor, você controla o comportamento de roteamento definindo os filtros de mensagem, normalmente em um arquivo de configuração. Os serviços de destino podem residir no mesmo computador que o RouterService, mas não precisam– eles também podem ser distribuídos pela rede e podem exigir uma variedade de protocolos diferentes.

Hospedando o RoutingService

Você pode hospedar o RoutingService em seu aplicativo como faria com qualquer outro serviço WCF. Basta criar uma instância do ServiceHost e especificar RoutingService para o tipo de serviço. Depois de chamar Abrir na instância do ServiceHost, o RoutingService estará pronto para "rotear" mensagens conforme ilustrado aqui:

usando o sistema;

usando System.ServiceModel;

usando System.ServiceModel.Routing;

public static void Main()

{

    Crie um ServiceHost para o tipo RoutingService.

    using (ServiceHost serviceHost =

        new ServiceHost(typeof(RoutingService)))

    {

        experimentar

        {

            serviceHost.Open();

            Console.WriteLine("O serviço de roteamento agora está em execução.");

            Console.WriteLine("Pressione <ENTER> para encerrar o roteador.");

            O serviço agora pode ser acessado.

            Console.ReadLine();

            serviceHost.Close();

        }

        catch (CommunicationException)

        {

            serviceHost.Abort();

        }

    }

}

Você também configura o RoutingService como qualquer outro serviço e é aí que você define os filtros de roteamento. Primeiro, você precisa configurá-lo com um ou mais pontos de extremidade. Ao definir um ponto de extremidade de roteamento, você escolhe uma associação WCF e um dos contratos de serviço de roteamento implementados pelo RoutingService mostrados acima (por exemplo, ISimplexDatagramRouter, IRequestReplyRouter etc). Você pode expor mais de um ponto de extremidade em seu RoutingService se quiser dar suporte a vários padrões de mensagens ou associações WCF.

O exemplo a seguir ilustra como configurar o RoutingService com quatro pontos de extremidade de roteamento: dois que usam o BasicHttpBinding (unidirecional e solicitação-resposta) e dois que usam o WSHttpBinding (unidirecional e solicitação-resposta). Observe como é como configurar qualquer outro serviço WCF:

<configuração>

  <system.serviceModel>

    <services>

      <SERVIÇO --ROUTING -->

      <service behaviorConfiguration="routingData"

          name="System.ServiceModel.Routing.RoutingService">

        <host>

          <baseAddresses>

            <add baseAddress="https://localhost:8000/routingservice/router"/>

          </baseAddresses>

        </Host>

        <!--

          Definir e configurar o ponto de extremidade em que queremos que o Roteador escute e o

          Contrato que queremos que ele use. Os contratos fornecidos pelo roteador são:

          ISimplexDatagramRouter, ISimplexSessionRouter, IRequestReplyRouter e

          Iduplexsessionrouter.

         -->

        <endpoint address="oneway-basic"

                  binding="basicHttpBinding"

                  name="onewayEndpointBasic"

                  contract="System.ServiceModel.Routing.ISimplexDatagramRouter" />

        <endpoint address="oneway-ws"

                  binding="wsHttpBinding"

                  name="onewayEndpointWS"

                  contract="System.ServiceModel.Routing.ISimplexDatagramRouter" />

        <endpoint address="twoway-basic"

                  binding="basicHttpBinding"

                  name="reqReplyEndpointBasic"

                  contract="System.ServiceModel.Routing.IRequestReplyRouter" />

        <endpoint address="twoway-ws"

                  binding="wsHttpBinding"

                  name="reqReplyEndpointWS"

                  contract="System.ServiceModel.Routing.IRequestReplyRouter" />

      </service>

    </Serviços>

    ...

As interfaces ISimplexDatagramRouter e IRequestReplyRouter definem definições genéricas de contrato de serviço unidirecional e de solicitação de resposta que podem ser usadas em conjunto com contratos de serviço específicos aos negócios. O seguinte mostra como essas interfaces foram definidas no WCF:

[ServiceContract(Namespace="https://schemas.microsoft.com/netfx/2009/05/routing",

    SessionMode = SessionMode.Allowed)]

interface pública ISimplexDatagramRouter

{

    [OperationContract(AsyncPattern = true, IsOneWay = true, Action = "*")]

    IAsyncResult BeginProcessMessage(Message message, AsyncCallback callback,

        estado do objeto);

    void EndProcessMessage(resultado IAsyncResult);

}

[ServiceContract(Namespace="https://schemas.microsoft.com/netfx/2009/05/routing",

    SessionMode = SessionMode.Allowed)]

interface pública IRequestReplyRouter

{

    [OperationContract(AsyncPattern = true, IsOneWay= false, Action = "*",

        ReplyAction = "*")]

    [GenericTransactionFlow(TransactionFlowOption.Allowed)]

    IAsyncResult BeginProcessRequest(Message message, AsyncCallback callback,

        estado do objeto);

    Message EndProcessRequest(IAsyncResult result);

}

A configuração do ponto de extremidade acima expõe os pontos de extremidade de roteamento para os consumidores usarem.  Os aplicativos cliente escolherão um desses pontos de extremidade a serem usados no código do cliente e direcionarão todas as invocações de serviço diretamente para o RoutingService. Quando o RoutingService recebe uma mensagem por meio de um desses pontos de extremidade, ele avalia os filtros de mensagem de roteamento para determinar para onde encaminhar a mensagem.

Configurando o RoutingService com filtros de mensagem

Você pode configurar o RoutingService com filtros de mensagem por meio de código ou configuração (como todo o resto no WCF). O WCF 4 fornece um RoutingBehavior para gerenciar os filtros de mensagem de roteamento. Primeiro, você precisa habilitar o RoutingBehavior no RouterService e, em seguida, especificar o nome da tabela de filtro que deseja usar com esta instância específica do RoutingService:

<configuração>

  <system.serviceModel>

    ...

    <behaviors>

      <serviceBehaviors>

        <behavior name="routingData">

          <serviceMetadata httpGetEnabled="True"/>

          <-- Definir o Comportamento de Roteamento e especificar o nome da tabela de filtro -->

          <filtro de roteamentoTableName="filterTable1" />

        </Comportamento>

      </Servicebehaviors>

    </Comportamentos>

    ...

Se você examinar o exemplo anterior em que configuramos o RoutingService com pontos de extremidade, verá que aplicamos o comportamento "routingData" ao serviço por meio do atributo behaviorConfiguration. Em seguida, precisamos definir uma tabela de filtro chamada "filterTable1".

No entanto, antes de definirmos uma tabela de filtro, precisamos de definições de ponto de extremidade para os serviços de destino para os quais pretendemos rotear. Você define esses pontos de extremidade de destino na seção de configuração do cliente> WCF <porque o RoutingService é essencialmente o "cliente" quando encaminha mensagens para um serviço de destino. O exemplo a seguir mostra como definir dois pontos de extremidade de destino para os quais podemos rotear:

<configuração>

    ...

    <-- Definir os pontos de extremidade do cliente com os quais queremos que o Roteador se comunique.

         Esses são os destinos para os quais o Roteador enviará mensagens. -->

    <client>

      <endpoint name="CalculatorService1"

       address="https://localhost:8000/servicemodelsamples/calcservice1"

       binding="wsHttpBinding" contract="*" />

      <endpoint name="CalculatorService2"

       address="https://localhost:8001/servicemodelsamples/calcservice2"

       binding="wsHttpBinding" contract="*" />

    </Cliente>

    ...

Agora podemos definir a tabela de filtro real, que determinará a lógica de roteamento em runtime. Você define as entradas da tabela de filtro dentro do <elemento filterTables> . Cada entrada dentro da <filterTable> define um mapeamento entre um "filtro" de roteamento e um ponto de extremidade de destino. Você define os "filtros" que deseja usar dentro do <elemento filters> – cada <entrada de filtro> especifica qual tipo de filtro você deseja usar junto com os dados específicos do filtro (como um valor de ação, uma expressão XPath etc. ).

O exemplo a seguir ilustra como configurar uma tabela de filtro com um único filtro que mapeia para o ponto de extremidade CalculatorService1. Nesse caso, o filtro "MatchAll" corresponde a todas as mensagens de entrada:

<configuração>

    ...

    <SEÇÃO --ROUTING -->

    <roteamento>

      <-- Definir os filtros que queremos que o roteador use. -->

      <filters>

        <filter name="MatchAllFilter1" filterType="MatchAll" />

      </Filtros>

      <-- Definir a tabela de filtro que contém o filtro matchAll -->

      <filterTables>

        <filterTable name="filterTable1">

            <-- Mapear o filtro para um ponto de extremidade do cliente que foi definido anteriormente.

                 As mensagens correspondentes a esse filtro serão enviadas para esse destino. -->

          <add filterName="MatchAllFilter1" endpointName="CalculatorService1" />

        </filterTable>

      </filterTables>

    </Roteamento>

  </system.serviceModel>

</Configuração>

Podemos verificar se o roteamento funciona corretamente executando o aplicativo host do serviço de roteamento, o aplicativo host CalculatorService1 e um cliente projetado para enviar mensagens para um dos pontos de extremidade do roteador. Quando executamos o cliente, devemos ver as mensagens chegarem ao CalculatorService 1 depois de serem "roteadas" pelo RoutingService intermediário (consulte Figura 14, Figura 15 e Figura 16).

Figura 14: o aplicativo host RoutingService

Figura 15: Cliente direcionado ao RoutingService em https://localhost:8000/routingservice/router

Figura 16: o serviço de destino (CalculatorService1)

Filtros de mensagem e roteamento baseado em conteúdo

O WCF vem com várias classes MessageFilter internas que você pode usar em conjunto com seus filtros de mensagem de roteamento para inspecionar o conteúdo das mensagens de entrada.

Por exemplo, o WCF fornece um ActionMessageFilter que permite que você corresponda a valores específicos WS-Addressing "ação". O WCF também fornece EndpointAddressMessageFilter, EndpointNameMessageFilter e PrefixEndpointAddressMessageFilter que permitem corresponder a detalhes específicos do ponto de extremidade. Um dos mais flexíveis é o XPathMessageFilter, que permite avaliar expressões XPath em relação às mensagens de entrada. Todos esses filtros permitem que você execute o roteamento baseado em conteúdo em sua solução.

Além desses tipos de MessageFilter internos, o WCF 4 também possibilita definir filtros de mensagem personalizados, que por acaso são um dos principais pontos de extensibilidade para o RoutingService.

Vamos examinar um exemplo de como executar o roteamento baseado em conteúdo com base em valores de ação. Suponha que queiramos rotear metade das operações CalculatorService para CalculatorService1 e a outra metade para CalculatorService2. Você pode fazer isso definindo filtros para cada um dos diferentes valores de ação CalculatorService e mapeando metade deles para cada ponto de extremidade de serviço de destino, conforme ilustrado aqui:

<configuração>

    ...

    <SEÇÃO --ROUTING -->

    <roteamento>

      <-- Definir os filtros que queremos que o roteador use. -->

     <filters>

        <filter name="addFilter" filterType="Action"

         filterData="http://Microsoft.Samples.ServiceModel/ICalculator/Add"/>

        <filter name="subFilter" filterType="Action"

         filterData="http://Microsoft.Samples.ServiceModel/ICalculator/Subtract"/>

        <filter name="mulFilter" filterType="Action"

         filterData="http://Microsoft.Samples.ServiceModel/ICalculator/Multiply"/>

        <filter name="divFilter" filterType="Action"

         filterData="http://Microsoft.Samples.ServiceModel/ICalculator/Divide"/>

      </Filtros>

      <filterTables>

        <filterTable name="filterTable1">

          <add filterName="addFilter" endpointName="CalculatorService1"/>

          <add filterName="subFilter" endpointName="CalculatorService2"/>

          <add filterName="mulFilter" endpointName="CalculatorService1"/>

          <add filterName="divFilter" endpointName="CalculatorService2"/>

        </filterTable>

      </filterTables>

    </Roteamento>

  </system.serviceModel>

</Configuração>

Agora, quando executarmos a solução e executarmos o cliente que invoca todas as quatro operações, veremos metade das operações aparecerem em cada janela do console de serviço (consulte Figura 17 e Figura 18).

Figura 17: Saída de CalculatorService1

Figura 18: Saída de CalculatorService2

O XPathMessageFilter oferece ainda mais flexibilidade, pois você pode fornecer uma variedade de expressões XPath diferentes para avaliar em relação às mensagens de entrada. Suas expressões XPath podem avaliar qualquer parte da mensagem de entrada, incluindo cabeçalhos SOAP ou o corpo SOAP. Isso oferece muita flexibilidade ao criar filtros de mensagem baseados em conteúdo. Para entender a mecânica de XPathMessageFilter, o seguinte mostra como reescrever o último exemplo usando expressões XPath:

<configuração>

    ...

    <SEÇÃO --ROUTING -->

    <roteamento>

      <-- Definir os filtros que queremos que o roteador use. -->

     <filters>

        <filter name="addFilter" filterType="XPath"

                filterData="/s:Envelope/s:Header/wsa:Action =

                'http://Microsoft.Samples.ServiceModel/ICalculator/Add'"/>

        <filter name="subFilter" filterType="XPath"

                filterData="/s:Envelope/s:Header/wsa:Action =

                'http://Microsoft.Samples.ServiceModel/ICalculator/Subtract'"/>

        <filter name="mulFilter" filterType="XPath"

                filterData="/s:Envelope/s:Header/wsa:Action =

                'http://Microsoft.Samples.ServiceModel/ICalculator/Multiply'"/>

        <filter name="divFilter" filterType="XPath"

                filterData="/s:Envelope/s:Header/wsa:Action =

                'http://Microsoft.Samples.ServiceModel/ICalculator/Divide'"/>

      </Filtros>

      <namespaceTable>

        <add prefix="s" namespace="http://www.w3.org/2003/05/soap-envelope" />

        <add prefix="wsa" namespace="http://www.w3.org/2005/08/addressing" />

      </namespaceTable>

      <filterTables>

        <filterTable name="filterTable1">

          <add filterName="addFilter" endpointName="CalculatorService1"/>

          <add filterName="subFilter" endpointName="CalculatorService2"/>

          <add filterName="mulFilter" endpointName="CalculatorService1"/>

          <add filterName="divFilter" endpointName="CalculatorService2"/>

        </filterTable>

      </filterTables>

    </Roteamento>

  </system.serviceModel>

</Configuração>

Observe que o atributo filterData contém uma expressão XPath que será avaliada em relação à mensagem de entrada (as expressões simplesmente marcar os valores de ação neste exemplo). Observe como também defini um conjunto de associações de prefixo de namespace usando o <elemento namespaceTable> . Isso é necessário se você quiser usar prefixos de namespace em suas expressões XPath, como fiz acima. Executar novamente a solução com essa configuração produz os mesmos resultados de antes (consulte Figura 17 e Figura 18).

Você precisará usar essa técnica de filtro XPath sempre que precisar rotear mensagens com base em cabeçalhos SOAP personalizados ou com base no conteúdo encontrado no corpo da mensagem SOAP.

Ponte de protocolo

Nos exemplos anteriores, estávamos usando a mesma associação do WCF (WSHttpBinding) para conversar entre o cliente e o roteador e entre o roteador e os serviços de destino. O RoutingService é capaz de fazer a ponte da comunicação na maioria das associações do WCF. Por exemplo, talvez você queira configurar o roteador para que os clientes se comuniquem com ele pelo WSHttpBinding, mas o roteador se comunica com os serviços de destino downstream usando o NetTcpBinding ou o NetNamedPipeBinding.

Vamos ver como configurar o RoutingService para lidar com esse cenário. Deixaremos a configuração do ponto de extremidade RoutingService da mesma forma que acima, o que permite que os consumidores se comuniquem com o RoutingService sobre BasicHttpBinding ou o WSHttpBinding. Mas agora alteraremos as definições de ponto de extremidade do cliente para que os serviços de destino usem NetTcpBinding e NetNamedPipeBinding, conforme mostrado aqui:

<configuração>

    ...

    <-- Definir os pontos de extremidade do cliente com os quais queremos que o Roteador se comunique.

         Esses são os destinos para os quais o Roteador enviará mensagens. -->

    <client>

      <endpoint name="CalculatorService1"

       address="net.tcp://localhost:8001/servicemodelsamples/calcservice1"

       binding="netTcpBinding" contract="*" />

      <endpoint name="CalculatorService2"

       address="net.pipe://localhost/servicemodelsamples/calcservice2"

       binding="netNamedPipeBinding" contract="*" />

    </Cliente>

    ...

E, claro, precisaremos atualizar os aplicativos CalculatorService1 e CalculatorService2 para dar suporte aos pontos de extremidade NetTcpBinding e NetNamedPipeBinding compatíveis, respectivamente. Com essa configuração em vigor, os consumidores podem se comunicar com o RoutingService usando BasicHttpBinding/WSHttpBinding e o roteador se comunicará com os serviços de destino usando NetTcpBinding ou NetNamedPipeBinding, dependendo de para qual serviço a mensagem está sendo roteada.

Tratamento de erros e tolerância a falhas

O RoutingService também fornece um mecanismo interno para lidar com erros de comunicação de runtime e dar suporte a um nível básico de tolerância a falhas. Ao definir sua tabela de filtros, você pode definir listas diferentes de pontos de extremidade alternativos que serão usados pelo RoutingService se a comunicação com o ponto de extremidade de destino inicial resultar em um erro. Isso essencialmente possibilita ter listas de pontos de extremidade de "backup".

O exemplo a seguir ilustra como definir uma lista de pontos de extremidade de backup dentro do <elemento backupLists> que podemos associar às entradas da tabela de filtros:

<configuração>

    ...

    <SEÇÃO --ROUTING -->

    <roteamento>

      ... <! -- Defina os filtros que queremos que o roteador use. -->

      <filterTables>

        <filterTable name="filterTable1">

          <add filterName="addFilter" endpointName="CalculatorService1"

               alternateEndpoints="backupEndpoints"/>

          <add filterName="subFilter" endpointName="CalculatorService1"

               alternateEndpoints="backupEndpoints"/>

          <add filterName="mulFilter" endpointName="CalculatorService1"

               alternateEndpoints="backupEndpoints"/>

          <add filterName="divFilter" endpointName="CalculatorService1"

               alternateEndpoints="backupEndpoints"/>

        </filterTable>

      </filterTables>

      <backupLists>

        <backupList name="backupEndpoints">

          <add endpointName="CalculatorService2"/>

        </backupList>

      </backupLists>

    </Roteamento>

  </system.serviceModel>

</Configuração>

Observe como configuramos todas as entradas da tabela de filtro para encaminhar para CalculatorService1 neste exemplo, porque CalculatorService2 agora é nosso ponto de extremidade de "backup" que só será usado quando CalculatorService1 resultar em um TimeoutException, um CommunicationException ou um tipo de exceção derivada. Por exemplo, se eu executar a solução novamente e fechar CalculatorService1 e, em seguida, executar o programa cliente, veremos todas as mensagens acabarem em CalculatorService2. É importante observar, mais uma vez, que toda essa configuração de roteamento pode ser executada dinamicamente no código do aplicativo host.

Comportamento de roteamento multicast

O RoutingService também dá suporte ao roteamento automático de uma mensagem de entrada específica para vários destinos de forma "multicast". Quando a mensagem de entrada corresponder a vários filtros encontrados na tabela de filtros configurada, o RoutingService roteará automaticamente a mensagem para cada um dos pontos de extremidade de destino associados aos filtros "correspondidos".

O exemplo a seguir mostra duas entradas de roteamento configuradas com o mesmo filtro curinga, que corresponde a todas as mensagens de entrada:

<configuração>

    ...

    <SEÇÃO --ROUTING -->

    <roteamento>

      <-- Definir os filtros que queremos que o roteador use. -->

     <filters>

        <filter name="wildcardFilter" filterType="MatchAll" />

      </Filtros>

      <filterTables>

        <filterTable name="filterTable1">

          <add filterName="wildcardFilter" endpointName="CalculatorService1"/>

          <add filterName="wildcardFilter" endpointName="CalculatorService2"/>

          <add filterName="wildcardFilter" endpointName="CalculatorService3"/>

        </filterTable>

      </filterTables>

    </Roteamento>

  </system.serviceModel>

</Configuração>

Com essa configuração em vigor, cada mensagem SOAP de entrada será roteada automaticamente para todos os pontos de extremidade de destino, independentemente do que for encontrado nas mensagens.

Esse comportamento multicast compõe os recursos de ponte de protocolo e tratamento de erros discutidos nas seções anteriores. O único problema é que o multicast só funciona para comunicação unidirecional ou duplex, mas não para a comunicação solicitação-resposta, pois o sistema subjacente precisa manter uma proporção um-para-um entre solicitações de saída e respostas de entrada.

Suporte REST aprimorado

O WCF 4 vem com vários novos recursos que são úteis ao criar serviços RESTful usando o WCF.  Esse conjunto de recursos agora é chamado de Serviços WebHttp do WCF. Eles incluem suporte para uma página de ajuda automática que descreve o serviço RESTful para consumidores, cache HTTP simplificado, seleção de formato de mensagem, exceções amigáveis a REST, integração de roteamento ASP.NET, alguns novos modelos de projeto do Visual Studio e muito mais. Não teremos espaço para cobrir todos esses recursos aqui em detalhes, mas darei a você uma rápida introdução a alguns dos meus favoritos abaixo, juntamente com links para obter mais informações sobre o resto.

Muitos desses recursos foram introduzidos pela primeira vez pelo WCF REST Starter Kit no ano passado e agora estão entrando na estrutura oficial. Você poderá ver mais recursos do Kit de Início REST do WCF no futuro.

Página de Ajuda Automática

Ao usar a classe WebServiceHost no WCF 4, seus serviços RESTful aproveitarão automaticamente os benefícios da funcionalidade automática da página de ajuda. Essa é uma adição muito necessária ao usar o REST, dada a falta de metadados WSDL e a geração de código do lado do cliente, o que torna muito mais fácil para os consumidores descobrirem como começar a usar seu serviço, portanto, geralmente é uma boa ideia habilitar esse novo recurso.

Quando você usa a classe WebServiceHost para hospedar seu serviço, ele configura automaticamente seu serviço com o WebHttpBehavior e adiciona um ponto de extremidade HTTP padrão configurado com o WebHttpBinding (no endereço HTTP base). A partir do WCF 4, a classe WebHttpBehavior vem com uma propriedade HelpEnabled que controla se a nova página de ajuda está ou não habilitada no host. O exemplo de configuração a seguir mostra como habilitar o recurso de página de ajuda automática para um ponto de extremidade REST específico:

<configuração>

  <system.serviceModel>

    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />

    <behaviors>

      <endpointBehaviors>

        <behavior name="HelpBehavior">

          <webHttp helpEnabled="true" />

        </Comportamento>

      </endpointBehaviors>

    </Comportamentos>

    <services>

      <service name="CounterResource">

        <comportamento do ponto de extremidadeConfiguration="HelpBehavior"

                  binding="webHttpBinding"

                  contract="CounterResource" />

      </service>

    </Serviços>

  </system.serviceModel>

</Configuração>

Você pode ver a página de ajuda navegando até o endereço base do serviço com "ajuda" acrescentada ao final da URL (consulte a Figura 19).

Figura 19: página de ajuda automática para serviços RESTFul

A página de ajuda fornece uma descrição legível por humanos de cada operação anotada com [WebGet] ou [WebInvoke], e para cada uma delas descreve o modelo de URI, a operação HTTP com suporte e os formatos de mensagem com suporte, basicamente tudo o que um consumidor precisa saber (consulte a Figura 20). Você também pode fornecer uma descrição mais amigável ao ser humano aplicando um atributo [Descrição] a cada operação.

Para cada solicitação/resposta, a página de ajuda também fornece um esquema XML e uma instância XML de exemplo correspondente que os consumidores podem usar para integrar ao serviço. Os consumidores podem usar o esquema para gerar tipos serializáveis apropriados do lado do cliente ou podem simplesmente inspecionar o documento XML de exemplo para determinar manualmente como escrever o código de processamento XML apropriado. Ambas as abordagens são úteis.

Figura 20: página de ajuda automática para uma operação específica

Esse novo recurso de página de ajuda torna automaticamente seus serviços RESTful mais detectáveis, o que facilita o consumo de outras pessoas. Seus consumidores podem descobrir o design de URI do serviço, as operações HTTP com suporte e os formatos de solicitação/resposta, e sua descrição sempre permanecerá em sincronia com seu código WCF , semelhante a como as coisas funcionam com ASP.NET Serviços Web.

Suporte ao cache HTTP

Um dos principais benefícios potenciais do REST é o cache HTTP. No entanto, para obter esse benefício, você precisa aproveitar os vários cabeçalhos de cache HTTP em suas mensagens de solicitação e resposta. Você pode fazer isso em suas operações de serviço do WCF acessando manualmente os cabeçalhos de solicitação/resposta por meio da instância WebOperationContext, mas não é trivial fazer corretamente.

Portanto, o WCF 4 vem com um modelo mais simples para controlar o cache por meio do atributo [AspNetCacheProfile] que você pode aplicar declarativamente às suas operações GET. Esse atributo permite que você especifique um ASP.NET nome de perfil de cache para cada operação e, nos bastidores, há um inspetor de cache (CachingParameterInspector) que cuida da manipulação de todos os detalhes de cache HTTP subjacentes.

A implementação [AspNetCacheProfile] baseia-se no mecanismo de cache de saída ASP.NET padrão. O exemplo a seguir mostra como aplicar o atributo [AspNetCacheProfile] a uma operação [WebGet]:

...

[AspNetCacheProfile("CacheFor60Seconds")]

[WebGet(UriTemplate=XmlItemTemplate)]

[OperationContract]

public Counter GetItemInXml()

{

    retornar HandleGet();

}

...

Com isso em vigor, você precisará definir um perfil de cache de saída ASP.NET chamado "CacheFor60Seconds" no arquivo web.config. O seguinte web.config mostra como isso pode ser feito:

<configuração>

  <system.web>

    <Cache>

      <Outputcachesettings>

        <Outputcacheprofiles>

          <add name="CacheFor60Seconds" duration="60" varyByParam="format" />

        </Outputcacheprofiles>

      </Outputcachesettings>

    </Cache>

  </System.web>

  <system.serviceModel>

    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />

  </system.serviceModel>

</Configuração>

Observe como o perfil de cache ASP.NET é definido para armazenar em cache a saída por 60 segundos e ele é configurado para variar o cache pela variável de cadeia de caracteres de consulta "format". Isso é importante porque o serviço em questão dá suporte a XML e JSON, que você controla por meio da variável de formato (por exemplo, "?format=json"). O serviço precisa armazenar em cache respostas XML e JSON independentemente umas das outras.

Esse arquivo de configuração também ilustra como habilitar o modo de compatibilidade ASP.NET, que é necessário quando você deseja aproveitar vários recursos de ASP.NET como o cache de saída.

O atributo [AspNetCacheProfile] torna muito mais fácil aproveitar o cache HTTP sem exigir que você trabalhe diretamente com os cabeçalhos de cache HTTP. O comportamento do WCF subjacente cuida da injeção dos cabeçalhos HTTP Cache-Control, Date, Expires e Vary HTTP na resposta, que os clientes também podem aproveitar para determinar a semântica de cache adequada em relação a viagens de ida e volta futuras.

Seleção de formato de mensagem

Se você olhar para trás na Figura 19, observará que o serviço CounterResource dá suporte aos formatos XML e JSON para as operações GET, POST e PUT. Isso tem sido possível desde o WCF 3.5 por meio das propriedades RequestFormat e ResponseFormat encontradas nos atributos [WebGet] e [WebInvoke].

Fiz isso no serviço CounterResource definindo um contrato de operação separado para cada versão – uma para a versão XML e outra para a versão JSON – conforme ilustrado aqui:

...

[WebGet(UriTemplate="")]

[OperationContract]

public Counter GetItemInXml()

{

    retornar HandleGet();

}

[WebGet(UriTemplate = "?format=json", ResponseFormat=WebMessageFormat.Json)]

[OperationContract]

public Counter GetItemInJson()

{

    retornar HandleGet();

}

...

Isso, infelizmente, significa que, para cada operação lógica, você precisará de dois contratos de operação se quiser dar suporte aos formatos XML e JSON. No caso do serviço CounterResource, fui obrigado a ter seis contratos de operação para dar suporte a XML e JSON para as operações GET, POST e PUT.

O WCF 4 torna esse cenário muito mais fácil de lidar fornecendo suporte para seleção automática de formato com base em cabeçalhos HTTP "Accept", que é uma maneira melhor de fazer isso. Deixe-me explicar como isso funciona.

Primeiro, precisamos apenas de um único contrato de operação para cada operação lógica:

[WebGet(UriTemplate="")]

[OperationContract]

public Counter GetItem()

{

    retornar HandleGet();

}

Em seguida, nós, a seleção de formato automático na WebHttpEndpoint padrão, conforme ilustrado aqui:

<configuração>

  <system.serviceModel>

    <standardEndpoints>

      <webHttpEndpoint>

        <-- o ponto de extremidade padrão "" é usado para criar automaticamente um ponto de extremidade da Web. -->

        <standardEndpoint name="" helpEnabled="true"

            automaticFormatSelectionEnabled="true"/>

      </webHttpEndpoint>

    </standardEndpoints>

  </system.serviceModel>

</Configuração>

Com isso em vigor, o serviço agora é capaz de lidar e retornar mensagens XML ou JSON. Ele descobre qual formato usar primeiro inspecionando o cabeçalho HTTP Accept encontrado na mensagem de solicitação. Se isso não funcionar, ele usará o mesmo formato de mensagem que a mensagem de solicitação, supondo que seja XML ou JSON, caso contrário, usará o formato padrão para a operação específica.

Agora cabe ao cliente determinar qual formato usar por meio dos cabeçalhos Tipo de Conteúdo HTTP e Aceitar. O cabeçalho Tipo de Conteúdo especifica o formato na mensagem de solicitação, enquanto o cabeçalho Accept indica qual formato o cliente "aceita" de volta do serviço. Por exemplo, se o cliente quiser receber JSON de volta do serviço, ele deverá especificar o seguinte cabeçalho HTTP Accept na solicitação:

Aceitar: application/json

Isso é fácil de realizar em qualquer modelo de programação de cliente HTTP e também é fácil usar ferramentas como o Fiddler. A Figura 21 mostra como usar o Fiddler para obter o JSON de volta do nosso serviço novo e aprimorado.

Figura 21: Usando o cabeçalho HTTP Accept para recuperar JSON

O WCF 4 também fornece novas APIs que facilitam a especificação explícita do formato de mensagem em runtime. O código a seguir mostra como podemos estender a operação GetItem escrevendo código que primeiro procura um parâmetro de cadeia de caracteres de consulta "format" e define explicitamente o formato de resposta adequadamente. Se ele não encontrar um parâmetro de "formato", ele simplesmente dependerá do cabeçalho Accept como antes.

[WebGet(UriTemplate="")]

[OperationContract]

public Counter GetItem()

{

    se um parâmetro de cadeia de caracteres de consulta de formato tiver sido especificado,

    defina o formato de resposta para isso. Se não houver tal

    O parâmetro de cadeia de caracteres de consulta existe, o cabeçalho Accept será usado

    formato de cadeia de caracteresQueryStringValue =

       WebOperationContext.Current.IncomingRequest.UriTemplateMatch.QueryParameters[

       "format"];

    if (!string. IsNullOrEmpty(formatQueryStringValue))

    {

        se (formatQueryStringValue.Equals("xml",

           System.StringComparison.OrdinalIgnoreCase))

        {

            WebOperationContext.Current.OutgoingResponse.Format = WebMessageFormat.Xml;

        }

        else if (formatQueryStringValue.Equals("json",

           System.StringComparison.OrdinalIgnoreCase))

        {

            WebOperationContext.Current.OutgoingResponse.Format = WebMessageFormat.Json;

        }

        else

        {

            gerar nova cadeia de caracteres WebFaultException<>(string. Format("Formato sem suporte '{0}'",

               formatQueryStringValue), HttpStatusCode.BadRequest);

        }

    }

    retornar HandleGet();

}

No final, esses novos recursos fazem com que você não precise mais codificar um tipo de formato de mensagem em seus contratos de operação [WebGet] e [WebInvoke] como você fez no WCF 3.5.

Tratamento de erros RESTful

Como os serviços RESTful não usam SOAP, você não tem mais o mecanismo de falha SOAP padrão à sua disposição, o que possibilita mapear automaticamente entre exceções do .NET e mensagens de falha SOAP. Ao criar serviços REST no WCF 3.5, você precisava construir manualmente a mensagem de resposta HTTP sempre que quisesse personalizar a mensagem de erro HTTP enviada de volta ao cliente.

No WCF 4, você encontrará uma nova classe chamada WebFaultException T> que facilita muito a transmissão<de "falhas da Web" (por exemplo, erros HTTP) de volta para seus consumidores. Ele funciona muito parecido com FaultException<T> no WCF, mas você pode especificar um código http status e um tipo de detalhe para fornecer mais detalhes.

O exemplo a seguir mostra como retornar um erro HTTP 400 (Solicitação Inválida) quando o usuário especifica um tipo de formato sem suporte – estou simplesmente usando cadeia de caracteres para o tipo de detalhe:

if (!string. IsNullOrEmpty(format))

{

    se (formato. Equals("xml", System.StringComparison.OrdinalIgnoreCase))

    {

        WebOperationContext.Current.OutgoingResponse.Format = WebMessageFormat.Xml;

    }

    caso contrário, se (formatar. Equals("json", System.StringComparison.OrdinalIgnoreCase))

    {

        WebOperationContext.Current.OutgoingResponse.Format = WebMessageFormat.Json;

    }

    else

    {

        gerar nova cadeia de caracteres WebFaultException<>(string. Format("Formato sem suporte '{0}'",

           format), HttpStatusCode.BadRequest);

    }

}

Esse novo recurso torna muito mais natural retornar mensagens de erro HTTP padrão simplesmente lançando exceções como você normalmente faz em seus serviços baseados em SOAP.

Integrando o WCF com rotas de ASP.NET

Há muitas semelhanças entre os recursos de roteamento baseados em URL encontrados no WCF 4 e ASP.NET 4. Ambos permitem que você faça a mesma coisa – essencialmente mapeie modelos de URL para métodos em classes. No entanto, há uma diferença significativa entre os dois modelos.

A abordagem WCF 4 vincula o design do modelo de URL a uma única classe por meio dos atributos [WebGet] e [WebInvoke] aplicados à definição de classe durante o desenvolvimento. À medida que o serviço cresce e evolui ao longo do tempo, isso pode levar a um design monolítico grande que não pode ser fatorado em partes menores. A abordagem ASP.NET 4, por outro lado, separa a lógica de roteamento da definição de classe de destino, permitindo mapear suas rotas de serviço em várias definições de classe quando desejado.

O WCF 4 agora fornece a capacidade de integrar o mecanismo de roteamento de ASP.NET aos serviços do WCF 4, possibilitando se beneficiar do modelo de roteamento de ASP.NET sobre seus serviços WCF.

Você faz isso em seu método Global.asax RegisterRoutes aproveitando a nova classe ServiceRoute que permite mapear uma rota ASP.NET para uma classe de serviço WCF:

private void RegisterRoutes()

{

   Fábrica WebServiceHostFactory = novo WebServiceHostFactory();

   RouteTable.Routes.Add(new ServiceRoute("Bookmarks", factory,

      typeof(BookmarkService)));

   RouteTable.Routes.Add(new ServiceRoute("Users", factory,

      typeof(UserService)));

}

Estou chamando RouteTable.Routes.Add duas vezes para adicionar novas rotas para duas classes de serviço WCF diferentes. A primeira rota mapeia "/Indicadores" para a classe BookmarkService, enquanto a segunda rota mapeia "/Usuários" para a classe UserService. Observe como ambas as rotas são configuradas para usar o WebServiceHostFactory.

Agora, quando usarmos os atributos [WebGet] e [WebInvoke] nas definições de classe de serviço do WCF, usaremos caminhos relativos – e eles serão relativos à rota ASP.NET especificada aqui.

Modelos de projeto REST

Um dos recursos interessantes no Visual Studio 2010 é o Gerenciador de Extensões, que você pode acessar por meio de Ferramentas | Gerenciador de Extensões. O Gerenciador de Extensões possibilita que a Microsoft e outros terceiros integrem novos modelos de projeto, controles e ferramentas à sua experiência do Visual Studio 2010 com um simples clique de um botão. Para as equipes de produtos da Microsoft, isso é ótimo porque possibilita enviar itens após o RTM e ainda torná-los extensões oficiais fornecidas pela Microsoft.

Se você abrir o Gerenciador de Extensões e expandir o nó "Modelos", encontrará um nó filho chamado "WCF". Clique em "WCF" e você verá quatro novos Modelos de Serviço REST do WCF – um para cada versão do .NET (3.5 vs 4.0) e um por idioma (C# vs. VB.NET) conforme ilustrado na Figura 22.

Figura 22: Novos modelos de projeto REST do WCF no Gerenciador de Extensões

Você pode selecionar esses novos modelos de projeto e baixá-los na instalação local do Visual Studio 2010 e usá-los como qualquer outro tipo de projeto. Você encontrará o novo tipo de projeto REST em Visual C# | Web | Aplicativo de Serviço REST do WCF (supondo que você instalou a versão do C#).

Ao usá-lo para criar um novo projeto, você observará que o aplicativo de serviço REST do WCF gerado usa a maioria dos novos recursos do WCF 4 que realcei ao longo desta seção.

Mais informações

Além do que discutimos aqui, o WCF 4 vem com vários recursos REST mais avançados e novas extensões de API do WCF que simplificam coisas como lidar com formatos de mensagem personalizados (não XML ou JSON), retornar "exibições" baseadas em modelo usando a nova funcionalidade T4, implementar suporte condicional a GET e ETag, implementar simultaneidade otimista e gerar links de saída.

Para obter mais informações sobre todos esses novos recursos do WCF 4, incluindo os que não tínhamos espaço para cobrir aqui, navegue até o Blog do Ponto de Extremidade do .NET e localize a entrada em Introdução aos Serviços WebHttp do WCF no .NET 4. A equipe forneceu uma série completa de blog de 12 partes que percorre cada novo recurso uma entrada de blog por vez, juntamente com muitos códigos de exemplo e instruções passo a passo.

Serviços de fluxo de trabalho

Uma das áreas de recurso que mais chamou a atenção no .NET 4 foi a de "serviços de fluxo de trabalho". A Microsoft vem investindo fortemente na melhoria da integração entre o WCF e o WF, a fim de fornecer um modelo de programação avançado e declarativo para criar uma ampla variedade de aplicativos.

Noções básicas sobre os Serviços de Fluxo de Trabalho

O WF fornece um modelo de programação declarativa que eleva o nível de abstração para aqueles que escrevem a lógica. O que o WF oferece, em última análise, é uma estrutura para escrever seus próprios idiomas definindo sua própria biblioteca de atividades de domínio empresarial. Em seguida, ele fornece um designer visual para compor essas atividades em programas e um runtime que sabe como gerenciar a execução desses programas.

O WF fornece um modelo especialmente bom para implementar aplicativos de longa execução que precisam aguardar a ocorrência de diferentes tipos de eventos externos, como o recebimento de uma mensagem de outro sistema. Seu modelo de persistência possibilita fazer uso eficiente dos recursos do sistema mantendo apenas o programa na memória quando ele estiver realmente em execução. Quando o programa está aguardando um evento externo ocorrer, ele pode ser persistido no banco de dados liberando assim os recursos do sistema que ele estava usando.

Figura 23: Serviços de Fluxo de Trabalho

O que torna o WF diferente de outras estruturas de desenvolvimento é que ele oferece esses recursos, ao mesmo tempo em que permite que você programe usando uma lógica de programação sequencial de controle de fluxo, o que geralmente é muito mais fácil para os desenvolvedores lerem e escreverem o código para entender.

Se você estiver criando serviços do WCF que são de longa duração por natureza ou podem se beneficiar de alguns dos outros benefícios que acabei de descrever, você pode implementar seus serviços do WCF usando um fluxo de trabalho do WF. E você pode usar o WF para coordenar a lógica de consumo de outros serviços externos.

A Figura 23 ilustra esses conceitos.

Embora isso tenha sido possível desde o .NET 3.5, o .NET 4 deu passos significativos para melhorar a experiência do desenvolvedor e fornecer alguns dos recursos necessários que estavam ausentes no .NET 3.5.

Combinando os mundos do WCF e do WF, você obtém o melhor dos dois mundos. Você obtém um modelo de programação declarativa, uma experiência amigável para os desenvolvedores graças aos designers do WF, um runtime avançado para gerenciar serviços de longa duração e a rica flexibilidade de comunicação oferecida pelo WCF.

Criando seu primeiro serviço de fluxo de trabalho

Para dar uma ideia da nova experiência de desenvolvedor dos serviços de fluxo de trabalho, explicarei um exemplo completo de criação de um usando o .NET 4 e o novo designer do Visual Studio 2010.

Se você abrir o Visual Studio 2010 e selecionar Arquivo | Novo Projeto, você encontrará um novo projeto de fluxo de trabalho nos modelos do WCF – ele é chamado de "Aplicativo de Serviço de Fluxo de Trabalho do WCF". Selecionarei este modelo de projeto e darei a ele um nome de "HelloWorldWorkflowService" e criarei o novo projeto.

Figura 24: Modelos de projeto dos Serviços de Fluxo de Trabalho do Visual Studio 2010

Depois de criado, o novo projeto conterá apenas dois arquivos – um arquivo Service1.xamlx que contém a definição de serviço de fluxo de trabalho declarativo e um arquivo Web.config que contém a configuração do serviço.

Uma das coisas mais atraentes sobre o novo modelo de serviços de fluxo de trabalho no .NET 4 é que toda a definição de serviço pode ser definida em XAML.   Em nosso novo projeto, o arquivo Service1.xamlx contém a definição de serviço e a implementação é simplesmente um fluxo de trabalho baseado em XAML. O arquivo Web.config contém a configuração do serviço WCF. É aqui que você definirá os pontos de extremidade e os comportamentos do serviço de fluxo de trabalho.

A Figura 25 mostra a aparência do designer do Visual Studio 2010 para o arquivo Service1.xamlx. Observe que ele é apenas o designer de fluxo de trabalho padrão, somente nesse caso específico o fluxo de trabalho que estamos projetando servirá como uma implementação de serviço WCF. A chave para integrar essa definição de fluxo de trabalho ao WCF é o novo WorkflowServiceHost e o conjunto de atividades de "Mensagens" do WCF (consulte a Caixa de Ferramentas na Figura 25).

Figura 25: Projetando um serviço de fluxo de trabalho no Visual Studio 2010

O serviço de fluxo de trabalho esqueleto fornecido com este modelo de projeto contém uma atividade Receive seguida por uma atividade Send. Observe que a atividade Receive contém uma propriedade OperationName e está definida como "GetData". Ele também tem uma propriedade Content para associar a mensagem de entrada a uma variável local definida dentro da atividade Sequence – nesse caso, a variável é chamada de "dados" e é do tipo Int32 (consulte a janela Variáveis abaixo do serviço de design de fluxo de trabalho na Figura 25).

A atividade Receive também está configurada para ativar o serviço, o que significa que a mensagem de entrada pode fazer com que uma nova instância do fluxo de trabalho seja criada. Observe que a propriedade CanCreateInstance está marcada no janela Propriedades à direita na Figura 25).

Vamos personalizar um pouco esse serviço de fluxo de trabalho. Primeiro, alterarei o nome do arquivo de serviço de Service1.xamlx para HelloWorld.xamlx. Em seguida, alterarei o nome do serviço para "HelloWorldService" na janela correta. Em seguida, alterarei a propriedade OperationName da atividade Receive para "SayHello". Por fim, definirei uma nova variável chamada "personName" do tipo String na Sequência.

Em seguida, associarei a propriedade Conteúdo da atividade Receive à variável "personName", conforme ilustrado na Figura 26. Em seguida, associarei a propriedade Conteúdo da atividade Send a uma cadeia de caracteres no formato "hello {0}!" inserindo a variável personName no espaço reservado. Em seguida, salvarei o arquivo Service1.xamlx resultante.

Figura 26: Definindo a propriedade Content para a atividade Receive

Neste ponto, vá em frente e abra o arquivo HelloWorld.xamlx e inspecione seu conteúdo. Você pode fazer isso clicando com o botão direito do mouse no arquivo no Gerenciador de Soluções e selecionando "Abrir com..." seguido por "Editor XML". Isso permite que você examine a definição de XAML bruta para o serviço. É importante observar que a definição de XAML representa a implementação completa do serviço. Não há nenhum código C#.

Para este exemplo, vamos contar com o ponto de extremidade HTTP padrão e presumir que temos um comportamento de serviço padrão em vigor que habilita automaticamente os metadados de serviço para que não precisemos de nenhuma configuração do WCF.

Agora estamos prontos para testar nosso serviço de fluxo de trabalho baseado em XAML. Isso é tão fácil quanto pressionar F5 no Visual Studio 2010. Pressionar F5 carregará o serviço de fluxo de trabalho no servidor de desenvolvimento ASP.NET e fará com que o cliente de teste do WCF apareça. O Cliente de Teste do WCF se conecta automaticamente ao serviço de fluxo de trabalho, baixa os metadados do WSDL e possibilita testar a lógica do serviço de fluxo de trabalho (consulte a Figura 27).

Figura 27: Testando o serviço de fluxo de trabalho

Isso ilustra que a infraestrutura de serviços de fluxo de trabalho é capaz de produzir dinamicamente uma definição WSDL para o serviço inspecionando as atividades de Envio e Recebimento usadas dentro da definição de fluxo de trabalho e os tipos de mensagens que estão enviando e recebendo.

Isso conclui o passo a passo simples da criação de seu primeiro serviço de fluxo de trabalho declarativo no .NET 4. Vimos que a implementação do serviço é definida completamente em XAML e que você usa as atividades de Envio/Recebimento para modelar as interações de mensagens dentro do fluxo de trabalho. Também vimos que o .NET 4 vem com suporte de hospedagem para arquivos .xamlx, removendo assim a necessidade de arquivos .svc adicionais. Continuaremos investigando cada uma dessas áreas com mais detalhes nas seções a seguir.

Serviços de fluxo de trabalho de hospedagem

O .NET 4 vem com infraestrutura de hospedagem nova e aprimorada para serviços de fluxo de trabalho. Você pode hospedar serviços de fluxo de trabalho no IIS/WAS ou em seus próprios aplicativos usando WorkflowServiceHost. A infraestrutura de hospedagem do .NET 4 fornece as partes necessárias para gerenciar instâncias de fluxo de trabalho de longa execução e para correlacionar mensagens quando você tiver várias instâncias de serviço em execução simultaneamente. O .NET 4 também fornece um ponto de extremidade de controle de fluxo de trabalho padrão para gerenciar instâncias de fluxo de trabalho remotamente.

Hospedagem no IIS/ASP.NET

O exemplo simples de HelloWorldWorkflowService que percorremos na seção anterior ilustrava como hospedar serviços de fluxo de trabalho no IIS/ASP.NET. Embora tenhamos testado o serviço usando o servidor de desenvolvimento ASP.NET, ele teria funcionado da mesma forma no IIS usando um pool de aplicativos ASP.NET 4. O .NET 4 instala o manipulador necessário para solicitações .xamlx, que manipula a criação do WorkflowServiceHost nos bastidores e a ativação das instâncias de fluxo de trabalho individuais.

Embora tenhamos usado a abordagem de "configuração zero" em nosso primeiro exemplo, você pode configurar serviços de fluxo de trabalho em Web.config como qualquer outro serviço WCF. É aqui que você configura todos os pontos de extremidade e comportamentos do WCF que gostaria de usar. Você pode expor quantos pontos de extremidade desejar em seus serviços de fluxo de trabalho, mas deve considerar o uso de uma das novas associações de "contexto" (por exemplo, BasicHttpContextBinding, WSHttpContextBinding ou NetTcpContextBinding) que gerenciam a comunicação específica da instância.

O exemplo a seguir ilustra como configurar um serviço de fluxo de trabalho com dois pontos de extremidade HTTP:

<configuração>

  <system.serviceModel>

    <services>

      <service name="HelloWorldWorkflowService">

        <endpoint binding="basicHttpContextBinding" contract="IHelloWorld"/>

        <endpoint address="ws" binding="wsHttpContextBinding"

                  contract="IHelloWorld"/>

      </service>

      ...

Você também pode configurar o serviço de fluxo de trabalho com comportamentos do WCF. O exemplo a seguir ilustra como configurar esse serviço de fluxo de trabalho com alguns dos comportamentos padrão do WCF:

<configuração>

  <system.serviceModel>

    <services>

      <service name="HelloWorldWorkflowService"

        behaviorConfiguration="HelloWorldWorkflowService.Service1Behavior" >

        <endpoint binding="basicHttpContextBinding" contract="IHelloWorld"/>

        <endpoint address="ws" binding="wsHttpContextBinding"

                  contract="IHelloWorld"/>

      </service>

     </Serviços>

    <behaviors>

      <serviceBehaviors>

        <behavior name="HelloWorldWorkflowService.Service1Behavior">

          <serviceDebug includeExceptionDetailInFaults="False" />

          <serviceMetadata httpGetEnabled="True"/>

        </Comportamento>

      </Servicebehaviors>

      ...

Além desses comportamentos padrão do WCF, o .NET 4 vem com alguns novos comportamentos específicos do WF para controlar a persistência de fluxo de trabalho, o acompanhamento de fluxo de trabalho e outros comportamentos de runtime de fluxo de trabalho em conjunto com seus serviços de fluxo de trabalho. Confira os exemplos do SDK do .NET 4 para obter mais detalhes.

Auto-hospedagem com WorkflowServiceHost

Embora o .NET 3.5 tenha vindo com uma classe WorkflowServiceHost para hospedar serviços de fluxo de trabalho, ele precisava ser reprojetado, considerando todas as alterações no modelo de programação e runtime do WF no .NET 4. Portanto, o .NET 4 vem com uma nova classe WorkflowServiceHost encontrada no assembly System.ServiceModel.Activities.

A classe WorkflowServiceHost facilita o host de serviços de fluxo de trabalho em seu próprio aplicativo, seja um aplicativo de console, um aplicativo WPF ou um serviço Windows. O fragmento de código a seguir ilustra como usar WorkflowServiceHost em seu próprio código de aplicativo:

...

Host WorkflowServiceHost = novo WorkflowServiceHost("HelloWorld.xamlx",

    new Uri("https://localhost:8080/helloworld"));

Host. AddDefaultEndpoints();

Host. Description.Behaviors.Add(

    new ServiceMetadataBehavior { HttpGetEnabled = true });

Host. Open();

Console.WriteLine("O host está aberto");

Console.ReadLine();

...

Como você pode ver, se você optar por hospedar seus serviços de fluxo de trabalho no IIS/ASP.NET ou em seus próprios aplicativos, eles são tão fáceis de hospedar quanto qualquer serviço WCF.

WorkflowControlEndpoint

Hospedar um serviço de fluxo de trabalho cuida da inicialização do runtime do WF e da ativação de novas instâncias de fluxo de trabalho quando as mensagens de ativação chegam por meio de um dos pontos de extremidade expostos. Além dessa funcionalidade básica de hospedagem, também é importante poder gerenciar a configuração e a execução dessas instâncias de fluxo de trabalho em execução remotamente. O .NET 4 facilita a realização fornecendo um WorkflowControlEndpoint padrão que você pode expor em seus serviços de fluxo de trabalho.

O exemplo a seguir ilustra como adicionar o ponto de extremidade de controle de fluxo de trabalho padrão ao seu serviço em um cenário de hospedagem personalizado:

...

Host WorkflowServiceHost = novo WorkflowServiceHost("HelloWorld.xamlx",

    new Uri("https://localhost:8080/helloworld"));

Host. AddDefaultEndpoints();

WorkflowControlEndpoint wce = new WorkflowControlEndpoint(

    new NetNamedPipeBinding(),

    new EndpointAddress("net.pipe://localhost/helloworld/WCE"));

Host. AddServiceEndpoint(wce);

Host. Open();

...

Se você estiver hospedando no IIS/ASP.NET, poderá adicionar o WorkflowControlEndpoint padrão da seguinte maneira:

<configuração>

  <system.serviceModel>

    <services>

      <service name="HelloWorldWorkflowService"

          behaviorConfiguration="HelloWorldWorkflowService.Service1Behavior" >

        <endpoint address="" binding="basicHttpContextBinding" contract="IHelloWorld"/>

        <endpoint address="ws" binding="wsHttpContextBinding" contract="IHelloWorld"/>

        <endpoint address="wce" binding="wsHttpBinding" kind="workflowControlEndpoint"/>

      </service>

      ...

O WorkflowControlEndpoint possibilitará que o serviço de fluxo de trabalho seja gerenciado em diferentes ambientes de hospedagem. Um desses ambientes é possibilitada pelo AppFabric do Windows Server, que estará disponível em uma versão futura do Windows Server.

Atividades de envio/recebimento

O .NET 3.5 veio com duas atividades de mensagens – Enviar e Receber – para enviar e receber mensagens usando o WCF, mas elas eram bastante limitadas em termos de funcionalidade. O .NET 4 aprimora as atividades de Envio e Recebimento com alguns recursos adicionais (por exemplo, correlação) e adiciona mais algumas atividades de mensagens – SendReply e ReceiveReply – que simplificam a modelagem de operações de solicitação-resposta.

Ao usar as atividades de Envio/Recebimento em um serviço de fluxo de trabalho, você está essencialmente usando-as para modelar o contrato de serviço que será exposto aos clientes por meio da definição do WSDL. Ao usar a atividade Receive, você dá a ela um nome de operação e mapeia a mensagem de entrada para um tipo .NET. Embora tenhamos usado cadeias de caracteres simples até agora, você também pode usar contratos de dados complexos definidos pelo usuário. Vamos atualizar o HelloWorldWorkflowService para usar o seguinte tipo de contrato de dados:

[DataContract(Namespace="")]

classe pública Person

{

    [DataMember]

    ID da cadeia de caracteres pública;

    [DataMember]

    cadeia de caracteres pública FirstName;

    [DataMember]

    cadeia de caracteres pública LastName;

}

Se adicionarmos essa definição de classe ao projeto Web e retornarmos a HelloWorld.xamlx, poderemos definir uma nova variável chamada "personMsg" do tipo Person (o contrato de dados definido acima). Em seguida, podemos mapear as atividades ReceiveRequest e SendResponse para a variável personMsg por meio da propriedade "Content". Para fazer isso, basta selecionar cada atividade e pressionar o botão "Conteúdo" para abrir a janela "Definição de Conteúdo". Em seguida, insira "personMsg" na caixa de texto "Dados da Mensagem" e "Pessoa" na caixa de texto "Tipo de Mensagem". Você precisará fazer isso para ambas as atividades.

As atividades de Envio/Recebimento também possibilitam configurar outros aspectos do serviço WCF, como o nome da operação, o nome do contrato de serviço, a ação, a coleção de tipos conhecidos, o nível de proteção e o serializador a serem usados em runtime (por exemplo, DataContractSerializer vs. XmlSerializer). Na atividade Enviar, você também especifica os detalhes do ponto de extremidade de destino. Todas essas configurações são configuráveis por meio do janela Propriedades quando você tem a atividade Enviar/Receber selecionada.

Com essas alterações em vigor, você pode testar HelloWorld.xamlx novamente para ver que a operação SayHello agora está definida para receber e retornar mensagens do tipo Pessoa.

Definindo operações de Request-Reply

Para facilitar o modelo de operações de solicitação-resposta, o .NET 4 apresenta algumas novas atividades para modelar esses tipos de interações. Uma delas é a atividade SendReply que você pode combinar com uma atividade Receive para implementar uma operação de solicitação-resposta dentro do serviço. Há também uma atividade ReceiveReply que você pode combinar com uma atividade Send ao invocar um serviço externo dentro de um fluxo de trabalho.

O .NET 4 também vem com algumas atividades de nível superior chamadas ReceiveAndSendReply e SendAndReceiveReply, que você verá na Caixa de Ferramentas do Visual Studio 2010. Essas atividades simplesmente compõem Receive/Send com SendReply/ReceiveReply, respectivamente, e facilitam o uso delas. Ao arrastá-los para a superfície de design do fluxo de trabalho, você verá que eles se expandem para uma sequência que contém uma atividade Enviar ou Receber seguida pela atividade de "resposta" apropriada.

Adicionar Referência de Serviço

Para facilitar ainda mais o consumo de serviços externos, o Visual Studio 2010 também fornece um recurso "Adicionar Referência de Serviço" que funciona como você esperaria, exceto que, em vez de gerar uma definição de classe proxy do cliente, ele gera um conjunto de atividades do lado do cliente. Depois de selecionar "Adicionar Referência de Serviço" e especificar o endereço da definição do WSDL, o Visual Studio baixará o WSDL e gerará uma atividade personalizada para cada operação encontrada na definição do WSDL.

Vamos examinar um exemplo simples. Suponha que haja um CalculatorService com as operações Adicionar, Subtrair, Multiplicar e Dividir que queremos usar do nosso serviço de fluxo de trabalho. Podemos simplesmente selecionar "Adicionar Referência de Serviço" e especificar o local da definição do WSDL CalculatorService conforme ilustrado na Figura 28.

Figura 28: Adicionar referência de serviço

Depois de pressionar OK para adicionar a referência de serviço, o Visual Studio baixa a definição do WSDL e gera quatro atividades personalizadas que aparecerão na caixa de ferramentas. Agora você tem as atividades Adicionar, Subtrair, Multiplicar e Dividir que são fáceis de usar nos fluxos de trabalho para invocar o serviço externo.

Correlation

Ao criar sistemas que consistem em serviços de fluxo de trabalho de longa execução, é comum ter várias instâncias de um único serviço de fluxo de trabalho em execução simultaneamente aguardando que o mesmo evento ocorra (por exemplo, aguardando uma mensagem específica chegar antes de continuar). O desafio com esse cenário é que você precisa de uma maneira de correlacionar a mensagem de entrada com a instância de fluxo de trabalho correta. Normalmente, a maneira como você determina a instância de fluxo de trabalho correta é inspecionando o conteúdo da mensagem de entrada.

O .NET 4 vem com um recurso sofisticado de correlação de mensagens baseada em conteúdo que você pode usar em conjunto com as atividades de Envio e Recebimento que acabamos de discutir. A implementação desse recurso gira em torno do que são conhecidos como "identificadores de correlação", outro novo conceito no .NET 4.

Para começar a usar a correlação, primeiro você deve definir uma variável de fluxo de trabalho do tipo CorrelationHandle. Você pode pensar nessa variável como o ponto de encontro para conectar um dado de (potencialmente) duas mensagens diferentes (sendo processadas por duas atividades de mensagens diferentes). As atividades Enviar e Receber fornecem uma propriedade CorrelatesWith para especificar uma variável CorrelationHandle.

Para correlacionar mensagens enviadas por várias atividades de Envio/Recebimento, você deve especificar o mesmo identificador de correlação em todas as atividades que desejam participar da correlação. Em seguida, em cada atividade, você configura a chave de correlação que será mapeada para na mensagem que está sendo processada por essa atividade específica (por exemplo, o elemento SSN em uma mensagem pode se correlacionar com o elemento CustomerId em outra mensagem). Você define chaves de correlação usando expressões XPath que são avaliadas em relação às mensagens.

Vamos estender nosso HelloWorldWorkflowService para ver um exemplo de como isso funciona. Atualmente, o exemplo com o qual estamos trabalhando recebe uma mensagem de Pessoa e a retorna ao cliente. Vamos adicionar outra atividade ReceiveAndSendReply logo abaixo da atividade SendResponse na parte inferior do fluxo de trabalho. Isso adiciona Sequence que contém outro Receive seguido por outro SendReply. Darei à atividade Receive um nome de operação de "Concluir" e exigiremos que o cliente forneça uma saudação na mensagem Concluir que usaremos para construir a resposta final da saudação.

Em outras palavras, os clientes primeiro chamarão SayHello fornecendo seu nome completo para iniciar uma nova instância de serviço de fluxo de trabalho. Em seguida, a instância de fluxo de trabalho aguardará até que o cliente chame Concluir o fornecimento de uma saudação (e isso pode levar minutos, horas ou dias, durante o qual o fluxo de trabalho pode persistir). Depois que o cliente chama Concluir o fornecimento da saudação, o fluxo de trabalho cria uma saudação usando a saudação e o nome original e a retorna. Nesse cenário, podemos facilmente ter várias instâncias de fluxo de trabalho em execução aguardando a operação Concluir ser chamada, portanto, definitivamente precisaremos correlacionar a mensagem Concluir com a mensagem SayHello anterior. Como esse é o caso, preciso associar a atividade Receive para "Finish" ao mesmo identificador de correlação que especificamos na atividade "SayHello" (nameHandle).

Agora vamos examinar o conteúdo da mensagem que vamos correlacionar nessas duas atividades. Para este exemplo, vou usar os dois tipos de contrato de dados a seguir para modelar as mensagens:

[DataContract(Namespace="")]

classe pública Person

{

    [DataMember]

    public string FirstName { get; set; }

    [DataMember]

    public string LastName { get; set; }

}

[DataContract(Namespace = "")]

saudação da classe pública

{

    [DataMember]

    public string Salutation { get; set; }

    [DataMember]

    public string NameKey { get; set; }

}

A atividade Receive para "SayHello" está configurada para usar a mensagem Pessoa e a atividade De recebimento para "Concluir" está configurada para usar a mensagem Saudação. Vamos supor que o valor LastName seja sempre exclusivo para que possamos usá-lo como o valor exclusivo para correlação. Portanto, quando o cliente envia a mensagem "Concluir", ele deve fornecer o mesmo valor LastName usado na mensagem "SayHello" anterior.

Agora vamos ver como configurar o identificador de correlação para configurar isso. Já defini uma variável CorrelationHandle dentro da sequência chamada "handle". A primeira coisa que precisamos fazer é "inicializar" o identificador de correlação dentro da atividade "SayHello" Receive. Portanto, selecione a atividade "SayHello" Receive e pressione o botão ao lado da caixa de texto "CorrelationInitializers".

Aqui, você precisa selecionar "Inicializador de correlação de consulta" na caixa suspensa e, em seguida, poderá escolher a propriedade "LastName" para o campo de consulta (isso deve produzir uma expressão XPath de "sm:body()/xg0:Person/xg0:LastName", conforme ilustrado na Figura 29).

Em seguida, precisamos especificar que a atividade "Concluir" recebimento com correlacionar no mesmo identificador. Selecione a atividade "Concluir" Recebimento e pressione o botão "CorrelatesOn". Em seguida, especifique "handle" para o identificador "CorrelatesWith" e selecione "NameKey" para o campo de consulta (consulte a Figura 30).

Figura 29: Definindo uma chave de correlação para "SayHello"

Figura 30: Definindo uma chave de correlação para "Concluir"

Isso significa, por fim, que o elemento LastName na mensagem Person precisa corresponder ao elemento NameKey na mensagem Greeting entre as duas solicitações separadas. Com isso em vigor, a infraestrutura de fluxo de trabalho poderá correlacionar automaticamente as mensagens para nós e rotear as mensagens de entrada "Concluir" para a instância de fluxo de trabalho correta (com base em "LastName/NameKey").

A atividade sendReply final retorna a seguinte cadeia de caracteres formatada para o chamador, incluindo informações do "personMsg" original e do novo "greetingMsg":

String.Format("{0}{1}{2}!",

   greetingMsg.Salutation, personMsg.FirstName, personMsg.LastName)

Vou testar o recurso de correlação usando o Cliente de Teste do WCF para iniciar várias instâncias de fluxo de trabalho chamando SayHello várias vezes com valores diferentes de FirstName e LastName. Depois de fazer isso, devemos ter várias instâncias em execução do serviço de fluxo de trabalho. Agora, podemos chamar a operação Concluir especificando um dos mesmos valores LastName usados em uma das mensagens SayHello e devemos ver o valor de FirstName correspondente usado na saudação final retornada ao cliente (consulte a Figura 31).

Isso ilustra a correlação baseada em conteúdo em ação, um recurso de serviços de fluxo de trabalho atraente que vem com o .NET 4. É importante observar que você também pode executar correlação com base em dados no nível do canal e do protocolo, como você pode ter feito no .NET 3.5 (por exemplo, usando o canal ou a ID da instância de fluxo de trabalho). A correlação baseada em conteúdo é uma abordagem mais flexível e sofisticada para fazer a mesma coisa.

Figura 31: Testando o exemplo de correlação baseado em conteúdo

Isso nos leva ao fim de nossa cobertura de serviços de fluxo de trabalho. Só conseguimos arranhar a superfície dos serviços de fluxo de trabalho neste artigo, mas você poderá encontrar informações adicionais nos exemplos do SDK do .NET 4 e na documentação crescente do MSDN encontrada online. Como você pode ver, a combinação de WCF e WF 4 abre as portas para um modelo de desenvolvimento totalmente novo para a criação de serviços declarativos, de longa duração e assíncronos que podem aproveitar os melhores recursos que ambas as estruturas têm a oferecer.

Diversos recursos avançados

Além de tudo o que falamos neste artigo, o WCF 4 vem com alguns recursos mais avançados que você pode achar úteis. Esses recursos avançados incluem suporte aprimorado à resolução de tipos por meio de DataContractResolver, a capacidade de lidar com consumidores de fila concorrentes usando ReceiveContext, um novo codificador de fluxo de bytes e rastreamento baseado em ETW de alto desempenho.

Resolução de tipos com DataContractResolver

No WCF 3.x, há um recurso de resolução de tipos chamado "tipos conhecidos". Durante a desserialização, quando o serializador encontra uma instância que não é do mesmo tipo que o tipo declarado, ele inspeciona a lista de "tipos conhecidos" declarados para descobrir qual tipo usar. Como autor do serviço, você pode anotar seus tipos/métodos com os atributos [KnownType] ou [ServiceKnownType] para definir a lista de possíveis substituições. Esse recurso é comumente usado para dar suporte à herança e ao polimorfismo.

Infelizmente, o WCF 3.x não fornece uma maneira fácil de substituir o algoritmo de mapeamento de tipo usado por DataContractSerializer ao executar esse tipo de resolução de tipo dinâmico em runtime. Para resolver esse problema, o WCF 4 fornece a classe abstrata DataContractResolver da qual você pode derivar para implementar seu próprio algoritmo de resolução de tipo personalizado.  O seguinte mostra como começar:

classe MyDataContractResolver : DataContractResolver

{

    Assembly de assembly;

    public MyDataContractResolver(Assembly assembly)

    {

        this.assembly = assembly;

    }

    Usado na desserialização

    Permite que os usuários mapeiem o nome xsi:type para qualquer Tipo

    public override Type ResolveName(string typeName, string typeNamespace,

        Type declaredType, DataContractResolver knownTypeResolver)

    {

        ... // implementar seu mapeamento

    }

    Usado na serialização

    Mapeia qualquer Tipo para uma nova representação xsi:type

    public override bool TryResolveType(Type type, Type declaredType,

        DataContractResolver knownTypeResolver, out XmlDictionaryString typeName,

        out XmlDictionaryString typeNamespace)

    {

        ... // implementar seu mapeamento

    }

}

Depois de implementar o DataContractResolver personalizado, você poderá fornecê-lo ao DataContractSerializer conforme ilustrado aqui:

DataContractSerializer dcs = new DataContractSerializer(

    typeof(Object), null, int. MaxValue, false, true, null,

    new MyDataContractResolver(assembly));

Agora, quando você usa essa instância dataContractSerializer para serializar/desserializar objetos, seu DataContractResolver personalizado será chamado para executar a resolução de tipo personalizado.

Para injetar seu DataContractResolver personalizado no runtime do WCF nos bastidores, você precisará escrever um comportamento de contrato do WCF que se conecta ao DataContractSerializerOperationBehavior e substitui o resolvedor padrão. O SDK do .NET 4 vem com um exemplo completo que ilustra como fazer isso por meio de um comportamento de contrato e um atributo personalizado chamado [KnownAssembly].

A resolução de tipo personalizado pode ser útil quando você deseja substituir os padrões dataContractSerializer, controlar exatamente quais tipos são usados para serialização ou gerenciar dinamicamente tipos conhecidos.

Processando mensagens na fila com ReceiveContext

Com o WCF 3.x, você pode garantir a entrega de mensagens exatamente uma vez ao usar o NetMsmqBinding usando filas transacionais e inscrevendo a operação de serviço WCF na transação MSMQ. Quando exceções são geradas durante o processamento de uma mensagem, o WCF garantirá que a mensagem não seja perdida retornando a mensagem para uma fila (ela pode ser retornada para a fila de origem, uma fila de mensagens suspeitas ou uma fila de mensagens mortas, dependendo da configuração).

Embora essa funcionalidade seja importante, há alguns problemas que as pessoas normalmente encontram. Uma delas é que as transações são caras e produzem muita leitura/gravação com as filas, o que introduz mais sobrecarga e complexidade. Outro problema é que não há como garantir que o mesmo serviço processe a mensagem na próxima vez que ela for extraída da fila (por exemplo, não há como um serviço específico "bloquear" uma mensagem), o que é uma garantia que você pode querer fazer em determinados cenários.

Para resolver esses problemas, o WCF 4 apresenta uma nova API chamada ReceiveContext para processar mensagens enfileiradas. Com ReceiveContext, um serviço pode "espiar" uma mensagem na fila para começar a processá-la e, se algo der errado e uma exceção for gerada, ela permanecerá na fila. Os serviços também podem "bloquear" mensagens para tentar o processamento novamente em um momento posterior. ReceiveContext fornece um mecanismo para "concluir" a mensagem uma vez processada para que ela possa ser removida da fila.

Essa abordagem simplifica as coisas em várias frentes porque as mensagens não estão mais sendo lidas e reescritas em filas pela rede, e as mensagens individuais não estão saltando entre diferentes instâncias de serviço durante o processamento. Vamos dar uma olhada em um exemplo simples para ilustrar como ele funciona.

O exemplo a seguir mostra como usar ReceiveContext para processar mensagens que chegam a uma fila:

...

[OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]

[ReceiveContextEnabled(ManualControl = true)]

public void CalculateProduct(int firstNumber, int secondNumber)

{

    ReceiveContext receiveContext;

    se (! ReceiveContext.TryGet(OperationContext.Current.IncomingMessageProperties,

         out receiveContext))

    {

        Console.WriteLine("ReceiveContext não instalado/encontrado neste computador.");

        return;

    }

    if ((firstNumber * secondNumber) % 2 == (receiveCount % 2))

    {

        receiveContext.Complete(TimeSpan.MaxValue);

        Console.WriteLine("{0} x {1} = {2}", firstNumber, secondNumber,

            firstNumber * secondNumber);

    }

    else

    {

        receiveContext.Abandon(TimeSpan.MaxValue);

        Console.WriteLine("{0}&{1} not processed", firstNumber, secondNumber);

    }

    receiveCount++;

}

...

Supondo uma passagem bem-sucedida, chamamos ReceiveContext.Complete para concluir o processamento da mensagem, fazendo com que ela seja removida da fila subjacente. Caso contrário, chamamos Abandon, que deixa a mensagem na fila para novas tentativas futuras. O SDK do .NET 4 vem com um exemplo completo que você pode usar para explorar esse novo recurso mais detalhadamente.

Simplificar a codificação com ByteStreamMessageEncodingBindingElement

Em alguns cenários de mensagens, você simplesmente deseja transmitir "blobs" de dados binários sem qualquer encapsulamento ou processamento adicional. Para simplificar esse cenário, o WCF 4 vem com um novo ByteStreamMessageEncodingBindingElement que faz exatamente isso. Infelizmente, o .NET 4 não vem com uma nova associação para esse codificador, portanto, você precisará criar uma associação personalizada para usá-la.

O SDK do .NET 4 vem com um exemplo completo que ilustra como definir uma associação personalizada que usa ByteStreamMessageEncodingBindingElement por meio de uma classe de associação personalizada. Depois que você tiver a nova classe de associação em vigor, o codificador de fluxo de bytes se tornará muito fácil de usar com seus serviços.

Rastreamento baseado em ETW de alto desempenho

O WCF 4 também faz algumas melhorias no rastreamento e no diagnóstico frontal. O WCF 4 agora usa o ETW para rastreamento, o que melhora muito o desempenho do rastreamento e fornece uma melhor integração com outras tecnologias relacionadas, como o Windows Workflow Foundation, o Windows Server AppFabric e as várias tecnologias de gerenciamento encontradas no Windows Server, oferecendo um modelo melhor para a plataforma.

Com o ETW, os provedores de eventos gravam eventos em sessões e sessões podem, opcionalmente, persistir esses eventos em um log. Os consumidores podem ouvir eventos de sessão em tempo real ou ler esses eventos de um log após o fato. Os controladores ETW são responsáveis por habilitar/desabilitar sessões e associar provedores a sessões.

O SDK do .NET 4 fornece um exemplo simples que ilustra como aproveitar essa nova arquitetura de rastreamento de alto desempenho em conjunto com seus serviços do WCF.

Conclusão

O WCF 4 traz várias melhorias e vários recursos completamente novos que abordam alguns dos cenários de comunicação mais comuns de hoje. Em primeiro lugar, o WCF 4 torna-se mais fácil de usar por meio do modelo de configuração simplificado e melhor suporte para padrões comuns.

Além disso, o WCF 4 fornece suporte de primeira classe para descoberta e roteamento de serviços, que são requisitos comuns na maioria dos ambientes corporativos e grandes iniciativas soa – esses recursos sozinhos definem o WCF, além de muitas das estruturas concorrentes. O WCF 4 também simplifica o desenvolvimento de serviços baseado em REST e fornece vários outros recursos mais avançados do WCF que abordam alguns pontos problemáticos específicos atualmente.

Além de tudo isso, o WCF 4 fornece uma integração sofisticada com o WF para fornecer um novo modelo para o desenvolvimento de serviços declarativos de fluxo de trabalho. Os serviços de fluxo de trabalho possibilitam desenvolver serviços de execução longa e assíncrona que se beneficiam do modelo de programação do WF e do runtime subjacente. E graças às novas atividades baseadas em WCF e ao suporte do designer encontrado no Visual Studio 2010, esse novo modelo de programação está se tornando uma opção de primeira classe para criar serviços.

No .NET 4, os mundos do WCF e do WF estão se mesclando para oferecer um modelo de programação coeso que oferece o melhor que ambos os mundos têm a oferecer. Para obter mais informações sobre as novidades no WF 4, marcar introdução de um desenvolvedor ao Windows Workflow Foundation no .NET 4.

Sobre o autor

Aaron Skonnard é cofundador do Pluralsight, um provedor de treinamento da Microsoft que oferece cursos de desenvolvedor do .NET orientados por instrutor e sob demanda. Hoje em dia Aaron passa a maior parte do tempo gravando Pluralsight Sob Demanda! cursos focados em Computação em Nuvem, Windows Azure, WCF e REST. Aaron passou anos escrevendo, falando e ensinando desenvolvedores profissionais em todo o mundo. Você pode alcançá-lo em http://pluralsight.com/aaron e http://twitter.com/skonnard.

Recursos adicionais