Sessão e Operações One-way

by Israel Aece 12. August 2010 08:58

O WCF fornece várias tipos de mensagens, e entre eles, temos as operações one-way. Esse tipo de operação deve ser utilizado quando o cliente não precisa de qualquer retorno (resultado ou erro), uma espécie de fire-and-forget.

Esse tipo de mensagem pode ter um comportamento estranho quando exposto através de um binding que suporte sessões nativamente, como é o caso do NetTcpBinding. O protocolo TCP estabelece um canal duplex entre o cliente e o serviço, mantendo uma espécie de conexão ativa, onde a qualquer momento um pode tranquilamente se comunicar com o outro.

É importante dizer que mensagens one-way não são assíncronas, mas o tempo de entrega é tolerável. O maior problema, do qual falava anteriormente, refere-se ao fechamento do proxy depois da mensagem one-way disparada ao destinatário, ou seja, quando invocamos a operação one-way, tão logo já conseguimos continuar trabalhando na aplicação cliente, mas se tentarmos fechar o proxy, ficaremos bloqueados até que a operação one-way seja completamente finalizada. Mas se a ideia é disparar e esquecer, porque eu preciso esperar, mantendo o proxy aberto até finalizar a operação?

Para ter o comportamento fire-and-forget a sério, uma das alternativas que temos é habilitar as mensagens (ou sessões) confiáveis. Isso tornará a comunicação entre as partes muito mais descritiva, onde além das mensagens normais, ainda haverão uma porção de outras mensagens que são geradas pelo protocolo WS-ReliableMessaging, indicando se a mensagem foi entregue, quando ela foi entregue e a ordem de chegada, permitindo ao cliente continuar sem a necessidade de esperar. Para habilitar as mensagens confiáveis, consulte este artigo.

Apesar disso funcionar, a comunicação entre as partes ficará bem mais volumosa, já que várias mensagens de infraestrutura serão trocadas para garantir a entrega da mensagem principal. Para evitar todo este overhead causado pelo protocolo WS-ReliableMessaging, podemos recorrer a construção de um binding customizado, empilhando os vários elementos para compor a comunicação via TCP, mas com um detalhe, que é a adição de um novo elemento, chamado de OneWayBindingElement. A adição deste elemento fará com que o canal de comunicação se comporte semanticamente como um fire-and-forget. O código abaixo ilustra o seu uso, via código imperativo e declarativo, respectivamente:

private static Binding CreateOneWayTcpBinding()
{
    BindingElementCollection currentElements = new NetTcpBinding().CreateBindingElements();
    BindingElementCollection bindingElements = new BindingElementCollection();

    bindingElements.Add(new OneWayBindingElement());

    foreach (BindingElement be in currentElements)
        bindingElements.Add(be);

    return new CustomBinding(bindingElements);
}

<system.serviceModel>
  <bindings>
    <customBinding>
      <binding name="OneWayTcpBinding">
        <oneWay />
        <binaryMessageEncoding />
        <tcpTransport />
      </binding>
    </customBinding>
  </bindings>
</system.serviceModel>

Só que utilizar este procedimento possui dois detalhes importantes: o primeiro é que ao configurá-lo, o canal somente permitirá a comunicação através do modelo one-way, e com isso, o contrato que você está expondo obrigatoriamente precisa definir todas as operações neste mesmo modelo. Se tiver qualquer operação neste contrato do tipo request/reply, ao tentar subir o serviço, você irá se deparar com uma exceção do tipo InvalidOperationException, indicando justamente isso. Se você conseguir garantir com que todas as operações sejam one-way, então utilizar esta técnica pode trazer uma grande melhora em performance, quando comparado à opção anterior.

O segundo detalhe é com relação ao modelo de gerenciamento de instância. Mesmo que você diga ao serviço que ele deve ser PerSession, ele não conseguirá manter a sessão, ou seja, terá o comportamento do modelo PerCall, pois cada chamada para uma operação one-way encerrará a "sessão".

Tags: , ,

WCF

Impacto de Timeouts no Proxy WCF

by Israel Aece 5. February 2010 08:32

Como comentei neste post, as exeções influenciam diretamente na vida útil do proxy de um serviço WCF. Da mesma forma, quando eventuais timeouts acontecem no serviço, isso também faz com que o proxy seja movido para um estado falho, mas não diretamente.

Isso acontece para aqueles bindings que suportam sessão, onde depois de um tempo de inatividade, a sessão que é mantida do lado do serviço para aquele cliente, será expirada, fazendo com que o canal de comunicação do serviço entre em um estado falho, não podendo mais receber requisições.

