Criação e Gerenciamento de Conexões HTTP

Desde o início do .NET Framework temos uma classe chamada HttpWebRequest, que tem a finalidade de realizar requisições HTTP para algum recurso disponível. E como era de se esperar, ela expõe diversos métodos e propriedades para configurar as requisições que partem da instancia desta classe.

A cada requisição que é realizada, internamente ela recorre a classe ServicePointManager para criar a conexão até o recurso desejado, e a partir dai passa a reutilizar a mesma conexão nas chamadas subsequentes, poupando recursos da máquina de onde a requisição esta sendo feita. O objeto de conexão é criado considerando o protocolo, o host e a porta; se algum destes parâmetros variar, uma nova conexão é criada para atender este novo endereço e, consequentemente, reutilizada nas próximas requisições.

Só que existe a possibilidade de interferir na criação e gestão destas conexões. A classe HttpWebRequest define uma proprieade chamada ConnectionGroupName, e como o próprio nome sugere, ele considera o valor especificado nesta propriedade para criar a conexão, ou seja, para cada valor distinto definido nesta propriedade, uma nova conexão é criada. Se o valor se repetir, a conexão criada previamente é reutilizada.

Como sabemos, o WCF utiliza a classe HttpWebRequest nos bastidores quando acessa um serviço através do protocolo HTTP, e até então ele não disponibilizava qualquer propriedade no binding para configurar este pool de conexões HTTP. A partir da versão 4.6 do .NET Framework, a Microsoft permite ajustar este parâmetro (adicionando um prefixo) a fim de fazer uma melhor gestão e reuso das conexões HTTP pelos serviços WCF.

Por padrão, uma única conexão é mantida e compartilhada por todos os channels factories (proxy) que são instanciados pelo cliente. O que essa nova configuração irá fazer é criar uma conexão dedicada para cada channel factory criado pelo cliente, utilizando uma espécie de contador de instâncias para definir a propriedade ConnectionGroupName.

Para isso funcionar, basta ligar o flag useUniqueConnectionPoolPerFactory através do arquivo de configuração (app.config ou web.config) da aplicação cliente, assim como é mostrado no código abaixo.

<appSettings>
    <add key=”wcf:httpTransportBinding:useUniqueConnectionPoolPerFactory”
         value=”true” />
</appSettings>

Agora, se quiser um controle mais refinado e, de repente, variar a conexão de acordo com alguma regra ou parâmetro de negócio, você pode extrair e definir o nome do pool de conexões através das propriedades da mensagem do WCF, que em baixo nível, também vai utilizar o valor definido aqui para configurar a propriedade ConnectionGroupName.

OperationContext
    .Current
    .OutgoingMessageProperties
    .Add(“HttpTransportConnectionGroupNamePrefix”, “ValorCustomizado”);

Por fim, para ver isso em funcionamento, considere o código a seguir, que cria um laço e dentro dele instancia o channel factory e invoca o método do serviço. Antes de rodar, vamos configurar o flag como True para ver o impacto causado, provocando a criação de uma conexão dedicada para cada proxy. Por fim, é possível visualizar as conexões criadas através do utilitário TCPView.

var endereco = “http://localhost:9388&#8221;;

using (var host = new ServiceHost(typeof(Teste), new Uri(endereco)))
{
    host.AddServiceEndpoint(typeof(IServico), new BasicHttpBinding(), “”);
    host.Open();

    for (int i = 1; i < 11; i++)
        using (var srv = new ChannelFactory<IServico>(
            new BasicHttpBinding(), new EndpointAddress(endereco)))
                Console.WriteLine(srv.CreateChannel().Ping(“Teste ” + i.ToString()));
}

Anúncios

Consumindo Serviços da NF-e com WCF

Todas as empresas, quando vendem uma mercadoria são obrigadas a emitir nota fiscal. Até pouco tempo atrás, as notas não eram padronizadas e cada empresa utilizava em modelo diferente. O governo decidiu então criar um mecanismo para que fosse possível a emissão de forma eletrônica.

