WCF - Usando o MsmqIntegrationBinding

by Israel Aece 21. December 2010 12:50

Há algum tempo eu mostrei neste artigo como podemos fazer o uso do Message Queue com WCF, e lá foi discutido todos os benefícios de se envolver uma fila na comunicação entre as duas partes. A exigência daquele modelo de utilização é que ambos os lados precisam utilizar a API do WCF para se comunicar com o Message Queue.

O problema é que as vezes já temos uma solução implementada e operante. Antes do WCF, a única forma que tínhamos para interagir com o Message Queue na plataforma .NET era através das classes contidas no assembly System.Messaging.dll. Além disso há outras soluções que recebem ou enviam mensagens para uma fila, escritas em outras tecnologias.

Para trabalhar com Message Queue no WCF, temos dois bindings: NetMsmqBinding e MsmqIntegrationBinding, onde o primeiro deles já foi esgotado naquele outro artigo. O segundo trata-se de um binding criado exclusivamente para interagir com filas em que as mensagens sejam postadas ou recebidas por outras tecnologias que não o WCF. A finalidade deste artigo é mostrar como proceder para utilizar este binding e interoperar com soluções que já estão em funcionamento.

Para exemplificar vamos interoperar com aplicativos que recebem e postam mensagens utilizando a API System.Messaging.dll, que existe desde a primeira versão do .NET Framework. Abaixo temos dois cenários distintos, separados em duas pastas: WCFNoCliente e WCFNoServidor. No primeiro cenário utilizaremos o WCF para enviar uma mensagem para a fila e o serviço irá extrair a mensagem de lá utilizando as classes contidas no assembly System.Messaging.dll. No segundo cenário, será o inverso, ou seja, utilizaremos as classes contidas no assembly System.Messaging.dll para enviar a mensagem, e utilizaremos o WCF no serviço para extrair a mensagem da fila e processá-la. A imagem abaixo ilustra a estrutura dos projetos de teste:

O primeiro passo é desenhar o contrato do serviço, que irá formalizar a comunicação. A ideia será ter um serviço que processa pedidos de algum comércio eletrônico. Sendo assim teremos duas classes: Pedido e Cliente, que são autoexplicativas. Cada uma delas tem propriedades que as descrevem, que também não precisam ser exibidas aqui. Uma característica de serviços que são expostos através do Message Queue, é que as operações eles devem ser sempre one-way, assim como já foi discutido anteriormente. Abaixo temos a definição do contrato que será utilizado como exemplo:

[ServiceContract]
[ServiceKnownType(typeof(Pedido))]
[ServiceKnownType(typeof(Cliente))]
public interface IComercioEletronico
{
    [OperationContract(IsOneWay = true, Action = "*")]
    void AdicionarPedido(MsmqMessage<Pedido> mensagem);
}

Infelizmente por se tratar de um cenário específico, a nossa operação deverá ter apenas um único parâmetro, e ele deve ser do tipo MsmqMessage<T>, que está debaixo do namespace System.ServiceModel.MsmqIntegration, e representará o body da mensagem. Caso você precise de mais parâmetros, então o correto é que você inclua isso em uma mesma classe e a defina como o parâmetro genérico T da classe MsmqMessage<T>. É importante dizer que o contrato irá definir a estrutura da mensagem a ser enviada para a fila (WCFNoCliente) ou a mensagem a ser extraída da fila (WCFNoServidor).

um detalhe importante na criação do contrato é o uso do atributo ServiceKnownTypeAttribute, que é exigido para todos os elementos mencionados direta ou indiretamente no contrato. Isso é necessário para que o serializador conheça todos os tipos envolvidos, caso contrário, a mensagem não será recebida pelo WCF, e ao ligar o tracing, vamos nos deparar com a seguinte mensagem de erro:

System.ServiceModel.ProtocolException: An error was encountered while deserializing the message. The message cannot be received. ---&gt; System.Runtime.Serialization.SerializationException: An error occurred while deserializing an MSMQ message's XML body. The message cannot be received. Ensure that the service contract is decorated with appropriate [ServiceKnownType] attributes or the TargetSerializationTypes property is set on the MsmqIntegrationBindingElement.

Utilizando o WCF no Servidor

Como será um serviço WCF que irá extrair as mensagens da fila, o primeiro passo aqui é a implementação do contrato na classe que representará o serviço. Aqui tudo é muito parecido com um serviço WCF tradicional, mas com uma pequena diferença que está na operação. Como a classe MsmqMessage<T> representa a mensagem que está dentro da fila, ela é muito mais do que a instância da classe Pedido, ou seja, temos outras propriedades que estão relacionadas ao protocolo.

Entre as várias propriedades expostas pela classe MsmqMessage<T> temos a propriedade Body, que retorna um object que representa a instância da classe que foi serializada no corpo da mensagem pelo cliente, que no nosso caso é a classe Pedido. Abaixo temos a implementação do serviço com o processamento da mensagem:

