Mensagens Assíncronas com ChannelFactory

Uma das formas que temos para consumir serviços WCF em um cliente qualquer, é utilizando a classe ChannelFactory<TChannel>, assim como eu já mostrei um exemplo neste post. Geralmente utilizamos esta técnica quando optamos por compartilhar os tipos (incluindo a interface que representa o contrato) entre o serviço e o cliente, evitando a publicação do documento WSDL, a reconstrução destes tipos do lado do cliente e, principalmente, a necessidade de efetuar a referência do serviço através da IDE do Visual Studio .NET.

Atenção: Antes de prosseguir a leitura deste post, aconselho que leia os seguintes artigos:

Quando fazemos a referência ao serviço através do Visual Studio, há uma opção chamada “Generate asynchronous operations”, que para cada operação encontrada no serviço, um par de métodos BeginNomeDaOperacao e EndNomeDaOperacao serão criados, e que irão trabalhar em conjunto, para que assim, o cliente consiga invocar a respectiva operação de forma assíncrona. O problema disso é que muitas vezes o contrato não oferece suporte à chamadas assíncronas, o que nos obrigará a criar toda a infraestrutura do lado do cliente para suportar isso. Imagine que temos o seguinte contrato:

[ServiceContract]
public interface IContrato
{
    [OperationContract]
    string FazAlgo(string value);
}

Como não temos a versão assíncrona do método FazAlgo, temos que recorrer à delegates para conseguir efetuar a chamada. Sendo assim, o código do lado do cliente, poderia ficar da seguinte forma:

private ChannelFactory<IContrato> _factory;
private IContrato _proxy;