Com isso cada estado brasileiro criou uma porção de web services que permite a emissão, autorização, cancelamento, consulta de status, etc. Como é possível perceber nas URLs dos serviços, eles utilizam o ASP.NET Web Services (ASMX) para expor estas funcionalidades.

Como sabemos, o WCF é o pilar de comunicação dentro do .NET Framework, e mantém compatibilidade com os antigos serviços desenvolvidos em ASMX. Isso quer dizer que é possível consumir estes serviços com a API do WCF. Para que isso seja possível, alguns cuidados precisam ser tomados para garantir que o acesso será permitido.

Os serviços são protegidos e para acessá-los é necessário apresentar um certificado digital válido. Os certificados podem ser adquiridos por entidades certificadores, tais como Serasa, Certsign, VeriSign, etc. E vale dizer que até mesmo o WSDL, documento que descreve as funcionalidades do serviço, também está protegido, e precisamos também apresentar um certificado para simplesmente adicionar a referência no Visual Studio.

Vale lembrar que existem alguns tipos de certificados, e no meu caso, estou utilizando um certifcado de cartão A3 (e-CNPJ) que está protegido por um PIN (senha) que é necessário informá-la sempre que o certificado está prestes a ser utilizado. Como estou com ele devidamente instalado, ao acessar a URL do serviço no navegador, já aparece uma lista com os possíveis certificados, assim como podemos notar na imagem abaixo. Uma vez que o certificado é escolhido, então a senha será exigida e, consequentemente, teremos acesso à página padrão do ASP.NET que descreve o serviço.

Da mesma forma que acontece acima, o Visual Studio também exigirá o certificado ao realizar o “Add Service Reference“, e informando corretamente, o download do WSDL será realizado e a referência para o serviço será configurada. Atente-se aqui que não estamos utilizando a opção “Add Web Reference“, pois ela recorre a API antiga (SoapHttpClientProtocol) para acesso e não utiliza o WCF.

Apesar da ferramenta já criar uma configuração padrão, ela não é válida para o acesso. Precisamos ajustar para informar ao WCF que ele precisa utilizar um certificado antes de fazer a requisição ao serviço. Estamos optando por utilizar um binding customizado para que seja possível adequar a segurança e a serialização de acordo com as exigências da SEFAZ.

Entre as customizações, temos que utilizar a versão 1.2 do SOAP. Além disso, informar que o transporte será baseado em HTTPS e que é necessário um certificado do cliente para apresentar ao serviço para ter acesso ao recurso. A configuração do certificado é feita através de um behavior e associado ao endpoint do serviço. Abaixo temos o resumo do arquivo de configuração válido para realizar o acesso. Por questões de simplicidade, estou utilizando o serviço que retorna o status (operante ou não) dos demais serviços da SEFAZ.

<?xml version=”1.0″ encoding=”utf-8″ ?>
<configuration>
  <system.serviceModel>
    <bindings>
      <customBinding>
        <binding name=”NFe”>
          <textMessageEncoding messageVersion=”Soap12″ />
          <httpsTransport requireClientCertificate=”true” />
        </binding>
      </customBinding>
    </bindings>
    <behaviors>
      <endpointBehaviors>
        <behavior name=”NFe”>
          <clientCredentials>
            <clientCertificate storeName=”My”
                               storeLocation=”CurrentUser”
                               x509FindType=”FindBySubjectName”
                               findValue=”ISRAEL AECE ME:………”/>
          </clientCredentials>
        </behavior>
      </endpointBehaviors>
    </behaviors>
    <client>
      <endpoint address=”https://nfe.fazenda.sp.gov.br/ws/nfestatusservico2.asmx&#8221;
                binding=”customBinding”
                bindingConfiguration=”NFe”
                behaviorConfiguration=”NFe”
                contract=”ServiceReference1.NfeStatusServico2Soap12″
                name=”ServiceReference1.NfeStatusServico2Soap12″ />
    </client>
  </system.serviceModel>