public class ServicoDeComercioEletronico : IComercioEletronico
{
    [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
    public void AdicionarPedido(MsmqMessage<Pedido> mensagem)
    {
        Pedido pedido = mensagem.Body as Pedido;

        if (pedido != null)
        {
            Console.WriteLine("---- Processando o Pedido: {0}", pedido.Codigo);
            Console.WriteLine("---- Cliente: {0}", pedido.Cliente);
            Console.WriteLine("---- Valor: {0:N2}", pedido.ValorTotal);
            Console.WriteLine("---- Data: {0:dd/MM/yyyy HH:mm}", pedido.Data);
            Console.WriteLine();
        }
    }
}

Note que decoramos a operação com o atributo OperationBehaviorAttribute definindo as propriedades TransactionScopeRequired e TransactionAutoComplete para True, pois utilizando o binding MsmqIntegrationBinding não temos o controle explícito das transações, que para isso exige que o binding suporte sessões, e não é o caso do MsmqIntegrationBinding.

Depois da implementação definida, agora chega o momento de configurar o serviço. Como já sabemos, vamos recorrer ao binding MsmqIntegrationBinding. Para uma fácil interoperabilidade, o endereço em que o serviço é exposto deve utilizar o scheme msmq.formatname ao invés do net.msmq, que permite entre vários modelos, o acesso direto a fila (através de format-name), sem a necessidade de contatar o Active Directory (AD). Abaixo temos a configuração declarativa do serviço:

<system.serviceModel>
  <services>
    <service name="Servico.ServicoDeComercioEletronico">
      <endpoint address="msmq.formatname:DIRECT=OS:.\private$\MinhaFila"
                binding="msmqIntegrationBinding"
                bindingConfiguration="bindingConfig"
                contract="Biblioteca.IComercioEletronico" />
    </service>
  </services>
  <bindings>
    <msmqIntegrationBinding>
      <binding name="bindingConfig">
        <security mode="None" />
      </binding>
    </msmqIntegrationBinding>
  </bindings>
</system.serviceModel>

Com o serviço criado, podemos agora inicializarmos a criação do cliente. Obviamente que não iremos referenciar o serviço, mesmo porque nem criamos o endpoint que expõe o documento WSDL. A ideia aqui será utilizar os tipos que estão dentro do assembly System.Messaging.dll para enviar a instância da classe Pedido para a fila, e utilizar o WCF do outro lado para processar.

Depois do assembly referenciado na aplicação, instanciamos a classe MessageQueue e informamos o caminho para a fila (via path-name). Depois instanciamos a classe Pedido e a configuramos com os dados do Pedido que foi realizado e, finalmente, enviamos a instância para a fila através do método Send. Neste momento, a instância da classe Pedido será serializada como o body da mensagem.

using (MessageQueue queue = new MessageQueue(@".\private$\MinhaFila"))
{
    Pedido pedido = new Pedido();
    pedido.Codigo = 123;
    pedido.Data = DateTime.Now;
    pedido.ValorTotal = 1983.81M;
    pedido.Cliente = new Cliente() { Codigo = 39, Nome = "Israel Aece" };

    queue.Send(pedido, MessageQueueTransactionType.Single);
    Console.WriteLine("Mensagem Enviada com Sucesso.");
}

Observação: Como a fila é transacionada, precisamos definir o tipo da transação a ser utilizada pelo Message Queue no momento do envio da mensagem. Se o controle da transação está sendo controlada por um elemento externo, como é o caso do TransactionScope, então é necessário definir o MessageQueueTransactionType para Automatic, para que o envio da mensagem possa participar da mesma transação, criada previamente.

Utilizando o WCF no Cliente

Neste cenário inverteremos as tecnologias que utilizamos. Agora no serviço utilizaremos os tipos do assembly System.Messaging.dll e no cliente o WCF. Aqui criaremos a instância da classe MessageQueue com a finalidade de extrair a mensagem que lá foi postada, enviada por um cliente que utiliza o WCF. E para isso utilizaremos o método Receive, que da mesma forma que o método Send, também precisa estar protegido por uma transação.

O método Receive retorna uma instância da classe Message (namespace System.Messaging), que entre várias propriedades, temos a Body, que retorna um object representando a classe que foi serializada, e que no nosso caso é a classe Pedido. O código abaixo ilustra como fazemos para extrair a mensagem:

using (MessageQueue queue = new MessageQueue(@".\private$\MinhaFila"))
{
    queue.Formatter = 
        new XmlMessageFormatter(new Type[] { typeof(Pedido), typeof(Cliente) });

    Pedido pedido = queue.Receive(MessageQueueTransactionType.Single).Body as Pedido;

    if (pedido != null)
    {
        Console.WriteLine("---- Processando o Pedido: {0}", pedido.Codigo);
        Console.WriteLine("---- Cliente: {0}", pedido.Cliente);
        Console.WriteLine("---- Valor: {0:N2}", pedido.ValorTotal);
        Console.WriteLine("---- Data: {0:dd/MM/yyyy HH:mm}", pedido.Data);
        Console.WriteLine();
    }
}

Da mesma forma que fizemos acima com o contrato do WCF, decorando-o com o atributo ServiceKnownTypeAttribute, temos também que mencionar quais sãos os tipos que compõem o body da mensagem que está na fila, e para isso podemos recorrer a instância da classe XmlMessageFormatter, e em seu contrutor informar um array contendo os tipos envolvidos.

Como não há referência do serviço no cliente, precisamos fazer com que o cliente conheça qual é o contrato do serviço, e justamente por isso que ele foi colocado em uma DLL a parte, chamada de Biblioteca. Utilizamos no cliente a classe ChannelFactory<TChannel> para criar a canal de comunicação entre a aplicação cliente e a fila.

Além do contrato, devemos também informar o binding que irá reger a comunicação, e que como sabemos, será o binding MsmqIntegrationBinding. Em seguida informarmos o endereço (format-name) da fila em que a mensagem será postada. Com a factory criada, agora podemos criar a instância do canal que é efetivamente o proxy:

using (ChannelFactory<IComercioEletronico> factory =
    new ChannelFactory<IComercioEletronico>(
        new MsmqIntegrationBinding(MsmqIntegrationSecurityMode.None),
        @"msmq.formatname:DIRECT=OS:.\private$\MinhaFila"))
{
    var proxy = factory.CreateChannel();

    Pedido pedido = new Pedido();
    pedido.Codigo = 123;
    pedido.Data = DateTime.Now;
    pedido.ValorTotal = 1983.81M;
    pedido.Cliente = new Cliente() { Codigo = 39, Nome = "Israel Aece" };

    proxy.AdicionarPedido(new MsmqMessage<Pedido>(pedido));
    Console.WriteLine("Mensagem Enviada com Sucesso.");
}

Note que depois da instância da classe Pedido criada e configurada, ela não é passada diretamente para o método AdicionarPedido. Como foi comentado acima, o contrato impõe que a classe Pedido seja envolvida por um wrapper (MsmqMessage<T>), que representará a mensagem dentro do fila, e a instância da classe Pedido será armazenada/serializada no body dela.

Assincronismo

Como sabemos, uma das características do uso do Message Queue é garantir o assincronismo, e permitir ao cliente enviar mensagens ao serviço mesmo que ele esteja offline, mascarando assim eventuais problemas que existam na comunicação entre as duas partes envolvidas.

Conclusão: No decorrer deste artigo vimos que podemos substituir a tecnologia que existe de um dos lados para uma tecnologia mais recente, como é o caso do WCF, e com isso tirar todo o proveito de seus recursos, mas tendo em mente que existem algumas poucas limitações e imposições.

UtilizandoMsmqIntegrationBinding.zip (20.48 kb)

Tags:

Interoperabilidade | WCF

Substituindo ASMX por WCF no servidor

by Israel Aece 3. September 2010 10:22

Os ASP.NET Web Services (ASMX) são baseados na especificação WS-I Basic Profile, gerando as mensagens na versão 1.1 do SOAP. Eles foram que foram introduzidos desde a primeira versão do .NET Framework, e mais recentemente foram substituídos pelo WCF.

Para manter a interoperabilidade, a Microsoft criou um binding chamado BasicHttpBinding, que possui as mesmas características dos serviços ASMX. Com isso, podemos substituir tanto o serviço quanto o cliente utilizando a API do WCF. Ao optar pela substituição do lado do serviço, precisamos nos preocupar com a SOAPAction.

A SOAPAction é um header que é incluído na requisição HTTP, e determina intenção da requisição SOAP. Esse header é representado por uma URI que não aponta necessariamente para um local válido na internet. O problema reside justamente aqui. Imagine que temos um serviço (ASMX), que disponibiliza apenas uma única operação:

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class Servico : System.Web.Services.WebService
{
    [WebMethod]
    public string Ping()
    {
        return "ping!";
    }
}

Os clientes que o consomem devem enviar as requisições da seguinte forma:

POST /Servico.asmx HTTP/1.1
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; MS Web Services Client Protocol 2.0.50727.4952)
Content-Type: text/xml; charset=utf-8
SOAPAction: "http://tempuri.org/Ping"
Host: 127.0.0.1:51982
Content-Length: 288
Expect: 100-continue
Connection: Keep-Alive

<?xml version="1.0" encoding="utf-8"?>
    <soap:Envelope ...>
        <soap:Body>
            <Ping xmlns="http://tempuri.org/" />
        </soap:Body>
</soap:Envelope>

Agora queremos substituir o serviço para o WCF. Optamos então por criar o contrato e, consequentemente, o implementamos em uma classe que representará o serviço. A interface do contrato está definida abaixo:

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