Neste momento, o proxy correspondente ainda continua aberto e íntegro, até que ele envie uma próxima requisição até o serviço que está falho, e que por sua vez, irá disparar uma exceção e retornará uma fault para o cliente, que ao recebê-la, irá mover o proxy para um estado falho e, consequentemente, nenhuma outra requisição poderá ser realizada a partir daquele proxy, a menos que você o reconstrua, podendo utilizar a mesma técnica mostrada no post anterior.

Tags: , ,

WCF

Impacto de Exceções no Proxy WCF

by Israel Aece 4. February 2010 11:17

Uma das preocupações que devemos ter em qualquer tipo de desenvolvimento é com o tratamento de erros. Eu já comentei neste artigo e vídeo as possibilidades que temos para fazer isso dentro do WCF, utilizando suas próprias características de interceptação, transformação (promoção) e propagação das exceções para faults.

Já sabemos que se conhecermos todos os eventuais problemas que podem acontecer, podemos já definí-los no contrato do serviço (através do atributo FaultContractAttribute), para que assim o cliente possa ser notificado do que realmente aconteceu. Só que ainda há uma questão que não foi tratada, que é justamente como fica o estado do proxy depois que a falha acontece no serviço?

A resposta para essa pergunta dependerá de qual tipo de exceção que está sendo disparada, e também de qual binding está sendo utilizado por aquele endpoint. Em grande parte dos cenários, vemos que o desenvolvedor utilizar a instância de um mesmo proxy para efetuar várias chamadas para o mesmo serviço, não importando se é ou não para uma mesma operação.

Envolver as chamadas para as operações dentro de um bloco try/catch, evita apenas que a aplicação cliente saiba se comportar quando um determinado erro ocorre. Mas dependendo do que aconteceu no serviço, você não conseguirá reutilizar a mesma instância do proxy. Depois da falha, qualquer tentativa de comunicacão não chegará mais até o serviço, resultando assim naquela famosa exceção do tipo CommunicationObjectFaultedException, com a seguinte mensagem:

System.ServiceModel.CommunicationObjectFaultedException: The communication object, System.ServiceModel.Channels.ServiceChannel, cannot be used for communication because it is in the Faulted state

Digamos que você não está preocupado com as exceções que ocorrem dentro do seu serviço. Dentro de uma determinada operação, uma exceção CLR (ArgumentNullException, IndexOutOfRangeException, etc.) é disparada. No exemplo abaixo, estou efetuando o teste para saber se o parâmetro é ou não nulo, e sendo, uma exceção do tipo ArgumentNullException está sendo disparada:

public string Ping(string value)
{
    if (value == null)
        throw new ArgumentNullException("value");

    return value;
}

Como exceções não tratadas do lado do serviço são consideradas um risco, o canal de comunicação (proxy) não ficará mais disponível. Para os bindings que suportam sessão (NetTcpBinding, WSHttpBinding, etc.), isso quer dizer que se você tentar invocar uma segunda requisição, ele estará em um estado inválido (Faulted), o que obrigará a você recriar o proxy (instanciar novamente) para depois utilizá-lo. A solução para este caso, já foi comentada nos artigos que referencie acima, que é trabalhar explicitamente com a classe FaultException (ou até mesmo FaultException<T>):

public string Ping(string value)
{
    if (value == null)
        throw new FaultException("value");

    return value;
}

Desta forma, o canal de comunicação não será afetado, e seguramente você poderá utilizá-lo para efetuar novas requisições. Já aqueles bindings que não suportam sessão, como é o caso do BasicHttpBinding ou do WebHttpBinding (utilizado para REST/AJAX), não serão danificados, mesmo se você não estiver se preocupando com o uso da classe FaultException.

Como muitas vezes as operações recorrem a outras classes, e que muitas você não tem acesso à elas, então você dificilmente conseguirá mapear todas as exceções que podem acontecer. Para garantir que o proxy consiga se "restaurar" de um estado falho, você pode recorrer ao evento Faulted, exposto pela interface ICommunicationObject e também pela classe ClientBase<TChannel>. Este evento é disparado quando o proxy entre em estado falho, e assinando este evento, você poderia reconstruir o proxy no exato momento, como eu mostro no código abaixo:

ChannelFactory<IContrato> factory = 
    new ChannelFactory<IContrato>(new NetTcpBinding(), new EndpointAddress("net.tcp://localhost:3732/srv"));

IContrato proxy = factory.CreateChannel();
((ICommunicationObject)proxy).Faulted += (o, e) => proxy = factory.CreateChannel();

Tags: , ,

WCF

Gerenciamento de Channels

by Israel Aece 1. February 2010 12:38

Algum tempo atrás eu falei sobre os internals de um proxy WCF. Como eu havia dito, existe um grande overhead quando criamos um novo proxy, que está condicionado as complexidades que podem ou não estarem habilitadas (segurança, transações, mensagens confiáveis, etc.).