</configuration>

Uma vez que a configuração está concluída, podemos acessar o serviço e visualizarmos o retorno. Note que o código C# nada sabe sobre as configurações de certificado. É importante ressaltar que como este certificado tem um PIN, um prompt irá aparecer automaticamente para a digitação do mesmo. Na sequencia é possível visualizar o resultado que foi devolvido pelo serviço.

using (var client = new NfeStatusServico2Soap12Client())
{
    var corpo = new XmlDocument();
    var mensagem = “<?xml version=”1.0″ encoding=”utf-8″ ?>” +
                “<consStatServ xmlns=”http://www.portalfiscal.inf.br/nfe&#8221; versao=”3.10″>” +
                “<tpAmb>1</tpAmb>” +
                “<cUF>35</cUF>” +
                “<xServ>STATUS</xServ>” +
                “</consStatServ>”;

    corpo.LoadXml(mensagem);

    var resultado = client.nfeStatusServicoNF2(new nfeStatusServicoNF2Request()
    {
        nfeCabecMsg = new nfeCabecMsg()
        {
            cUF = “35”,
            versaoDados = “3.10”
        },
        nfeDadosMsg = corpo
    });

    Console.WriteLine(resultado.nfeStatusServicoNF2Result.OuterXml);
}

<retConsStatServ versao=”3.10″ xmlns=”http://www.portalfiscal.inf.br/nfe”><tpAmb
>1</tpAmb><verAplic>SP_NFE_PL_008f</verAplic><cStat>107</cStat><xMotivo>Serviço
em Operaçao</xMotivo><cUF>35</cUF><dhRecbto>2015-03-19T08:31:31-03:00</dhRecbto>
<tMed>1</tMed></retConsStatServ>

BufferManager

O WCF faz uso internamente de uma classe chamada BufferManager. Como o próprio nome sugere, ela faz o gerenciamento de memória durante as requisições, e ao invés de ficar criando array de bytes para ler ou escrever uma mensagem, o BufferManager gerencia um pool de buffers para fazer uma melhor reutilização quando o recurso não é mais necessário.

Quando a requisição é lida e a memória reservada para isso não é mais necessária, este gestor recicla a mesma ao invés de descartar, evitando assim ter todo o custo de criação e alocação quando uma nova requisição chegar. A não utilização de um gestor de memória pode degradar a performance do serviço, do host e, consequentemente, da máquina onde tudo isso é executado.

Os bindings já fornecem propriedades para criar e gerenciar o BufferManager, a saber: MaxBufferPoolSize e MaxBufferSize. A primeira indica a quantidade máxima de memória (em bytes) que ele terá, enquanto a segunda, indicará o máximo de memória que cada item dentro do pool poderá ter, também em bytes. É importante dizer que a propriedade MaxBufferSize deve ter o mesmo valor da propriedade MaxReceivedMessageSize.

Engana-se quem acha que esta classe só traz benefícios. Se ela for mal configurada, a memória pode não ser devolvida/reciclada, e em pouco tempo, esgotará os recursos disponíveis na máquina. O algoritmo utilizado por esta classe, cria os pools de acordo com o tamanho das mensagens que precisam ser manipuladas, e se não havendo nenhum pool que atenda a mesma ou a mensagem é maior que o valor estipulado na configuração, o WCF aloca memória diretamente na HEAP, ou seja, não gerenciada pelo BufferManager.

Se isso ocorrer com frequência, ao longo do tempo os recursos estarão esgotados e as mensagens não serão mais processadas. É comum ver cenários onde todos as configurações são definidas com seus valores máximos (int.MaxValue), e isso poderá impactar consideravelmente o serviço. É importante dimensionar as configurações de acordo com a necessidade de cada serviço, baseando-se, principalmente, na demanda e tamanho das mensagens que chegam até ele.