O ponto mais importante é expor o serviço através do binding BasicHttpBinding, que não será abordado aqui. Se analisarmos o WSDL gerado pelo serviço, veremos que a SOAPAction gerada é: http://tempuri.org/IContrato/Ping. Como podemos perceber, ela leva o nome do contrato. Com isso, os clientes existentes não conseguirão mais enviar mais mensagens para o serviço, já que o WCF utiliza a SOAPAction para identificar a operação a ser disparada, e como ela é diferente, não a encontrará. Abaixo temos a descrição do erro que ocorre:

Unhandled Exception: System.Web.Services.Protocols.SoapHeaderException: The message with Action 'http://tempuri.org/Ping' cannot be processed at the receiver, due to a ContractFilter mismatch at the EndpointDispatcher. This may be because of either a contract mismatch (mismatched Actions between sender and receiver) or a binding/security mismatch between the sender and the receiver.  Check that sender and receiver have the same contract and the same binding (including security requirements, e.g. Message, Transport, None).

Para resolvermos este problema, temos que recorrer a propriedade Action que é definida no atributo OperationContractAttribute, e lá colocarmos a mesma SOAPAction que os clientes estão enviando, mantendo assim a compatibilidade e não sendo necessária qualquer mudança por parte daqueles que o consomem.

[ServiceContract]
public interface IContrato
{
    [OperationContract(Action = "http://tempuri.org/Ping")]
    string Ping();
}

Tomando este cuidado, podemos tranquilamente substituir serviços construídos em ASMX para WCF, sem afetar qualquer cliente que já consuma o mesmo. É importante dizer também que é possível consumir um serviço escrito em ASMX através da API do WCF do lado cliente, mas isso está fora do escopo deste artigo. E para finalizar, sempre se atente aos namespaces.

Tags: ,

Interoperabilidade | WCF

WCF - MessageContracts

by Israel Aece 24. May 2009 16:32

Quando desenvolvemos serviços WCF geralmente iniciamos com a criação do contrato, suas respectivas operações e possíveis parâmetros que elas aceitam e/ou retornam. Os tipos que são enviados e recebidos por um serviço podem ser desde um tipo simples, como um DateTime, Integer ou String até mesmo tipos mais complexos, como classes que representam alguma estrutura específica da nossa aplicação.

Esses dados (simples ou complexos) que definimos no contrato serão sempre serializados e enviados como parte do body da mensagem SOAP, ficando sob responsabilidade do WCF, montar a mensagem (headers e body) de acordo com as informações que serão enviadas ao destino (body) e como o binding será configurado (headers). Mas poderemos ter situações em que esse comportamento padrão não nos atenderá, e é neste momento que entra em cena os contratos de mensagem, ou Message Contracts.

Através dos contratos de mensagem, podemos ter o controle total da estrutura da mensagem SOAP, customizando como e onde os dados que fazem parte do contrato serão acomodados dentro da mesma, já que podem fazer parte do header ou do body. Em algumas situações você precisará dessa customização, como por exemplo, quando você necessitar de uma melhor interoperabilidade com um determinado cliente, que possivelmente poderá exigir a mensagem em um formato diferente do qual o WCF cria. Além disso, você poderá ter um objeto não fará somente parte do body, mas também há propriedades dentro dele que deverão ser inseridos como elementos na coleção de headers.

Quando formos trabalhar com contratos de mensagem, além dos contratos que somos obrigados a criar em qualquer tipo de serviço (interface que representa o contrato do serviço e as classes que serão utilizadas como contratos de dados), temos também que criar as classes que representarão o contrato (formato) da mensagem que será enviada/recebida pela operação. Nesse contrato, basicamente temos propriedades que expõem tipos (complexos ou não) e que serão definidas como parte dos headers ou do body da mensagem.

Em primeiro lugar, precisamos definir que a classe será utilizada como contrato de mensagem, e para isso, decoramos ela com o atributo chamado MessageContractAttribute. É importante dizer que as classes que servem como contrato de mensagem devem, obrigatoriamente, ter um construtor sem parâmetros. Em seguida, para determinar se um campo desta classe vai fazer parte dos headers ou body, você deverá utilizar os atributos MessageHeaderAttribute ou MessageBodyMemberAttribute, respectivamente.

Para exemplificar o uso de contratos de mensagem, vamos imaginar a seguinte situação: uma aplicação poderá enviar para o serviço a relação de contas de um determinado cliente que ela quer recuperar os respectivos saldos. O serviço, por sua vez, extrairá de algum repositório o saldo atual de cada uma das contas, e retornará para o solicitante. Neste caso, teremos duas classes: CriterioDeBusca e ResultadoDaBusca.

A primeira delas representará a mensagem que o cliente irá enviar para o serviço. Essa classe disponibiliza três campos: Cliente, Codigo e Contas, utilizadas para a aplicação informar o cliente e as contas que ele deseja extrair o saldo. Já a classe ResultadoDaBusca irá representar o resultado da pesquisa, com os campos Cliente, Codigo e Saldos. A última propriedade desta classe retorna um array, onde cada elemento é representa por um objeto do tipo Saldo, que é composto pela identificação da conta e seu respectivo saldo. Abaixo temos a estrutura de cada uma delas:

[MessageContract]
public class CriterioDeBusca
{
    [MessageHeader] public string Cliente;
    [MessageHeader] public int Codigo;
    [MessageBodyMember] public string[] Contas;
}

[MessageContract(IsWrapped = true, WrapperName = "contas")]
public class ResultadoDaBusca
{
    [MessageHeader] public string Cliente;
    [MessageHeader] public int Codigo;
    [MessageBodyMember] public Saldo[] Saldos;
}

public class Saldo
{
    public string Conta { get; set; }
    public decimal Valor { get; set; }
}

Como podemos notar, as classes CriterioDeBusca e ResultadoDaBusca estão decoradas com o atributo MessageContractAttribute, que diz ao runtime do WCF que elas devem ser consideradas como a mensagem SOAP em si, e não como simples contratos de dados. Os campos destas classes também possuem os atributos para determinar o que irá compor o header e o body.

Já comentamos a finalidade de cada atributo que foi decorado nos membros acima. Apesar de eles estarem com a configuração padrão (com exceção da classe ResultadoDaBusca), ainda há uma série de propriedades que cada um deles disponibiliza, para que possamos definir algumas outras configurações que serão utilizadas pelo WCF durante o processamento/serialização da mensagem. Antes de prosseguirmos, é necessário entender cada uma dessas propriedades que podemos, opcionalmente, utilizar. As tabelas abaixo sumarizam cada uma delas.

MessageContractAttribute
Propriedade Descrição
HasProtectionLevel Recebe um valor boleano indicando se a mensagem deverá ter um nível de proteção.
IsWrapped Recebe um valor boleano indicando se o corpo da mensagem terá um elemento que servirá como wrapper. Caso True, o WCF utilizará as informações definidas nas propriedades WrapperName e WrapperNamespace para criá-lo.
ProtectionLevel Propriedade que recebe um dos valores definidos pelo enumerador ProtectionLevel, indicando se a mensagem deverá ser encriptada, assinada, ambos ou não precisará de nenhuma espécie de proteção.
WrapperName Define o elemento que servirá como wrapper para o body da mensagem. Quando omitido ou quando a propriedade IsWrapped estiver definida como False, o corpo será colocado imediatamente após o elemento <soap:Body>.
WrapperNamespace Define o namespace para o elemento que servirá como wrapper da mensagem.