Como disse naquele mesmo post, o ponto mais custoso da criação, que é a factory (ChannelFactory<TChannel>), está sendo reutilizada, em nível de AppDomain, ou seja, mesmo que você não mantenha a instância do proxy gerado (ClientBase<TChannel>) pela IDE do Visual Studio ou pelo utilitário svcutil.exe, o runtime do WCF irá reciclar a factory, e em seguida, a colocará em um cache.

Quando estabelecemos a comunicação entre o cliente e o serviço, utilizamos um canal de comunicação, referido também como channel. Estes são utilizados por nós, na maioria das vezes implicitamente, para efetuar as requisições para o respectivo serviço. Na verdade, é a factory que fornece um método chamado CreateChannel, que retorna a instância de um TransparentProxy. Este tipo nos permite efetuar uma conversão para o contrato exposto pelo documento WSDL, e assim invocar as operações como se elas fossem simples métodos locais. Se você abrir a classe que é gerada quando você faz a referência à um serviço, verá que dentro das operações sempre há uma chamada para a propriedade Channel, que por sua vez, irá até o método CreateChannel.

Como a parte custosa já está sendo reutilizada de forma performática pelo WCF, as vezes surge a dúvida se devemos ou não manter um outro cache, mas este para armazenar as instâncias dos channels que são retornados pelo método CreateChannel, mas que a classe ClientBase<TChannel> não se preocupa com isso. O objeto retornado por esse método implementa a interface ICommunicationObject, que fornece entre vários membros, um método autoexplicativo chamado Close, que é responsável por encerrar mover o estado do proxy corrente para Closed e também descartar os recursos que ele utiliza.

Assim como todo recurso caro, é importante você sempre descartá-lo quando não precisar mais dele. Isso também é o caso dos channels. Mas é importante você analisar cuidadosamente o cenário. Se você faz chamadas subsequentes, não convém a todo momento invocar o método CreateChannel para criar um novo canal; reutilize-o durante essas chamadas, e somente feche-o quando realmente não precisar mais dele, dentro daquele escopo.

Da mesma forma, antes de descartá-lo completamente, analise se você não pode manter um cache para estes channels. Fazer pooling de channels é uma boa alternativa para diminuir ainda mais os custos de criação deles. Você pode manter os channels desde que:

  • Não há um contexto de segurança exclusivo para cada usuário (SCT).
  • O mesmo channel não é utilizado por múltiplas threads ao mesmo tempo.
  • Não há manutenção de estado.

Na primeira situação o problema acontece porque as credenciais são definidas durante a criação da factory, e não podem mais serem alteradas. Já o segundo cenário, acontece porque os channels são thread-safe, ou seja, eles não dão suporte ao envio de mensagem de forma concorrente. Se em uma thread A você está utilizando o channel C1 para mandar uma mensagem grande, a thread B que utilizará o mesmo channel C1, somente conseguirá enviar a mensagem depois que a thread A finalizar. A última das situações implica quando você mantém uma sessão entre o cliente e o serviço, pois ela está fortemente ligada ao channel correspondente.

Tags: ,

WCF

Mensagens Assíncronas com ChannelFactory

by Israel Aece 18. November 2009 10:41

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.

Tags: , , ,

Async | WCF

Configuração do WCF no cliente

by Israel Aece 20. September 2009 16:01

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

Tags: ,

WCF

Internals - Proxy de serviços WCF

by Israel Aece 17. September 2009 08:18

Para aqueles que desejam consumir um serviço (WCF ou não) através da infraestrutura do WCF que existe do lado do cliente, existem duas opções para referenciá-lo na aplicação cliente, e cada uma dessas opções fornecem suas vantagens e desvantagens. A finalidade deste artigo é tentar descrever como as coisas funcionam nos bastidores, para conseguirmos tirar o melhor proveito disso.

A mais comum entre as formas de acesso um serviço, é a geração do proxy de forma automática, que através da IDE do Visual Studio ou do utilitário svcutil.exe, basta que eu informe o endereço do documento WSDL, e uma classe será criada com toda a estrutura necessária para gerenciar e invocar operações do serviço remoto. Nesta opção, todos os tipos que, eventualmente, o serviço exponha, serão automaticamente reconstruídos do lado do cliente, para que o mesmo seja capaz de enviar e/ou receber tais tipos.

Entre os tipos que são reconstruídos do lado do cliente, uma Interface é criada representando o contrato do serviço, contendo todas as operações que ele fornece. Além disso, uma classe também é criada, herdando da classe abstrata ClientBase<TChannel>. Essa classe é a que conhecemos como "proxy". Ao herdar de ClientBase<TChannel>, o parâmetro genérico TChannel é substituído pela Interface que representa o contrato do serviço, que como vimos, foi reconstruída do lado do cliente. Além disso, a Interface do contrato do serviço também é implementada nesta classe, ou seja, ao instanciar o proxy, veremos os métodos que foram disponibilizados no serviço. Durante o tempo de execução, ao invocar esses métodos locais, o WCF encaminhará a requisição para o serviço.