Há uma forma de informar ao runtime do WCF para não utilizer o pool de buffers. Para isso basta configurar a propriedade MaxBufferPoolSize com o valor 0 (zero). Isso fará com que o WCF crie um buffer para cada requisição diretamente na memória HEAP, e fica a cargo do Garbage Collector gerenciar o seu tempo de vida. Para confirmar isso, no interior da classe BufferManager, se o valor informado for zero, a classe GCBufferManager é criada, e caso contrário, a classe PooledBufferManager é utilizada.

Se criarmos um pequeno exemplo de um serviço que retorna uma coleção de objetos (5000), podemos perceber no resumo do profiler que foi aplicado à execução, uma diferença significativa da quantidade de bytes que foram alocados quando utilizamos a propriedade MaxBufferPoolSize com valor diferente de zero e com valor igual a zero.

O buffer é útil quando falamos de mensagens pequenas. Quando se tratam de mensagens que ultrapassam 64KB, opte por não utilizar este recurso.

Lidando com cookies no WCF

Há algum tempo falei aqui sobre a propriedade AllowCookies dos bindings HTTP e seu funcionamento. Apesar do WCF suportar cookies, era um pouco complicado lidar (gerar e extrair) com eles, ou seja, havia muito código (e não muito intuitivo) a ser escrito para atingir este objetivo.

Como isso é algo comum quando trabalhamos com o protocolo HTTP, então a Microsoft facilitou o acesso aos cookies a partir da versão 4.5 do .NET/WCF. A partir de agora, é possível o cliente lidar diretamente com a classe a CookieContainer, que representa a coleção de cookies e possui um acesso aos mesmos de forma mais simples.

Para acessarmos este novo recurso, podemos recorrer a factory que utilizamos para construir o proxy no cliente, e através da interface IHttpCookieContainerManager chegamos até o CookieContainer. A partir daí, a inclusão e leitura de cookies (gerados pelo servidor) é extremamente simples, assim como podemos visualizar através do código abaixo:

var url = “http://localhost:8272/srv&#8221;;

using
(var host = new ServiceHost(typeof(Servico)))
{
    host.AddServiceEndpoint(typeof(IContrato), new BasicHttpBinding() { AllowCookies = true }, url);
    host.Open();

    using (var factory =
        new ChannelFactory<IContrato>(
            new BasicHttpBinding() { AllowCookies = true },
            url))
    {
        var proxy = factory.CreateChannel();
        var container = factory.GetProperty<IHttpCookieContainerManager>().CookieContainer;

        //Gerando um Cookie
        container.Add(new Uri(url), new Cookie(“Info”, “Cliente->Servidor”));

        Console.WriteLine(proxy.Ping(“Israel Aece”));

        //Extraindo um Cookie
        Console.WriteLine(container.GetCookies(new Uri(url))[0]);
    }
}

WCF – Suporte à WebSockets

Em um mundo cada vez mais conectado, cresce a necessidade de termos acesso às informações de forma muito mais rápido, em qualquer lugar, a qualquer momento e em qualquer dispositivo. E quando falamos em ter acesso rapidamente à informações, intrinsicamente estamos pensando em “tempo real” que, teoricamente, assim que a informação chega em algum concentrador, ela deve ser encaminhada à todos os interessados.

Uma vez que temos o provedor de informações, os clientes são meros assinantes que recebem as informações e as exibem ou armazenam para seu uso específico. Quando estamos dentro de um ambiente controlado, como por exemplo, a rede privada de uma empresa e com uma infraestrutura homogênea, podemos recorrer à recursos específicos da tecnologia, como é o caso do TCP ou UDP. Mas nem sempre os provedores e consumidores estão dentro de um mesmo ambiente ou utilizando uma mesma tecnologia. Hoje é comum as empresas dependerem excessivamente de parceiros de negócio que fornecem informações extremamente relavantes para o bom andamento de suas atividades.