MessageHeaderAttribute
Propriedade Descrição
HasProtectionLevel Recebe um valor boleano indicando se a mensagem deverá ter um nível de proteção.
Name Define o nome do elemento que será serializado. Quando omitido utilizará o nome do próprio campo.
Namespace Fornece um namespace para o header em questão e seus possíveis filhos, a menos que eles sobrescrevam isso.
ProtectionLevel Propriedade que recebe um dos valores definidos pelo enumerador ProtectionLevel, indicando se a mensagem deverá ser encriptada, assinada, ambos ou não precisará de nenhuma espécie de proteção.
Actor Define uma URI que determina a quem se destina aquele atributo.
MustUnderstand Valor boleano que indica se o ator a quem se destina aquela informação deverá ou não entendê-la. Caso esteja definido como True, e o ator não entende aquele header, uma fault deverá ser lançada.
Relay Também define um valor boleano, que indica se o header deverá ser encaminhado para o próximo nó, caso ele não seja interpretado pelo ator atual.

MessageBodyMemberAttribute
Propriedade Descrição
HasProtectionLevel Recebe um valor boleano indicando se a mensagem deverá ter um nível de proteção.
Name Define o nome do elemento que será serializado. Quando omitido utilizará o nome do próprio campo.
Namespace Fornece um namespace para o header em questões e seus possíveis filhos, a menos que eles sobrescrevam isso.
Order  Como o próprio nome diz, é uma propriedade que recebe um valor inteiro indicando a ordem de serialização de cada elemento dentro do corpo da mensagem. Quando omitido, os elementos são serializados de forma alfabética, seguido dos campos que tem essa propriedade definida explicitamente.
ProtectionLevel  Propriedade que recebe um dos valores definidos pelo enumerador ProtectionLevel, indicando se a mensagem deverá ser encriptada, assinada, ambos ou não precisará de nenhuma espécie de proteção.

Ainda temos uma especialização da classe MessageHeaderAttribute, que é a MessageHeaderArrayAttribute. Este atributo pode ser somente aplicado a membros do tipo array e que serão acomodados no header da mensagem. Decorando uma propriedade ou campo com este atributo, os elementos do array serão serializados de forma independente, ao invés de estarem envolvidos por um wrapper. Por exemplo, se tivermos um array de saldos, por padrão, ele será serializado da seguinte forma:

<header value="1" />
<saldos>
  <conta numero="123" valor="100.00" />
  <conta numero="456" valor="200.00" />
</saldos>
<header value="2" />

Já quando aplicamos o atributo MessageHeaderArrayAttribute, ele ficará da seguinte forma:

<header value="1" />
<conta numero="123" valor="100.00" />
<conta numero="456" valor="200.00" />
<header value="2" />

Depois disso, o próximo passo é a criação do contrato. Agora, ao invés de trabalharmos diretamente com os tipos mais simples e/ou complexos nos parâmetros e resultado das operações, elas devem começar a trabalhar diretamente com as classes previamente criadas. As classes que criamos para o exemplo são autoexplicativas, onde uma representa a entrada de dados e a outra o resultado, e como ambas estão decoradas como o atributo MessageContractAttribute, elas definirão a estrutura da mensagem SOAP. Abaixo está o contrato já configurado com elas:

[ServiceContract]
public interface IContrato
{
    [OperationContract]
    ResultadoDaBusca RecuperarSaldo(CriterioDeBusca criterio);
}

Observação: Você ainda tem uma segunda alternativa para a definação de contratos de mensagem, que também é conhecida como “inline”. Neste modo, você pode decorar os parâmetros do teu contrato com os atributos MessageHeaderAttribute ou MessageBodyAttribute.

RPC vs. Messaging

Uma das idéias das aplicações orientadas a serviços é que elas devem trocar mensagens, ou seja, ao invocar uma operação, teoricamente, você deveria criar uma mensagem explicitamente e enviá-la para o seu destino, enquanto do outro lado, você deve capturá-la, analisar o seu conteúdo, e executar a tarefa que ela solicita. O contrato que vimos acima segue essa linha, já que devemos lidar com objetos que representam a mensagem. Esse tipo de formato é conhecido como Messaging.

Existe também uma outra possibilidade, conhecida como RPC (Remote Procedure Call). Neste estilo, nós trabalhamos com os serviços como se fossem componentes/classes tradicionais, ou seja, não precisamos lidar com as mensagens diretamente, pois o método irá receber e/ou retornar os tipos corretos e a tecnologia, que neste caso é o WCF, se encarregará de montar e serializar a mensagem que será enviada de um lado a outro.

O WCF não está limitado a apenas um desses estilos de mensagens, mas na maioria das vezes utilizamos o estilo RPC. Mas o tema deste artigo é justamente mostrar como podemos desenvolver serviços que utilizem o padrão Messaging, e como já vimos acima, vamos trabalhar diretamente com as classes que representarão as mensagens.

Implementação

Ambos os lados da comunicação precisarão trabalhar de forma diferente, já que não vamos mais passar ou receber os parâmetros diretamente; agora vamos lidar com uma classe que representará a resposta e uma classe que representará a requisição. Ambas já foram criadas acima, e o contrato foi desenvolvido utilizando essas duas classes.

Apesar de mudar a forma que desenvolvemos, não há muita complexidade, já que basta instanciarmos uma das classes (dependendo do contexto), abastecer as respectivas propriedades e enviá-la para o WCF, que seguirá os atributos definidos para configurar o formato da mensagem que está sendo enviada. Se analisarmos a classe que representará o serviço, veremos o contrato IContrato implementado nela, e com isso podemos analisar e compreender as classes que representam as mensagens:

public class Servico : IContrato
{
    public ResultadoDaBusca RecuperarSaldo(CriterioDeBusca criterio)
    {
        List<Saldo> saldos = new List<Saldo>(criterio.Contas.Length);
        ResultadoDaBusca resultado =
            new ResultadoDaBusca() { Cliente = criterio.Cliente, Codigo = criterio.Codigo };

        foreach (var item in criterio.Contas)
            saldos.Add(new Saldo() { Conta = item, Valor = 1.0M });

        resultado.Saldos = saldos.ToArray();
        return resultado;
    }
}

Em termos de hosting e binding, nada muda. Já do lado do cliente, ao fazer a referência devemos nos atentar a um pequeno detalhe, que é justamente a criação dos contratos de mensagem. Por padrão, quando fazer a referência através da IDE do Visual Studio ou através do utilitário svcutil.exe, ele não irá criar o proxy adequadamente, ou seja, ele sempre irá trabalhar no estilo RPC, o que nos obrigará a passar os parâmetros individualmente para cada operação.

Quando utilizamos a IDE e queremos que ele mantenha o estilo Messaging, então devemos ir até as configurações da referência do serviço, e marcar a opção “Always generate message contracts”, ou se estiver utilizando o svcutil.exe, utilize a opção /mc. Isso irá nos permitir a trabalhar de uma forma semelhante ao que vemos no exemplo abaixo:

using (ContratoClient proxy = new ContratoClient())
{
    CriterioDeBusca cb =
        new CriterioDeBusca("Israel Aece", 5, new string[] { "12345-6", "09876-2" });

    ResultadoDaBusca rb = proxy.RecuperarSaldo(cb);

    foreach (Saldo s in rb.Saldos)
        Console.WriteLine("{0}: {1:C2}", s.Conta, s.Valor);
}

Apesar de você mudar a forma que você desenvolve ou consome os serviços no estilo Messaging, não há muitas dificuldades. A grande mudança fica por parte do formato da mensagem que é criada pelo WCF, já que respeitará todos os atributos que decoramos nas classes que representarão as mensagens. Se analisarmos o tracing da mensagem que está sendo trafegada entre o serviço e o cliente, teremos um resultado como este (alguns trechos foram omitidos por questões de espaço):

<s:Envelope>
  <s:Header>
    <Action s:mustUnderstand="1">.../IContrato/RecuperarSaldoResponse</Action>
    <h:Cliente>Israel Aece</h:Cliente>
    <h:Codigo>5</h:Codigo>

    <ActivityId>dc2132ac-46c5-495d-a313-e9bb7152fe0a</ActivityId>
  </s:Header>
  <s:Body>
    <contas xmlns="http://tempuri.org/">
      <Saldos>
        <d4p1:Saldo>
          <d4p1:Conta>12345-6</d4p1:Conta>
          <d4p1:Valor>1.0</d4p1:Valor>
        </d4p1:Saldo>
        <d4p1:Saldo>
          <d4p1:Conta>09876-2</d4p1:Conta>
          <d4p1:Valor>1.0</d4p1:Valor>
        </d4p1:Saldo>
      </Saldos>
    </contas>
  </s:Body>
</s:Envelope>

Se analisarmos detalhadamente a mensagem gerada, veremos que os campos Cliente e Codigo fazem parte do header da mensagem, enquanto o array Saldos é parte do corpo da mensagem, que está envolvido (wrapped) pelo elemento <contas />.

Conclusão: Vimos no decorrer deste artigo uma nova forma de trabalhar com o WCF, utilizando o contrato de mensagens. É importante dizer que na maioria das vezes, você utilizará o estilo “tradicional”, onde você deverá recorrer ao estilo RPC, que já é o padrão. Em casos mais específicos, como aqueles que comentamos acima, então esse estilo de comunicação poderá dar uma maior flexibilidade, já que permitirá ao cliente ou ao serviço, ler ou gerar uma mensagem em um formato diferente daquele que o WCF cria automaticamente.

MessageContracts.zip (66.48 kb)

Tags: ,

Interoperabilidade | WCF

Migrando de ASMX para WCF

by Israel Aece 12. May 2009 20:25

Junto com a primeira versão do Visual Studio .NET e do .NET Framework, temos a possibilidade de criarmos serviços Web, baseados em XML e utilizando a tecnologia ASP.NET Web Services (ASMX). Isso ainda continua disponível nas templates de projeto da versão mais atual do Visual Studio .NET, mas, para a criação de novos projetos, ou melhor, de novos serviços, o ideal é recorrer ao WCF – Windows Communication Foundation.

De qualquer forma, os ASP.NET Web Services já existem há algum tempo e há muitas aplicações que ainda o utilizam, e este artigo ajudará a entender melhor as diferenças entre ASMX e o WCF, desde a sua estrutura de projeto até detalhes relacionados à execução do mesmo. Cada uma das seções a seguir irá analisar e discutir essas mudanças, falando também sobre alguns detalhes importantes que, se não se atentar, poderá ter um comportamento “estranho” durante a execução.

Templates e Estrutura de Projeto

Quando você opta por criar um projeto ASMX, então você deve recorrer à template ASP.NET Web Service Application. Ao criar esse tipo de projeto, você poderá adicionar dentro dele arquivos com extensão *.asmx. Esse tipo de arquivo representará um serviço. Dentro desta classe, teremos os métodos que queremos publicar. Vale lembrar que o modificador de acesso do método (public, private, etc.) não tem validade. O que determinará a visibilidade do método é se ele estiver ou não decorado com o atributo WebMethodAttribute.

Além disso, a classe que representa o serviço pode, opcionalmente, herdar da classe WebService. Essa classe fornece acesso direto aos objetos ASP.NET, como Application e Session. Assim como nas aplicações ASP.NET tradicionais (UI), o arquivo ASMX apenas possui uma diretiva chamada @WebService, que define alguns atributos utilizados pelo compilador do ASP.NET. As classes necessárias para trabalhar com o ASMX estão contidas no Assembly System.Web.Services.dll e debaixo do namespace System.Web.Service.

Já com o WCF, trabalhamos de forma bem parecida. Para trabalhar com ele, é necessário que você, ao criar um novo projeto, escolha a template WCF Service Application. Neste projeto, adicionaremos arquivos com extensão *.svc, que representará o serviço. Esse tipo de arquivo também possui uma diretiva própria, chamada de @ServiceHost, que também leva informações ao compilador do ASP.NET. As classes necessárias para trabalhar com o WCF estão contidas no Assembly System.ServiceModel.dll e debaixo do namespace System.ServiceModel.

Apesar de existirem templates distintas para cada uma das tecnologias, isso não quer dizer que você está obrigado a criar um projeto específico para os teus serviços. Caso você já possua uma aplicação ASP.NET criada, é perfeitamente possível adicionar arquivos com extensão *.asmx ou *.svc neste projeto. Uma aplicação ASP.NET (UI) consegue coexistir com qualquer uma dessas tecnologias.

Contratos

O ASMX irá se basear nos métodos decorados com o atributo WebMethodAttribute para gerar o documento WSDL. Você deverá controlar a visibilidade dos teus métodos adicionando um removendo este atributo. Qualquer tipo complexo referenciado nos métodos, será automaticamente inserido na descrição do serviço sem nenhuma configuração extra.

Já o WCF trabalha de forma bem diferente. Ele utiliza interfaces para determinar os contratos que o serviço possui. Essas interfaces são aquelas tradicionais, que já utilizamos no nosso dia-à-dia, mas decorada com um atributo chamado ServiceContractAttribute. Dentro das interfaces teremos os métodos, e controlamos se eles serão ou não expostos através do atributo OperationContractAttribute.

Em relação a tipos complexos vinculados ao contrato do serviço, a exposição deles será determinado pela versão do WCF que está utilizando. Se estiver utilizando a versão 3.0, então você precisará decorar essas classes com o atributo DataContractAttribute, e para cada propriedade que desejar expor, decorá-la com o atributo DataMemberAttribute (opt-in). Com o Service Pack 1 para o .NET Framework 3.5, esses parâmetros são opcionais (opt-out), tornando-os POCOs. Mas há um detalhe: quando você decorar a classe com o atributo DataContractAttribute, então você deverá explicitamente determinar quais propriedades deseja disponibilizar, utilizando o atributo DataMemberAttribute.

Há um outro detalhe importante durante a execução das operações. Quando você utiliza ASMX, ele concatena o namespace com o nome da mensagem para determinar o Action. O WCF concatena o namespace, o nome do serviço e o nome da operação. Para manter compatibilidade com possível clientes ASMX, você deve manter a mesma fórmula, e para isso, pode recoorer a propriedade Action do atributo OperationContextAttribute.