public Form1()
{
    this._factory =
        new ChannelFactory<IContrato>(
            new NetTcpBinding(),
            “net.tcp://localhost:8722/srv”);

    this._proxy = this._factory.CreateChannel();
}

private void button1_Click(object sender, EventArgs e)
{
    string parametro = “algum valor”;

    Func<string, string> executor = p => this._proxy.FazAlgo(p);

    executor.BeginInvoke(
        parametro,
        result =>
        {
            this.textBox1.Invoke(
                new Action<string>(valor => this.textBox1.Text = valor),
                ((Func<string, string>)result.AsyncState).EndInvoke(result));

        },
        executor);
}

A diferença que vemos no código acima, é que invocamos o método FazAlgo através de um delegate. Ao invés de criarmos delegates a todo momento para uma necessidade específica, podemos recorrer aos delegates expostos pelo .NET Framework Action<> e Func<>. No caso acima, estamos fazendo uso do Func<string, string>, que coincide com a assinatura do método FazAlgo, ou seja, recebe e devolve uma string. No exemplo acima, estamos fazendo o uso de lambda ao invés de explicitamente criar a instância do delegate.

Todos os delegates dão suporte à chamada assíncrono para método que ele mantém a referência, e sendo assim, via BeginInvoke disparamos a execução da operação do serviço, utilizando uma segunda thread, e mantendo a aplicação disponível para outros trabalhos. Neste caso, o método BeginInvoke recebe três parâmetros: o primeiro é o parâmetro de entrada que o serviço recebe; o segundo é um delegate de callback, que será invocado quando o resultado voltar; e finalmente, o terceiro parâmetro é um System.Object que será passado para o callback, e que estará acessível através da propriedade AsyncState, e que no caso, passamos a instância do delegate criado para invocar o método.

Para recuperar o resultado, dentro do callback invocamos o método EndInvoke, que retornará o resultado do serviço, e como na maioria dos casos, precisamos exibir isso na tela. Como o callback sempre é disparado na thread que está executando o método assíncrono, você não pode tocar nos controles, já que eles tem afinidade com a thread de criação deles. Aqui entra em cena o método Invoke, exposto pelo do controle que deseja atualizar, e através dele, determinamos um método para ser executado na mesma thread que o criou, e aqui também utilizando lambda.

Se você tiver acesso ao contrato e poder alterá-lo, então você pode dar suporte ao processamento assíncrono ao mesmo, e grande parte do trabalho que vimos acima, do lado do cliente, será descartado. Para suportar o processamento assíncrono no contrato, temos que criar as versões assíncronas do método, e com isso, o nosso contrato de exemplo ficará da seguinte forma:

[ServiceContract]
public interface IContrato
{
    [OperationContract]
    string FazAlgo(string value);

    [OperationContract(AsyncPattern = true)]
    IAsyncResult BeginFazAlgo(string value, AsyncCallback callback, object state);

    string EndFazAlgo(IAsyncResult result);
}

Ao contrário do que vimos anteriormente, ao utilizar essa técnica, você terá o processamento assíncrono tanto do lado do cliente, quanto do lado do serviço. Eu já discuti bastante sobre isso nestes outros artigos. Sendo assim, toda a complexidade da chamada assíncrona será movida para o serviço, que deveremos criar a versão assíncrona do método FazAlgo. O código do lado do cliente ficará relativamente mais simples:

private void button2_Click(object sender, EventArgs e)
{
    string parametro = “algum valor”;

    this._proxy.BeginFazAlgo(
        parametro,
        result =>
        {
            this.textBox1.Invoke(
                new Action<string>(valor => this.textBox1.Text = valor),
                this._proxy.EndFazAlgo(result));
        }, null);
}

Para ajudar no entendimento, você pode baixar o código de exemplo clicando aqui.

Ativação do host baseando-se na memória

O WCF fornece uma opção que nos permite configurar a porcentagem mínima de memória disponível na máquina antes de ativar o serviço. Para customizar isso, recorremos a propriedade (atributo) MinFreeMemoryPercentageToActivateService da classe (elemento) ServiceHostingEnvironmentSection. Essa propriedade recebe um número inteiro, que corresponde a quantidade de memória disponível que a máquina onde o serviço está rodando deverá ter disponível.

Como essa configuração recorre a métodos não gerenciados para fazer essa verificação, a aplicação (serviço) deverá estar rodando em full-trust. Caso contrário, uma exceção do tipo SecurityException será disparada e, consequentemente, o serviço não estará disponível para receber requisições.

Consumindo serviços via HTTPS

Quando você instala um certificado em um servidor Web, ele poderá ser utilizado por alguma aplicação (UI ou serviços) que é executada no mesmo. Ao vincular esse certificado através do binding de HTTPS no IIS, você poderá começar a fazer uso deste protocolo na sua aplicação. Agora, imagine que você tenha um certificado que foi emitido para um outro domínio, diferente daquele que você está utilizando. Por exemplo, veja que o certificado abaixo foi emitido para o “Dummy”, diferente do meu domínio utilizado pela aplicação, que é localhost.

Ao tentar acessar através do navegador, uma mensagem de aviso será exibida, indicando que o certificado que ele está utilizando é duvidoso, já que foi emitido para um domínio diferente daquele que ele foi vinculado. A imagem abaixo mostra essa mensagem:

Quando o usuário é interrogado, ele poderá decidir se continua ou não acessando aquele site, algo simples de fazer quando trata-se de aplicações que permitem a interação do usuário. Podemos também hospedar serviços no IIS, sejam eles WCF ou ASP.NET Web Services (ASMX), expondo através de HTTPS. Depois deste serviço referenciado na aplicação cliente, ao executá-la, você poderá se deparar com uma exceção do tipo SecurityNegotiationException, com a seguinte mensagem: Could not establish trust relationship for the SSL/TLS secure channel with authority [Servidor].

Durante a execução da aplicação cliente, antes dela efetivamente consumir o serviço, o WCF pergunta se ele deve ou não confiar no certificado, e o comportamento padrão é não. É exatamente o que acontece quando acessado através do navegador. Como o serviço não é interativo, você precisa de alguma forma, verificar se deseja ou não confiar naquele certificado que está sendo utilizado. Se não pudermos alterar o certificado, de uma forma semelhante a que vimos aqui, podemos escrever um código que permita efetuar essa verificação em tempo de execução.

Para isso, fazemos o uso da classe estática ServicePointManager, que está no namespace System.Net. Essa classe é responsável por gerenciar as conexões com a internet. Essa classe fornece uma propriedade chamada ServerCertificateValidationCallback, que deverá apontar para um método que segue a assinatura imposta pelo delegate RemoteCertificateValidationCallback. Abaixo temos um exemplo da sua utilização:

ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) => true;

A partir do momento que você configura esta propriedade, as requisições HTTPS que são realizadas, começam a ser verificadas pelo método vinculado. Repare que entre os parâmetros enviados para este método, um deles é o certificado que está sendo avaliado. E além disso, esse método deverá retornar um valor boleano, indicando se o certificado é ou não válido, de acordo com a regra que você especificar ali. É importante que você analise cuidadosamente antes de especificar isso no seu código, pois dessa forma, ele dará “bypass” em todos os certificados, independentemente se eles forem ou não válidos. Talvez isso é algo que seja legal em ambiente de desenvolvimento, mas em produção poderá representar uma vulnerabilidade de segurança.