Existem algumas técnicas para possibilitar, ou melhor, emular a conexão ativa entre as partes para o envio e recebimento de informações. Entre estas técnicas podems citar a técnica de polling, que consiste em ficar consultando o serviço fornecedor das informações de duas maneiras:

  • Periódico: A cada X minutos enviar uma nova requisição para o serviço em busca de novas informações.
  • Requisição Fixa: Esta técnica consiste no servidor prender a requisição até que uma nova informação esteja disponível. Quando estiver, devolve o resultado para o cliente, que por vez, realiza uma nova requisição e fica, novamente, aguardando um novo resultado.

Ambas as técnicas funcionam e do ponto de vista do usuário acaba sendo transparente. Mas o grande ponto negativo é a latência que teremos, sem contar na possibilidade de prejudicar a escalabilidade na medida em que o número de clientes aumenta. Felizmente os grupos que regem a internet fizeram um trabalho, criando e especificando um – novo – protocolo chamado de WebSockets, que tenta resolver grande parte destes problemas de forma simples e objetiva, sem a necessidade de improvisações que vínhamos fazendo até então.

Basicamente falando, este protocolo tem a finalidade de criar um vínculo entre o cliente o servidor sem a necessidade de realizar múltiplas conexões HTTP, fornecendo, nativamente, uma comunicação bidirecional. Ele faz uso de alguns novos headers que são incluídos no cabeçalho da requisição, e que irão controlar se o cliente e o servidor podem ou não estabelecer uma conexão, e a partir daí, passam a trocar mensagens entre eles. A imagem abaixo ilustra as etapas que ocorrem entre as partes quando utilizamos este protocolo:

  • Passo 1: O cliente envia uma requisição para servidor para iniciar e configurar uma conexão via WebSockets.
  • Passo 2: O servidor aceita a requisição e passa ao cliente uma chave para indicar a efetivação da conexão.
  • Passo 3: A partir da conexão estabelecida, ambos podem enviar mensagem um para o outro.
  • Passo 4: Finalmente, quando o cliente não estiver mais interessado, ele pode encerrar a conexão com o servidor.

Os passos 1 e 2 são conhecidos como handshake, que determina se aquele cliente pode se conectar àquele servidor. Caso positivo, o servidor instrui o cliente à migrar/trocar para o protocolo WebSocket. Mais tarde, ainda neste artigo, vamos monitorar o tráfego entre as partes e analisar as mensagens que são trocadas.

Por se tratar de um protocolo multi-plataforma, todos os grandes fabricantes estão se preocupando em adaptar e desenvolver mecanismos em suas tecnologias e ferramentas para entenderem tal protocolo. A Microsoft se preocupa em adaptar suas tecnologias para que seja possível o desenvolvimento e o consumo de serviços que façam uso deste protocolo. O .NET Framework 4.5 incluiu um namespace chamado System.Net.WebSockets, que fornecem classes de baixo nível para este protocolo. Já as camadas mais acima, tais como o ASP.NET e o WCF fazem uso destas classes para abstrair e facilitar a criação e exposição destes tipos de serviços.

Recapitulando, o WCF fornece um binding chamado WSDualHttpBinding que fornece a conexão duplex sobre o protocolo HTTP, mas este utiliza dois canais para a comunicação e faz uso do protocolo WS-RM (Reliable Messaging) para gerenciar a sessão entre as partes. Além disso, muitas vezes o firewall, quando está devidamente configurado, pode barrar o callback que o serviço envia para o cliente.

Para encapsular toda a comunicação utilizando o WebSockets, a Microsoft incluiu no WCF um novo binding chamado de NetHttpBinding. Este binding fornece uma propriedade interessante chamada TransportUsage que recebe uma das três opções expostas pelo enumerador WebSocketTransportUsage:

  • Always: Força a utilização de WebSockets mesmo quando trata-se de contratos request-reply.
  • Never: Evita o uso de WebSockets, resultando em uma exceção se o contrato for duplex.
  • WhenDuplex: Como o próprio nome indica, será utilizado quando trata-se de um contrato duplex.