Serialização/Deserialização

A serialização e deserializaão estão condicionadas ao serializador que cada tecnologia utiliza. O ASMX utiliza o XmlSerializer (System.Xml.Serialization) para transformar os objetos em XML e vice-versa. O XmlSerializer serializa todos os membros públicos (propriedades e campos), sem a necessidade de definir algum atributo. Você ainda pode controlar como essa serialização será realizada, utilizando vários atributos que existem debaixo deste mesmo namespace. Para maiores detalhes sobre o funcionamento do XmlSerializer e dos atributos, consulte este artigo.

O WCF, por outro lado, utiliza o serializador DataContractSerializer por padrão. Este serializador trabalha de forma semelhante ao XmlSerializer, com poucos diferenças. Entre essas diferenças temos o entendimento por parte do DataContractSerializer do atributo SerializableAttribute, para manter a compatibilidade com objetos que foram criados para serem utilizados pelo .NET Remoting. Além disso, uma outra diferença é a capacidade que este serializador tem de persitir também membros definidos como private e protected. Este serializador ainda gera um XML mais simplificado, melhorando a interoperabilidade entre as plataformas. Se desejar utilizar o XmlSerializer, então basta decorar o seu contrato com o atributo XmlSerializerFormatAttribute. Somente utilize o XmlSerializer para cenários onde você precisa ter controle total sob como o XML é gerado.

Ainda temos um terceiro serializador que é o NetDataContractSerializer. A diferença em relação ao DataContractSerializer é que ele armazena no XML gerado, informações a respeito do tipo (CLR), como versão, assembly e full name. Este serializador é rico nas informações referente ao tipo, ele compartilha tipos, ao contrário do DataContractSerializer, que compartilha contratos. Este ainda possui baixa interoperabilidade, e pode ser utilizado em cenários onde se tem .NET Remoting.

Protocolo/Hosting

O ASMX pode somente ser hospedado no IIS, utilizando o protocolo HTTP/HTTPS. Já o WCF tem uma arquitetura muito mais flexível e é independente do protocolo, ou seja, ele pode rodar em HTTP, HTTPS, TCP, MSMQ, etc. Isso quer dizer que ao construir um serviço através do WCF, você não deve mencionar e/ou confiar em nenhum momento que o mesmo será exposto através de um determinado protocolo, já que você não conseguirá garantir isso.

O WCF também pode utilizar o IIS como host. Mas além dele, podemos fazer uso de outras aplicações para expor um serviço, como um Windows Service, ou ainda, uma simples aplicação Windows/Console. Para mais detalhes sobre cada um dos tipos de host, consulte este artigo.

Extensibilidade

O ASMX permite você interceptar as requisições através de SOAP Extensions. Com elas, podemos acoplar um código customizado no processamento da mensagem, efetuando logs, tratando a autenticação/autorização, etc. A estrutura para a criação de um extensão é simples: basta herdar da classe SoapExtension e implementar o método ProcessMessage. Como parâmetro, este método traz uma propriedade chamada Stage, que podemos identificar o estágio do processamento da mensagem. Ela nos fornece quatro opções auto-explicativas: BeforeSerialize, AfterSerialize, BeforeDeserialize e AfterDeserialize. Para utilizá-la, basta criar um atributo que herda de SoapExtensionAttribute, e sobrescrever o método ExtensionType.

O WCF traz inúmeros pontos de extensibilidade tanto do lado do serviço quanto do cliente. Através destes pontos, podemos interceptar e inspecionar os parâmetros que estão sendo enviados, a escolha da operação a ser disparada, a criação da instância da classe que representa o serviço, a serialização e deserialização da mensagem (é o que a classe SoapExtension faz), entre outros. O WCF fornece vários tipos (interfaces e classes) para você customizar o que for necessário. Para entender detalhadamente sobre todos as possibilidades que temos, consulte este artigo.

Segurança

O ASMX pode confiar somente na segurança baseada no transporte, ou seja, ele somente será seguro se você expor o serviço através de HTTPS. Você somente conseguirá abrir mão do HTTPS se utilizar a segurança baseada na mensagem, que está disponível no ASMX através do WSE – Web Services Enhancements. Muitas vezes se utiliza um SoapHeader com usuário e senha. Isso somente terá alguma segurança se utilizar HTTPS ou segurança em nível de mensagem. Do contrário, qualquer um que intercepte a requisição, conseguirá extrair o conteúdo da mensagem e seus respectivos headers.

Como já era de se esperar, o WCF fornece ambos níveis de segurança nativamente. São configurações que você realiza (de forma imperativa ou declarativa), e que o serviço utilizará para efetuar a autenticação e autorização do cliente. Uma das grandes dificuldades que o pessoal encontra ao utilizar o WCF, é que se configurar o WCF para autenticar o cliente através de usuário e senha, ainda assim será necessário utilizar um certificado para garantir a segurança.

Configuração

O ASMX possibilita que algumas configurações sejam feitas de forma declarativa, ou seja, aquela que é realizada através do arquivo Web.config. Entre essas configurações, temos a possibilidade de definir as SoapExtesions, página de ajuda/WSDL customizada, os protocolos que podem ser utilizados para acessar o serviço (HttpSoap, HttpPost e HttpGet) e mais algumas outras.

No WCF, a seção de configurações são extremamente ricas. Grande parte de tudo que utilizamos no WCF pode ser configurado a partir do arquivo de configuração. Segurança, transações, know types, behaviors, bindings, endpoints, contratos, etc. O arquivo de configuração é tão rico que as vezes chega a ser complexo. A Microsoft melhorou isso no WCF 4.0, como já foi discutido neste artigo. Aqui não há muito o que se comparar, já que grande parte do que temos no WCF não existe nativamente no ASMX. Devido a isso, muitos desenvolvedores experientes na construção de serviço utilizando o ASMX, sofrem bastante quando passam a usar o WCF.

Compatibilidade com o ASP.NET

Dentro de métodos criados no ASMX, você pode tranquilamente acessar os objetos nativos do ASP.NET, como caching, Session, Application, Cookies, etc. Você pode utilizar esses repositórios para manter as informações de acordo com o contexto. Todas essas informações são disponibilizadas através da propriedade estática Current da classe HttpContext.

A configuração padrão do WCF não permite você acessar essas informações. Na verdade, isso não é uma boa prática. Se fizer uso dessas classes dentro do seu serviço, ele ficará dependente do protocolo HTTP. Se quiser expor o mesmo serviço através de TCP ou MSMQ, essas informações não terão nenhuma utilidade. De qualquer forma, se quiser manter a compatibilidade e continuar utilizando os mesmos recursos, então você deverá explicitamente habilitá-los. Para fazer isso, você deve decorar a classe que representa o serviço com o atributo AspNetCompatibilityRequirementsAttribute, definindo a propriedade RequirementsMode como Required.

Dependendo do tempo de vida e do escopo que deseja manter alguma informação, você pode recorrer a técnicas nativas do WCF, como o compartilhamento de estado, utilizando a interface IExtension<T>, como é abordado no final deste artigo.

Interoperabilidade