Essa classe nada mais é do que um wrapper, que visa facilitar a vida de quem consome o serviço fornecendo, além dos métodos que refletem as suas operações, métodos que gerenciam a vida do canal de comunicação, tais como Open, Close, Abort, etc. Ao criar a instância desta classe ou descartá-la, uma série de tarefas custosas e complexas são realizadas, tais como: carregar as configurações do arquivo de configuração, criar toda a árvore de tipos que descreve o serviço, construção da channel stack (a complexidade depende das funcionalidades habilitadas) e o descarte dos recursos que a mesma utiliza para efetuar a comunicação.

Internamente essa classe mantém/referencia um objeto do tipo ChannelFactory<TChannel>, que é uma factory responsável por criar e configurar, efetivamente, o canal de comunicação entre o cliente e o serviço. Antes do .NET Framework 3.5 + SP1, essa classe era instanciada uma única vez, e depois é mantida viva durante a instância do proxy. Na versão mais recente, não existe mais essa afinidade, ou seja, a instância da ChannelFactory<TChannel> é mantida em nível do AppDomain, e sendo assim, mesmo que você crie e descarte o proxy a todo o momento, grande parte do processo complexo e custoso, será mantido e reutilizado independentemente de quantas instâncias do proxy você venha a criar, gerando uma espécie de cache.

Esse cache melhora consideravelmente o custo de inicialização, que na maioria das vezes, é sempre igual. Esse cache trabalha no formato MRU (most recently used), e está "limitado" à 32 objetos, e quando essa capacidade for atingida, os últimos vão sendo removidos. A cada instância do proxy, antes do WCF efetivamente criar a instância da classe ChannelFactory<TChannel> correspondente, ele interroga o cache a procura deste objeto (que atenda a essas mesmas características), e caso o encontre, evitará a criação de um novo.

Um cuidado especial que se deve ter é com relação as versões que temos no cache. O construtor do proxy possui vários overloads, e os objetos que estão colocados no cache são versionados de acordo com esses parâmetros. Aqueles overloads que não aceitam um Binding como parâmetro, seguramente, objetos que estão no cache serão reutilizados, já que os demais parâmetros são imutáveis. Quando um Binding é passado como parâmetro para um dos construtores, o caching será desabilitado, já que não há como garantir que as informações definidas na instância do binding serão sempre as mesmas. A mesma regra se aplica quando você acessa as propriedades ChannelFactory, Endpoint e ClientCredentials do proxy antes do canal de comunicação ser criado e aberto.

Voltando as formas que temos de invocar um serviço, a segunda possibilidade é utilizar o ChannelFactory<TChannel> diretamente, sem criar o proxy por algumas das ferramentas que vimos acima. Essa técnica nos permite ter um maior controle do caching, já que essa funcionalidade ficará sob nossa responsabilidade. Você pode manter a factory em algum ponto do teu código, e quando precisar de um novo canal de comunicação com as mesmas configurações, basta invocar o método CreateChannel, que ele te retorna uma instância configurada. O ponto negativo desta técnica, é que o cliente já tem que, de alguma forma, conhecer os tipos e contratos que são expostos pelo serviço, cenário comum quando utilizamos assemblies compartilhados. O código abaixo ilustra como podemos proceder para fazer isso:

ChannelFactory<IContrato> factory =
    new ChannelFactory<IContrato>(new NetTcpBinding(), "net.tcp://localhost:3922/srv");

IContrato proxy = factory.CreateChannel();
Console.WriteLine(proxy.Ping("Israel"));

Além de você já precisar conhecer os tipos, para invocar qualquer método que gerencie a vida do proxy (Open, Close, Abort, etc.), você precisará converter explicitamente para ICommunicationObject. E, para finalizar, quando não precisar mais da factory, tudo o que precisa fazer é também invocar o seu método Close, que descarta todos os recursos custosos que ela armazena. O exemplo abaixo ilustra como podemos proceder para encerrar o proxy e, eventualmente, a factory:

((ICommunicationObject)proxy).Close();

//Eventualmente
factory.Close();

Conclusão: Ambas técnicas tem seus pontos positivos e negativos. Muito do que vimos neste artigo, acaba sendo gerenciado de forma automática pelo WCF, mas conhecendo alguns detalhes internos como estes, nos permite identificar problemas de performance que, eventualmente, podemos estar sofrendo.

Tags: , , ,

WCF

Powered by BlogEngine.NET 1.5.0.0
Theme by Mads Kristensen

Sobre

Meu nome é Israel Aece e sou especialista em tecnologias de desenvolvimento Microsoft, atuando como desenvolvedor de aplicações para o mercado financeiro utilizando a plataforma .NET. [ Mais ]

Twitter

Host