É importante dizer que este binding, ao contrário do WSDualHttpBinding que utiliza o WS-RM para gerenciamento da sessão, recorre aos recursos do transporte para gerenciar a mesma. Já a codificação das mensagens (que são SOAP) não são interoperáveis, pois estão em formato binário. Para possibilitar o consumo por outras partes que não são .NET, podemos configurar o binding para que as mensagens sejam codificadas em texto através da propriedade MessageEncoding.

Como exemplo de utilização deste protocolo, vamos criar uma espécie de chat. A criação dos contratos e configuração do serviço para a utilização deste novo protocolo é idêntica aquela que já conhecemos até então. Utilizamos duas interfaces, sendo uma para descrever as ações do chat, e a segunda para determinar como e onde será realizado o retorno (callback) das mensagens.

[ServiceContract(CallbackContract = typeof(IChatCallback))]
public interface IChat
{
    [OperationContract]
    void Entrar(string nome);

    [OperationContract]
    void EnviarMensagem(string de, string para, string mensagem);

    [OperationContract]
    void Sair(string nome);
}

public interface IChatCallback
{
    [OperationContract(IsOneWay = true)]
    void ReceberMensagem(string de, string para, string mensagem);
}

Depois do contrato criado e implementado (vou omitir aqui a classe que representa o serviço, mas ela está no arquivo em anexo para download), temos que expor o serviço para que os consumidores consigam acessar. Novamente, veremos que a estrutura é semelhante aquela que já utilizamos na criação de outros serviços em WCF “tradicionais”, sendo a única exceção o binding, que deve ser o NetHttpBinding.