Há várias especificações que foram definidas por grandes players do mercado, que regem a estrutura do envelope SOAP para suportar alguma funcionalidade. Essas especificações são conhecidas como WS-* e estão divididas em várias categorias, sendo Messaging, Security, Reliable Messaging, Transaction, Metadata, entre outros. Cada uma das empresas utiliza essas especificações e as implementam em sua plataforma. Como todos seguem (teoricamente) as mesmas especificações, haverá interoperabilidade entre serviços construídos em plataformas diferentes.

O ASMX não possui nativamente suporte a elas. A Microsoft criou um Add-on para o Visual Studio .NET, chamado WSE – Web Services Enhancements, que atualmente está na versão 3.0. Ao instalá-lo, além de várias ferramentas que ele adiciona na IDE para auxiliar na configuração destes protocolos, adiciona o runtime necessário para fazer tudo isso funcionar. É importante dizer que o WCF consegue também interoperar com o WSE, já que ambos implementam os mesmos padrões.

Como esses padrões já foram implementados nativamente no WCF, não exige nenhum complemento. Todas as especificações são configuradas através do binding, podendo inclusive efetuar essa configuração através do arquivo Web.config. Antes de habilitar ou desabilitar essas funcionalidades, é importante que se tenha o devido conhecimento, para evitar qualquer problema ao até mesmo criar alguma vulnerabilidade. 

Ainda falando sobre interoperabilidade, a Microsoft se preocupou em manter os investimentos feitos com o ASMX. O WCF consegue facilmente conversar com serviços construídos em ASMX em ambos os lados, ou seja, você pode construir um serviço ASMX e consumí-lo com a infraestrutura do WCF, bem como pode construir um serviço escrito em WCF e consumí-lo com a infraestrutura do ASMX. Na verdade, quando você for criar um novo serviço, opte sempre pelo WCF. Você somente teria essa interoperabilidade entre essas tecnologias, quando desejar substituir o ASMX pelo WCF em serviços que já estão rodando, e com vários clientes dependendo do mesmo.

Internals

As requisições para arquivos *.asmx são direcionadas pelo IIS para o ISAPI do ASP.NET (aspnet_isapi.dll). Em um determinado momento, o ISAPI direciona a requisição do pedido para o código gerenciado. As requisições para estes arquivos tem como alvo um dos handlers WebServiceHandlerFactory ou ScriptHandlerFactory. A primeira delas, se baseando no arquivo requisitado, construirá dinamicamente a classe que representa o serviço. Já a classe ScriptHandlerFactory construirá o handler baseando-se em uma requisição REST/AJAX.

O WCF também utiliza o pipeline do ASP.NET, e quando a requisição é entregue pelo IIS para o ASP.NET, ele verifica se trata-se de uma requisição para um arquivo *.svc. Caso positivo, o handler responsável pelo processamento do pedido é o HttpHandler (System.ServiceModel.Activation). Internamente o WCF faz o processamento síncrono da operação, não liberando a thread do ASP.NET, que ficará bloqueada até a finalização da operação. Para entender melhor esse problema e também como melhorá-lo, você pode recorrer a este artigo.

Deployment

Assim como qualquer aplicativo .NET, basta mover os serviços para o IIS remoto e tudo já funciona. Obviamente que você deverá se certificar que você tenha a mesma versão do .NET Framework (isso inclui o Service Packs) instalada no servidor. É importante dizer que ambas tecnologias necessitam de um diretório virtual devidamente criado no IIS, com as permissões também configuradas. Apenas atente-se ao WCF, que tem um pequeno bug quando você opta pela pré-compilação do projeto.

Cliente

Dentro do Visual Studio .NET você tem duas opções para referenciar um serviço: “Add Web Reference” e “Add Service Reference”. Podemos dizer que com a primeira opção, você deve utilizar quando for referenciar um serviço ASMX em uma aplicação; já com a segunda opção, você deve utilizar quando você for referenciar um serviço WCF em uma aplicação. Quando fizer isso, a IDE criará o proxy utilizando a API da tecnologia correspondente.

Isso não quer dizer que você precisa seguir sempre esta regra. Você pode referenciar um serviço ASMX na opção “Add Service Reference”. Ao fazer isso, toda a estrutura do lado do cliente, será criada utilizando a API do WCF, ou seja, o proxy será baseado na classe ClientBase<TChannel> ao invés da SoapHttpClientProtocol, e toda a configuração do endpoint do serviço será colocada no arquivo Web.config. O WCF criou um binding chamado BasicHttpBinding para a interoperabilidade com o ASMX, que você pode utilizar para interagir com o serviço criado através do ASMX, sem maiores problemas.

Conclusão: Como vimos neste artigo, há vários detalhes que precisamos nos atentar na construção ou migração de um serviço escrito em ASMX para WCF. Para efeito de compatibilidade, algumas funcionalidades continuam trabalhando da mesma forma, enquanto para outras há um jeito diferente de fazer, e que na maioria das vezes, acaba sendo mais flexível. O artigo também não aborda detalhadamente cada uma das funcionalidades do WCF. Se desejar construir um serviço utilizando WCF, ou se quiser entender mais detalhadamente cada uma das funcionalidades abordadas aqui, pode consultar este artigo, que no final dele, há uma relação com os principais artigos que falam sobre essas funcionalidades.

Tags: , , , ,

WCF | CSD | Interoperabilidade | Security

Embutindo o PIA

by Israel Aece 24. April 2009 22:57

Quando referenciamos um componente COM em um aplicativo .NET, o Visual Studio .NET nos auxilia na criação de um Assembly de Interoperabilidade, conhecido como Primary Interop Assembly, ou somente PIA. Na verdade, a IDE recorre à um utilitário chamado tlbimp.exe, que dado um determinado componente COM, gera uma DLL com toda estrutura (tipos) do componente COM, já “traduzindo” os tipos COM para tipos .NET, facilitando assim o consumo por uma aplicação .NET.

Uma das melhorias que o .NET 4.0 está trazendo é a possibilidade de embutirmos esse Assembly de interoperabilidade no Assembly da aplicação. Para isso, basta ir até as propriedades da referência COM e definir a propriedade Embed Interop Types como True. Isso evita ter um Assembly exclusivo para servir como “ponte” entre a aplicação .NET e o componente COM. Lembro-me de alguns poucos projetos que tivemos que utilizar esse recurso que, como se já não bastasse o overhead que é causado pelo RCW, a distribuição era terrível, já que tínhamos que registrar efetivamente o componente COM no Windows, e instalar o Assembly de interoperabilidade e a aplicação em si.

Além de facilitar a distribuição, esse recurso ainda se preocupa em inserir no Assembly da aplicação somente os tipos necessários que a mesma utiliza, diminuindo consideravelmente o tamanho final da mesma. Podemos notar isso através da imagem abaixo:


PIA

Tags: ,

.NET Framework | Interoperabilidade

WWS - Windows Web Services

by Israel Aece 11. January 2009 08:18

Sendo publicado como um componente a partir do Windows 7, o Windows Web Services - WWS, é uma API que permite ao código nativo, não gerenciado, criar e consuimir Web Services sem a necessidade do .NET Framework. O propósito de ser um componente é que a Microsoft pretende disponibilizá-lo para as outras versões do Windows, como XP, Vista, 2003 e 2008.