Disponibilizando o WSDL Out-Of-Band

Quando criamos um serviço WCF, geralmente disponibilizamos um endpoint conhecido como MEX (Metadata Exchange), que tem a responsabilidade de expor o documento WSDL, para que os clientes sejam capazes de utilizar as informações fornecidas por ele, para criar toda a estrutura necessária para efetuar a comunicação com o respectivo serviço.

Os interessados em consumir aquele serviço, podem fazer uso de ferramentas que, dado o endereço até o documento WSDL, irão gerar o proxy localmente. Quando você faz a referência através do Visual Studio .NET ou do svcutil.exe, ao colocar o endereço do serviço, ele sai em busca de um endpoint que fornece o documento WSDL. Se esse endpoint não for encontrado, ele não conseguirá extrair as informações e, consequentemente, você não conseguirá referenciar o serviço.

Publicar o WSDL através de um endpoint é bem simples, mas temos sempre que diminuir a superfície de ataque, para assim evitar problemas que possam acontecer. Uma alternativa e tema deste artigo, é distribuir o documento WSDL “out-of-band”. Com isso, podemos enviar ou disponibilizar o documento que descreve o serviço para a pessoa certa, ou invés de deixar que todos o acessem.

Para isso, uma vez que o seu serviço estiver rodando, você pode extrair os documentos que o descrevem a partir do utilitário svcutil.exe. Ele tem uma opção que permite a exportação do WSDL, salvando isso em documentos locais, para que mais tarde você envie/disponibilize para os seus clientes. Sendo assim, tudo o que precisamos fazer é utilizar a opção /t:metadata, assim como é mostrado abaixo:

C:Temp>svcutil.exe /t:metadata http://localhost:9298/

Com isso, arquivos WSDL e XSD (que descrevem os tipos utilizados pelo serviço) serão gerados localmente. Quando o cliente receber esses arquivos, ele poderá gerar o proxy a partir do mesmo utilitário, só que ao invés de passar o endereço do serviço, ele informará o caminho até os documentos WSDL/XSD que compõem toda a descrição. Para gerar o proxy, informe o local dos arquivos e a linguagem, assim como é mostrado abaixo:

C:Temp>svcutil *.wsdl *.xsd /language:C#

Depois de executar esse comando, dois arquivos serão criados, sendo um deles o proxy em si e o outro o arquivo *.config, contendo todas as configurações necessárias para efetuar a comunicação com o serviço. Feito isso, tudo o que resta fazer, é adicionar esses arquivos na aplicação que irá consumir o serviço, e como o proxy nada mais é do que uma classe local, basta instanciá-la e invocar as operações.

ConfigurationName

O WCF permite a configuração de um serviço de forma declarativa e imperativa, assim como já falei aqui. Quando optamos pela forma declarativa, todas as configurações do serviço/cliente serão feitas em um arquivo de configuração (App.config/Web.config), e com isso, sempre temos que referenciar o serviço e o contrato com o seu nome completo, ou seja, incluindo o namespace até eles.

Para facilitar isso, temos a propriedade ConfigurationName, exposta através dos atributos ServiceBehaviorAttribute e ServiceContractAttribute, onde podemos definir um “alias” para o serviço e o contrato, respectivamente. Com isso, evitamos especificar o nome completo até os tipos. O código abaixo ilustra a utilização dessa propriedade nos dois atributos:

[ServiceContract(ConfigurationName = “MeuContrato”)]
public interface IContrato
{
    //…
}

[ServiceBehavior(ConfigurationName = “MeuServico”)]
public class Servico : IContrato
{
    //…
}

E, se repararmos no arquivo de configuração agora, teremos:

<services>
  <service name=”MeuServico”>
    <host>
      <baseAddresses>
        <add baseAddress=”http://localhost:9383″/&gt;
      </baseAddresses>
    </host>
    <endpoint address=”srv” binding=”basicHttpBinding” contract=”MeuContrato” />
  </service>
</services>

É importante dizer que isso não interfere no nome do serviço e do contrato que será exposto através do documento WSDL. Essas configurações são apenas características locais dos arquivos de configuração do serviço ou do cliente.