using (var host = new ServiceHost(typeof(Comunicador), 
    new Uri(“http://localhost:8282&#8221;)))
{
    host.AddServiceEndpoint(typeof(IChat), new NetHttpBinding(), “chat”);

    host.Open();

    Console.WriteLine(“Comunicador no Ar”);
    Console.ReadLine();
}

Ao rodar o serviço e analisar os metadados no navegador, veremos que o endereço possui um novo scheme chamado “ws”. Nós podemos utilizar tanto o “http” quanto o “ws”. Ao referenciar o serviço através da opção “Add Service Reference” em uma aplicação cliente, o WCF já faz a configuração utilizando o “ws”, conforme podemos ver no trecho extraído do arquivo de configuração.

<client>
  <endpoint address=”ws://localhost:8282/chat”
            binding=”netHttpBinding”
            bindingConfiguration=”NetHttpBinding_IChat”
            contract=”ServicoDeComunicacao.IChat”
            name=”NetHttpBinding_IChat” />
</client>

Vou omitir o código da aplicação cliente por não haver nada de especial em relação aquilo que já conhecemos em como consumir um serviço WCF que geram callbacks, mas de qualquer forma, todo o código de exemplo está na arquivo anexado à este artigo. Vamos nos atentar as mensagens que trafegam entre as partes quando o cliente e o servidor entram em ação, e para isso, vamos utilizar o Fiddler para monitorar as requisições, que não deixam de ser HTTP. Quando rodamos o serviço e dois clientes (Israel e Jack), podemos ver a conexão sendo estabelecida com o servidor que gerencia o chat e as mensagens sendo trocadas entre eles:

Por fim, ao monitorarmos as requisições via Fiddler, podemos perceber a conexão sendo criada entre as partes, e tudo isso é definido através de headers no cabeçalho das mensagens, conforme notamos na imagem abaixo. Os primeiros que nos chamam a atenção são o header Connection e Upgrade, que indica ao servidor que a conexão deve ser migrada para WebSockets. Na sequencia temos o header Sec-WebSocket-Key, que trata-se de um hash gerado e codificado em Base64, que em conjunto com o header de resposta chamado Sec-WebSocket-Accept, previnem um ataque conhecido como “cross-protocol attack”, e garantem a conexão estabelecida e que as mensagens já podem ser trocadas.

Neste artigo foi possível analisar superficialmente os benefícios e como utilizar este protocolo no WCF. Apesar de útil em alguns cenários, ele pode ser impraticável em outros, como por exemplo, o consumo por aplicações Web (Javascript), que tem certa dificuldade em lidar com mensagens SOAP. Com um pouco de trabalho é possível customizar o serviço para conseguir se comunicar com este tipo de cliente, mas o assunto fica para um próximo artigo.

WCFWebSockets.zip (57.53 kb)

Recursos da Palestra do MVC Summit 2012

 

Acabei de efetuar uma palestra no MVC Summit 2012. Entre os vários temas que foram discutidos sobre o desenvolvimento Web, eu falei sobre o ASP.NET Web API, que pode ser utilizado para a construção de serviços baseados em REST. Peço desculpas, mas infelizmente não houve tempo suficiente para abrir para perguntas e respostas, então para aqueles que gostaríamos de fazer algum questionamento, peço encarecidamente para que vocês coloquem nos comentários deste post, ou se desejarem, podem me contatar diretamente através da seção de contato.

Gostaria imensamente de agradecer a todos os presentes, e também ao Vinicius Quaiato, Andre Baltieri, Victor Cavalcante e ao Alexandre Tarifa por esta oportunidade. Para aqueles interessados, o download do projeto pode ser baixado clicando aqui, e se desejarem, aqui também estão os slides da apresentação.

Configuração dinâmica no WCF

Existem duas formas de configurar um serviço WCF: imperativa e declarativa. A primeira opção consiste em criar explicitamente a classe ServiceHost, configurá-la e gerenciar a sua execução, e tudo isso é feito diretamente através da linguagem (C# e VB.NET). Já no modelo declarativo, recorremos ao arquivo de configuração (App.config ou Web.config), onde tudo é configurado dentro deles, o que te dá uma maior flexibilidade, já que te permite alterar mesmo depois de instalado no servidor.

Quando queremos algum dinamismo na configuração do serviço, podemos recorrer a primeira opção. Mas quando estamos hospedando o serviço em um projeto Web, através de um arquivo *.svc, a construção da classe ServiceHost é criada automaticamente pelo WCF/ASP.NET.

Nestes casos, para conseguir atingir o nosso objetivo, devemos construir uma factory customizada, e para isso, precisamos criar uma classe herdando de ServiceHostFactory, sobrescrever o método CreateServiceHost, onde dentro dele, criaremos o ServiceHost de acordo com nossa necessidade e, finalmente, acoplamos  à execução através da diretiva @ServiceHost no arquivo *.svc. Mais detalhes aqui.

Na versão 4.5 do WCF, a Microsoft facilitou isso, sem a necessidade de fazer várias customizações e configurações. Basicamente colocamos na classe que representa o serviço, um método estático chamado Configure, que receberá como parâmetro a instância da classe ServiceConfiguration, e como o próprio nome diz, corresponde as configurações daquele serviço, e que será aplicado ao ServiceHost. O código abaixo ilustra este novo recurso. Isso irá eliminar tudo aquilo que fazíamos até então, para conseguir customizar e/ou gerar a configuração para os serviços que são hospedados no IIS.

public class Servico : IContrato
{
    public static void Configure(ServiceConfiguration config)
    {
        config.Description.Behaviors.Add(
            new ServiceMetadataBehavior() { HttpGetEnabled = true });

        config.Description.Behaviors.Add(
            new ServiceDebugBehavior() { IncludeExceptionDetailInFaults = true });
    }

    public string Ping(string value)
    {
        return value + ” ping”;
    }
}