Com um modelo bem similar ao WCF, com bindings, channels, endpoints, etc., o WWS facilita a criação e hosting de serviços, bem como o consumo deles, fornecendo interoperabilidade com o WCF, ASMX e outras tecnologias não-Microsoft, que seguem os padrões WS-*.

Os tres grandes pilares do WWS são: Service Model, Channel LayerXML Layer. O primeiro deles gerencia a comunicação, ou seja, se estivermos consumindo o serviço, ele fornece tipos para facilitar o seu consumo (service proxy); já se estivermos expondo o serviço, então ele fornece tipos para efetuar o hosting (service host). A Channel Layer controla a comunicação, como os dados que são enviados e recebidos, abstração do protocolo, segurança, etc. E, finalmente, temos a XML Layer, fornece acesso completo ao conteúdo das mensagens.

Algumas funcionalidades ainda não foram implementadas, como é o caso do suporte a segurança em nível de mensagem e, pelo que parece, isso irá acontecer de acordo com a necessidade dos clientes. Assim como o WCF, o WWS também é orientado a contratos, permitindo trabalhar de forma bem semelhante no código não gerenciado. O WWS também fornece um utilitário chamado WsUtil.exe, que mapeia o WSDL/XSD para tipos C.

Para aqueles que possuem o Windows 7, verá que no diretório %windir%\System32\ existe uma DLL chamada Webservices.dll que gerencia grande parte desta comunicação. Além disso, o Windows SDK disponibilizado no PDC 2008, traz esta API (WWSAPI), contendo o Wsutil.exe, Webservices.h e webservice.lib.

É importante dizer que, segundo Nikola Dudar, o WWS não substitui o WCF. A finalidade do WWS é possibilitar aplicações nativas consumirem ou disponibilizarem um serviço, enquanto o WCF é a alternativa para o código gerenciado, e depende do .NET Framework.

Tags:

CSD | Interoperabilidade

Algumas novidades interessantes

by Israel Aece 28. October 2008 09:04

A versão CTP do Visual Studio 2010 com o .NET Framework 4.0 já está disponível, assim como o Soma Somasegar disse neste post. Olhando superficialmente, há algumas funcionalidades que achei bastante interessantes, que quero listar aqui:

  • Code-snippet para HTML.
  • ClientIDMode: Nova propriedade que está contida nos controles ASP.NET, dando-nos a possibilidade de definir como renderizar o ID dos controles.
  • Gráficos: Agora temos nativamente um controle chamado Chart, mas que pode também ser utilizado em conjunto com o Visual Studio 2008 e .NET Framework 3.5. Download: Controles, VS.NET Toolbox (Add-On) e Documentação.
  • Web.config: Possibilidade de criar/transformar o seu arquivo Web.config para diversos estágios do desenvolvimento (Debug/Staging/Release) que, na maioria das vezes, as configurações mudam em cada uma dessas fases.
  • PIA - Primary Interop Assemblies: Quando adicionamos uma referencia a algum componento COM, era criado um Assembly de interoperabilidade em nosso projeto. Esse Assembly apenas contém a estrutura do componente não gerenciado e, que devemos distribuir juntamente com a aplicação onde ele foi referenciado. Com o Visual Studio 2010, podemos embutir esses tipos no Assembly da aplicação/DLL onde o componente está sendo referenciado, sem a necessidade da criação de um Assembly a parte.
  • Computação Paralela: Até então chamada de Parallel Extensions, agora ela foi incorporada dentro do .NET Framework.
  • VB.NET - Propriedades Automáticas: Assim como no C#, o VB.NET agora suporta propriedades automáticas. A sintaxe para isso é: Public Property Nome As String.
  • C# - Parametros Opcionais: Assim como nas versões do Visual Basic, o C# agora passa a suportar parametros opcionais e, assim como no VB, é necessário especificar um valor padrão. A sintaxe para isso é: public void Teste(string nome, int valor = -1){ }.
  • C# - Tipos Dinamicos: Assim como Charlie Calvert comentou aqui há algum tempo, o C# agora terá uma keyword chamada dynamic. Ao declarar uma variável como dinamica, ela suportará a chamada de membros e, a verificação se eles existem ou não, somente acontecerá em tempo de execução. Isso também é conhecido como late-binding.

Tags: ,

.NET Framework | ASP.NET | C# | Interoperabilidade | VB.NET

WCF e a interoperabilidade COM

by Israel Aece 29. September 2008 09:12

O WCF fornece diversos meios de interoperabilidade com tecnologias existentes e, entre elas, a possibilidade de consumir um serviço a partir de aplicações COM, como é o caso do Visual Basic 6.0, C ou mesmo através do ASP Clássico. Isso permitirá que aplicações desenvolvidas nessas tecnologias façam o uso de serviços construídos em WCF sem muito trabalho.

A criação do serviço e a forma como voce irá expor não mudará. Atualmente temos duas alternativas para possibilitar essa interoperabilidade: a primeira delas seria fazer o uso do utilitário svcutil.exe, criando o arquivo cs/vb representando o proxy e também o seu respectivo arquivo de configuração. Com isso, podemos criar um Assembly (atente-se ao atributo ComVisibleAttribute) gerenciado e, a partir daqui, voce poderá expor o tlb e consumí-lo em aplicações COM (para maiores detalhes sobre este processo, consulte este link). Utilizando esta técnica, será necessário registar o Assembly e colocar o arquivo de configuração gerado pelo svcutil.exe no mesmo diretório do *.exe, com o mesmo nome da aplicação acrescido de .config, como por exemplo: AplicacaoEmVB6.exe.config. A segunda possibilidade e mais simples, é acessar diretamente o serviço que está rodando, sem a necessidade de expor um Assembly gerenciado ou até mesmo registrar o componente dentro do Windows.

Para ambos os casos, a aplicação cliente fará o uso do service moniker. Este elemento está disponibilizado a partir da função GetObject que, por sua vez, retorna uma instancia do objeto especificado como parametro (ProgID) e que para serviços WCF, essa instancia representará o canal de comunicação, ou seja, o proxy. No caso de clientes COM que farão o uso de um serviço WCF, é comum especificar como parametro o endereço do serviço (HTTP, TCP, etc.), endereço do WSDL, o contrato e o binding a ser utilizado pela aplicação.

O código abaixo ilustra como consumir o serviço WCF a partir do ASP Clássico, utilizando a segunda alternativa de interoperabilidade comentado acima:

<%

    Dim proxy
   
    Set proxy = GetObject("service:mexAddress=net.tcp://localhost:9292/mex, " & _
        "address=net.tcp://localhost:9292/srv, " & _
        "contract=IOperacao, contractNamespace=http://www.projetando.net, " & _
        "binding=NetTcpBinding_IOperacao, bindingNamespace=http://tempuri.org/")
   
    Response.Write(proxy.Adicionar(3, 34))

%>

Neste caso, o moniker utilizará os dados disponibilizados pelo WSDL para recuperar as informações necessárias e, possibilitar a chamada para o serviço, diferentemente do primeiro caso, onde disponibilizamos o proxy criado para o mundo COM através dos próprios recursos nativos do .NET Framework. Caso utilizarmos a primeira possibilidade de interoperabilidade, as únicas informações que devemos passar para a função GetObject são o endereço e o binding.

Tags: , ,

CSD | Interoperabilidade | 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