Host de Serviços em Aplicações Windows

Uma das grandes vantagens do WCF é que qualquer aplicação pode servir como hosting para ele. Isso quer dizer que além do tradicional IIS, podemos recorrer à outros tipos, como Windows Services, Console, Windows Forms ou WPF. Cada uma tem as suas vantagens e desvantagens, mas isso já foi abordado neste artigo.

Cada uma dessas opções de hosting, possuem implementações diferentes, e aplicações Windows Forms e WPF tem um sofrimento maior em relação às outras. Isso se deve ao fato de que aplicações Windows não gerenciam apenas o serviço, mas também há uma interface (formulários) para controlar. Ao utilizar um host próprio, temos que recorrer à classe ServiceHost que gerenciará a vida e a execução do serviço. O problema já começa aqui, ou seja, onde criar e manter a instância desta classe?

Antes de prosseguirmos, precisamos analisar o que são os Synchronization Contexts. Synchronization Contexts permite executarmos uma determinada tarefa em uma outra thread, diferente da qual estamos atualmente, representando uma espécie de “canal” entre as duas threads envolvidas, fazendo tudo o que for necessário para isso. Dentro do namespace System.Threading existe uma classe chamada SynchronizationContext, que representa esse “canal”, e o WCF interage com ele através da propriedade boleana UseSynchronizationContext do atributo ServiceBehaviorAttribute, que por padrão é True.

Na configuração padrão, quando criamos o ServiceHost e invocamos o método Open, o WCF irá verificar se há um synchronization context definido e o utilizará. Quando não existir ou a propriedade UseSynchronizationContext é definida explicitamente como False, as operações serão executadas em uma outra thread, que são extraídas do ThreadPool.

Como o Windows Forms cria automaticamente o SynchronizationContext no construtor de um formulário, quando criamos a instância da classe ServiceHost dentro dele, o WCF irá executar todas as operações nesta mesma thread (que também atende aos comandos do usuário (message loop)), pois o contexto já está criado. O exemplo abaixo ilustra isso:

public partial class CadastroDeClientes : Form
{
    private ServiceHost _host;

    public CadastroDeClientes()
    {
        //Neste momento, o context já está criado
        this._host = new ServiceHost(typeof(ServicoDeClientes), new Uri[] { });
    }
}

O problema desta técnica é que você sobrecarregará a thread de UI, já que ela terá que se preocupar com as operações do serviço e com os eventos tradicionais do formulário. Se você optar por abrir o host antes da chamada de um formulário, como por exemplo, dentro do método Main da aplicação, você ainda não terá o contexto estabelecido. Neste caso, qualquer manipulação que você, eventualmente, faça nos controles do formulário dentro das operações do serviço, precisarão de um tratamento especial, pois você não poderá manipular os controles em uma thread diferente da qual eles foram criados.

Finalmente, se você estiver com a propriedade UseSynchronizationContext definida como True (que é o padrão), e estiver consumindo o serviço no mesmo formulário que possui o ServiceHost criado, você terá problemas. Isso se deve ao fato de que a chamada para o serviço bloqueia a thread de UI, enquanto o WCF posta a mensagem para essa mesma thread para invocar o serviço (que está ocupada). Sendo assim, o deadlock será garantido.

Propagando exceções para o Silverlight

O WCF é uma das formas que temos para estabelecer a comunicação entre uma aplicação Silverlight e a aplicação (ASP.NET) que a hospeda. Com o WCF podemos permitir que o Silverlight se conecte e envie e/ou receba informações, pois como sabemos, ele roda dentro do navegador do usuário. Apesar de você não ter acesso a maior parte dos recursos do WCF, o básico você consegue fazer.

Antes da versão 3 do Silverlight, há uma grande dificuldade em propagar as exceções que esse serviço, eventualmente, disparasse. Qualquer exceção que ocorra dentro do WCF, a resposta é enviada ao cliente definindo o código de resposta do HTTP (Http Status Code) como 500, o que indica um erro interno do servidor e impede o navegador de ler qualquer resposta. Esse problema pode ser facilmente contornado, criando um message inspector, verificando antes de enviar a resposta para o cliente, se a mensagem é ou não uma fault. Caso seja, você altera o status do HTTP para 200 (OK) ao invés de 500. O código abaixo ilustra como criar esse behavior customizado:

public class SLFaultMessageInspector : IDispatchMessageInspector
{
    public void BeforeSendReply(ref Message reply, object correlationState)
    {
        if (reply.IsFault)
            reply.Properties[HttpResponseMessageProperty.Name] =
                new HttpResponseMessageProperty() { StatusCode = HttpStatusCode.OK };
    }

    //….
}

Isso ainda poderia ser feito na versão 2 do Silverlight, mas o cliente não conseguia interpretar a fault, ou melhor, traduzí-la para uma exceção, e permitir que o desenvolvedor trate o erro da forma tradicional. Já a versão 3, incorpora a capacidade ao Silverlight de processar as faults, podendo especificar os contratos de faults ou optar por não especificá-los, para assim propagar ao cliente os objetos que representam e trazem maiores informações sobre o problema que ocorreu do outro lado.

ASP.NET Session em serviços WCF

Como foi visto aqui, podemos utilizar as variáveis de sessão do ASP.NET para manter o estado de um serviço WCF. Para isso, basta habilitar o modo de compatibilidade com ASP.NET e você já terá acesso ao HttpContext (HttpContext.Current) em seu serviço.

Você precisa ter um certo cuidado aqui com dois detalhes: o primeiro deles é com relação a dependência que o seu serviço terá com o protocolo HTTP, pois dentro da classe que representa o serviço, você mencionará classes que fazem parte do ASP.NET, assim como a HttpContext, e com isso, você não conseguirá acessá-lo através de TCP. Já o segundo detalhe, é com relação ao consumo deste serviço. Ao contrário do gerenciamento de instâncias do WCF, a sessão do ASP.NET é utilizada de forma diferente, mas com a mesma finalidade, ou seja, de manter o estado das informações durante as requisições.

Quando você faz uso do modelo PerSession, ao fechar o proxy o WCF descartará a instância da classe do lado do serviço. Como no caso do ASP.NET é ele quem controla as variáveis de sessão, você precisará explicitamente removê-las. Para que você consiga descartar tudo o que tem na memória, precisará chamar o método Abandon da classe HttpSessionState. Para automatizar essa tarefa e não poluir a Interface do contrato com métodos que “limpam” a sessão, você pode implementar na classe do serviço a Interface IDisposable, que será invocada quando o proxy for encerrado.

Como sabemos, a configuração padrão da sessão, é manter um cookie do lado do cliente com o “Id”, para que ela consiga identificar o usuário. Para que isso funcione corretamente, precisamos habilitar o gerenciamento de cookies pelo binding, assim como é mostrado neste artigo. Com isso, o proxy do WCF encaminhará, automaticamente, os cookies nas futuras requisições. Mesmo que você utilize instâncias diferentes do proxy, devido ao recurso de caching, elas compartilharão as mesmas variáveis de sessão do lado do serviço.

Você pode consumir um serviço WCF na mesma aplicação em que ele está hospedado (comum em aplicações Silverlight). Mesmo que você utilize o mesmo diretório virtual/worker process, você não conseguirá dentro do serviço acessar as variáveis de sessão criadas no ASP.NET e vice-versa. Lembre-se de que o serviço nem sempre será consumido por essa mesma aplicação, e justamente por isso, é proibido. Se você precisar passar informações que estão armazenadas em variáveis de sessão do lado do cliente, tudo o que você precisa fazer, é enviá-las através do contrato.

Overloading de métodos no WCF

Um outro detalhe que funciona perfeitamente bem no C# ou qualquer linguagem orientado a objetos, é o overloading de métodos, onde você tem várias métodos com o mesmo nome, variando o tipo ou a quantidade de parâmetros. Podemos aplicar essa técnica também em Interfaces, e quando ela for implementada nas classes, teremos tais métodos a nossa disposição.

A questão é que, assim como a implementação explícita, quando aplicado em uma Interface que servirá como contrato de um serviço WCF, isso não funcionará. Mas isso não é uma limitação da plataforma, mas sim do documento WSDL, que não suporta algumas características de programação orientada à objetos, e a sobrecarga de métodos é uma delas. Com essa “limitação”, ao rodar um serviço (hosting) com algum método que possua algum overload, você receberá uma exceção do tipo InvalidOperationException, indicando que isso não é suportado.

Para resolver, basta recorrermos a propriedade Name do atributo OperationContractAttribute. Quando essa propriedade é omitida, o WCF assume o nome do método para publicar no documento WSDL. Eventualmente, você poderá customizar, escolhendo o nome que será exposto, já que você não está obrigado a manter o mesmo nome, que muitas vezes reflete algo intuitivo para a sua aplicação, mas incoerente com a operação que será publicada pelo serviço. O exemplo abaixo, ilustra como proceder para utilizar este atributo:

[ServiceContract]
public interface IContrato
{
   [OperationContract(Name = “RecuperarClientesPorEstado”)]
   string[] RecuperarClientes(string estado);

   [OperationContract(Name = “RecuperarClientesPorCidade”)]
   string[] RecuperarClientes(string estado, string cidade);
}

Ao referenciar esse serviço em um aplicação cliente, o mesmo enxergará dois métodos, com os respectivos nomes: “RecuperarClientesPorEstado” e “RecuperarClientesPorCidade”, baseando-se no alias que demos ao criar o contrato. Apesar da ferramenta criar dois métodos, você poderá customizar o proxy gerado, recriando os overloads e tornando a programação do lado do cliente mais intuitiva. Ao gerar o proxy – automaticamente – do lado do cliente, o contrato é criado da seguinte maneira:

[System.CodeDom.Compiler.GeneratedCodeAttribute(“System.ServiceModel”, “3.0.0.0”)]
[ServiceContractAttribute(ConfigurationName=”Servico.IClientes”)]
public interface IClientes {
    [
        OperationContract(
            Action=”http://tempuri.org/IClientes/RecuperarClientesPorEstado&#8221;, 
            ReplyAction=”http://tempuri.org/IClientes/RecuperarClientesPorEstadoResponse&#8221;)
    ]
    string[] RecuperarClientesPorEstado(string estado);

    [
        OperationContract(
            Action=”http://tempuri.org/IClientes/RecuperarClientesPorCidade&#8221;, 
            ReplyAction=”http://tempuri.org/IClientes/RecuperarClientesPorCidadeResponse&#8221;)
    ]
    string[] RecuperarClientesPorCidade(string estado, string cidade);
}

Para que você consiga refazer o overloading do lado do cliente, você precisa seguir a mesma técnica aplicada do lado do serviço, ou seja, adicionar a propriedade Name no atributo OperationContractAttribute, renomear as operações para “RecuperarClientes” e, finalmente, reimplementar o contrato na classe que representa o proxy. Desta forma, o cliente e o serviço trabalharão da forma tradicional (OO), enquanto em runtime, tudo será tratado com os “aliases”.

Configuração do WCF no cliente

Quando fazemos a referência a um serviço WCF em uma aplicação cliente, o arquivo de configuração sofre uma grande mudança, pois todas as configurações de acesso ao serviço (endpoints, bindings, behaviors, etc.), são colocadas dentro deste arquivo. Muitas vezes, o arquivo de configuração de uma aplicação já possui várias outras entradas, e colocar todas essas outras configurações, torna o arquivo muito extenso.

A partir do WCF 4.0, a Microsoft incluiu uma nova classe chamada ConfigurationChannelFactory<TChannel> (System.ServiceModel.Configuration), que herda diretamente da classe ChannelFactory<TChannel>. Com essa classe, podemos isolar as configurações do WCF em um arquivo de configuração a parte, dando uma maior flexibilidade, já que podemos criar um proxy (DLL) e distribuir o respectivo arquivo de configuração, e aquele que consume, não precisa fazer nada além de colocar o arquivo no diretório da aplicação.

Para trabalhar com essa classe, temos que recorrer a criação manual do proxy, assim como foi falado aqui. Primeiramente precisamos utilizar a classe ExeConfigurationFileMap, que recebe o nome de um arquivo de configuração, e que neste caso, conterá as configurações necessárias para o serviço. Em seguida, abrimos esse arquivo mapeado e o passamos para a classe ConfigurationChannelFactory<T>. Depois disso, tudo fica conforme já sabemos, invocando o método CreateChannel e, finalmente, o consumo das operações. O código abaixo ilustra esse exemplo:

ExeConfigurationFileMap map = new ExeConfigurationFileMap() { ExeConfigFileName = “wcf.config” };
Configuration config = ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationLevel.None);
ConfigurationChannelFactory<IContrato> factory =
       new ConfigurationChannelFactory<IContrato>(
              “NetTcpBinding_IContrato”,
              config,
              new EndpointAddress(“net.tcp://localhost:9292”));
IContrato proxy = factory.CreateChannel();
Console.WriteLine(proxy.Ping(“teste”));