Disponibilizando o WSDL Out-Of-Band

by Israel Aece 29. September 2009 08:34

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.

Tags:

WCF

Métodos Sincronizados

by Israel Aece 25. September 2009 12:00

Quando criamos uma classe e dentro dela colocamos métodos, estes podem ser acessados por múltiplas threads ao mesmo tempo. Caso você queira prevenir isso, ou seja, permitir que somente uma única thread por vez o acesse, você pode fazer uso de métodos sincronizados. Tudo o que você precisa fazer para isso funcionar, é decorar o método com o atributo MethodImplAttribute (namespace System.Runtime.CompilerServices), definindo em seu construtor a opção Synchronized, fornecida pelo enumerador MethodImplOptions, assim como é mostrado abaixo:

public class Processo
{
    [MethodImpl(MethodImplOptions.Synchronized)]
    public void Metodo()
    {
        //...
    }
}

Com isso, você terá a garantia de que somente uma thread por vez o executará, enquanto as outras que, eventualmente, cheguem a este mesmo método, ficarão em uma fila, esperando com que a thread atual finalize o seu trabalho.

Apesar de “proteger” as informações, você limitará o acesso à sua classe/tipo. Quando este atributo é aplicado à um método de instância, ele efetuará o bloqueio da instância como um todo (lock(this) { }); já se aplicá-lo em um método estático, ele bloqueará o tipo (lock(typeof(Processo)) { }). A implementação é fácil, mas você será prejudicado com os efeitos colaterais, pois terá maior contenção. Para melhorar o throughput, o ideal é você utilizar alguma técnica de locking que o próprio .NET Framework fornece, para assim refinar o bloqueio da sua classe/tipo.

Tags: ,

.NET Framework

ConfigurationName

by Israel Aece 25. September 2009 08:58

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"/>
      </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.

Tags:

WCF

Host de Serviços em Aplicações Windows

by Israel Aece 24. September 2009 21:42

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.

Tags: , ,

WCF

Propagando exceções para o Silverlight

by Israel Aece 23. September 2009 09:56

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.

Tags: , , ,

WCF

ASP.NET Session em serviços WCF

by Israel Aece 22. September 2009 10:46

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.

Tags: , , ,

ASP.NET | WCF

Overloading de métodos no WCF

by Israel Aece 21. September 2009 08:16

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", 
            ReplyAction="http://tempuri.org/IClientes/RecuperarClientesPorEstadoResponse")
    ]
    string[] RecuperarClientesPorEstado(string estado);

    [
        OperationContract(
            Action="http://tempuri.org/IClientes/RecuperarClientesPorCidade", 
            ReplyAction="http://tempuri.org/IClientes/RecuperarClientesPorCidadeResponse")
    ]
    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".

Tags: ,

WCF

Método Zip

by Israel Aece 20. September 2009 17:29

Um novo método (de extensão) foi adicionado à classe Enumerable na versão 4.0 do .NET, chamado de Zip. Dado duas coleções/arrays, esse método tem a finalidade de aplicar uma espécie de "zíper" entre eles, ou seja, agrupando os elementos correntes de cada coleção, onde o resultado é a combinação entre esses dois elementos. Para exemplificar, considere os dois arrays de inteiros:

int[] pares = new int[] { 0, 2, 4, 6, 8 };
int[] impares = new int[] { 1, 3, 5, 7, 9 };

foreach
(var item in impares.Zip(pares, (i, p) => string.Format("{0}, {1}, ", p, i)))
    Console.Write(item);

O resultado ficará: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9. O processamento se encerra quando não for encontrado um elemento "correspondente" na outra coleção.

Tags: ,

.NET Framework | C#

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