Israel Aéce

Microsoft MVP, MCP, MCAD, MCTS, MCPD e MCT

My Links

Blog Stats

Archives

Post Categories

Links

Login

Saturday, September 06, 2008 #

Chamadas assíncronas à Serviço via modelo de eventos

O WCF fornece, não por padrão, a possibilidade de invocar as operações de um serviço de forma assíncrona. Quando efetuamos a referencia para um serviço via IDE do Visual Studio 2008 ou através do utilitário svcutil.exe (com a opção /async) automaticamente, além da versão síncrona do método, dois métodos chamados BeginXXX/EndXXX (onde XXX é o nome do método síncrono) são adicionados, permitindo que voce invoque assincronamente uma determinada operação.

Além do tradicional modelo de chamadas assíncronas (APM), temos a possibilidade da chamada assíncrona baseada em eventos. A idéia aqui é, antes de invocar a operação, voce poderá assinar à um evento que será disparado somente quando o processo assíncrono finalizar. Isso evitará de ter um trabalho manual para analisar se o processo finalizou ou não (poll, waiting, etc.). Internamente durante a geração do proxy, o código que é auto-gerado já inclui a implementação necessária para o modelo baseado em eventos.

Basicamente é criado mais uma versão do método, agora com o sufixo XXXAsync que, internamente, faz a chamada para os métodos BeginXXX/EndXXX que, como já sabemos, dispararam a operação de forma assíncrona. Além disso, um delegate do tipo EventHandler<T> também será criado para representar o callback que, quando disparado, invocará o evento do lado de quem está consumindo o serviço. Abaixo um exemplo de como efetuar a chamada assíncrona baseada em eventos:

using (ClienteClient p = new ClienteClient())
{
    p.CalcularComissaoCompleted += 
        new EventHandler<CalcularComissaoCompletedEventArgs>(p_CalcularComissaoCompleted);

    p.CalcularComissaoAsync("2");
    Console.ReadLine();
}

static void p_CalcularComissaoCompleted(object sender,
    CalcularComissaoCompletedEventArgs e)
{
    Console.WriteLine("Fim.");
}

Se notarmos a implementação interna do proxy, veremos que o método XXXAsync faz o uso do método InvokeAsync, da classe ClientBase<T>. Este método está disponível somente a partir do .NET Framework 3.5. Sendo assim, alguns detalhes durante a geração do proxy precisam ser analisados:

  • Via "Add Service Reference": se voce estiver fazendo a referencia em um projeto que esteja utilizando o .NET Framework 3.5 e voce opta pela geração dos métodos que dão suporte ao processamento assíncrono, ele também criará os tipos necessários para suportar o modelo de eventos.
  • Via svcutil.exe: neste caso voce precisará especificar através do parametro /async e, além disso, especificar a versão do .NET Framework através do parametro /targetClientVersion, apontando para Version30 ou, se quiser utilizar o modelo baseado em eventos, utilizar a opção Version35.

posted @ 1:15 AM | Feedback (0)

Friday, September 05, 2008 #

#error

Em alguns momentos durante a escrita de um projeto para servir de exemplo para um artigo, eu gostaria de colocar uma diretiva no código para forçar o leitor a antes de executar o mesmo, configurar algo que seja necessário para que o projeto funcione como o esperado. Depois de algumas pesquisa no MSDN Library, a solução que encontrei foi a diretiva #error, que podemos aplicar ao código da seguinte forma:

#error Altere o valor da connectionstring apontando para uma base válida
private const string SQL_CONN_STRING = "Data Source=.;Initial Catalog=DBTest;Integrated Security=SSPI;";

Isso impedirá o usuário de compilar a aplicação sem antes alterar essa informação ou, ao menos, comentá-la. :P

posted @ 7:02 PM | Feedback (3)

Tuesday, September 02, 2008 #

Func vs. Expression

Há algum tempo eu comentei sobre a evolução dos delegates, passando pelas versões 1.0, 2.0 e 3.0 do C#. Sabemos que a partir da versão 3.0 temos uma nova forma de expressar os delegates: expressões lambda. Neste modelo, não há mais necessidade de criar um método adicional ou um método anonimo para executar uma determinada tarefa.

Com a vinda do LINQ, novos delegates também foram introduzidos dentro do namespace System, através do Assembly System.Core.dll:

public delegate TR Func<TR>();
public delegate TR Func<T0, TR>(T0 a0);
public delegate TR Func<T0, T1, TR>(T0 a0, T1 a1);
public delegate TR Func<T0, T1, T2, TR>(T0 a0, T1 a1, T2 a2);
public delegate TR Func<T0, T1, T2, T3, TR>(T0 a0, T1 a1, T2 a2, T3 a3);

Esta família de delegates genéricos servem para construir delegates "on-the-fly", eliminando a necessidade de criá-los explicitamente. TR representa o resultado do delegate (nunca podendo ser void); depois temos outras versões do mesmo podendo, no máximo, termos quatro parametros de entrada. Em uma operação de soma, poderíamos utilizar o terceiro delegate, como por exemplo:

Func<int, int, int> exemplo = (v1, v2) => v1 + v2;
int resultado = exemplo(2, 3);

Ao compilar este código, o compilador do C# criará: um método que retorna um número inteiro e, no corpo do mesmo, terá o cálculo a ser realizado (v1 + v2) e um delegate que apontará para esse método recém criado; além disso, ele converterá a expressão lambda em um método anonimo, fazendo o uso do delegate criado anteriormente que, neste momento, apontará para o método que fará a soma dos números. O código acima é compilado para:

private static void Main(string[] args)
{
    Func<int, int, int> exemplo1 = delegate (int v1, int v2) {
        return v1 + v2;
    };
    int resultado = exemplo1(2, 3);
}

[CompilerGenerated]
private static Func<int, int, int> CS$<>9__CachedAnonymousMethodDelegate1;
 
[CompilerGenerated]
private static int <Main>b__0(int v1, int v2)
{
    return (v1 + v2);
}

Uma outra alternativa é a utilização da classe Expression<TDelegate>, contida dentro do namespace System.Linq.Expressions. Essa classe deve ser tipificada com o mesmo delegate que utilizamos acima e, ao invés de converter a expressão lambda em um código IL que avalia a expressão, irá transformá-la em uma árvore de objetos IL, representando a expressão. Se utilizarmos o mesmo exemplo, veremos que o código mudará:

Expression<Func<int, int, int>> exemplo1 = (v1, v2) => v1 + v2;
int resultado = exemplo1.Compile()(2, 3);

Neste caso, não podemos invocar diretamente o delegate por ele não é um delegate. Essa classe fornece um método chamado Compile que, ao invocá-lo, retorna o delegate especificado na sua criação (Func<int, int, int>) e, a partir daí, podemos utilizá-lo da forma tradicional. Como o compilador lida de forma diferente quando utilizamos a classe Expression<TDelegate>, o código IL gerado para ele corresponde à:

private static void Main(string[] args)
{
    ParameterExpression CS$0$0000;
    ParameterExpression CS$0$0001;

    int resultado = 
        Expression.Lambda<Func<int, int, int>>(
            Expression.Add(
                CS$0$0000 = Expression.Parameter(typeof(int), "v1")
                , CS$0$0001 = Expression.Parameter(typeof(int), "v2")
            )
        , new ParameterExpression[] { CS$0$0000, CS$0$0001 })
        .Compile()(2, 3);
}

Como podemos ver, as expressões lambdas podem ser representadas como código (delegates) ou como dados (árvore de expressões). É importante lembrar que uma expressão é um tipo de Abstract Syntax Tree (AST), que é uma estrutura de dados que representa um código já analisado. Essa técnica nos dá a habilidade de converter/traduzir um determinado código em outro, como é o caso do LINQ to SQL, que transforma essas árvores de expressão em linguagem T-SQL.

posted @ 9:21 PM | Feedback (0)

Thursday, August 28, 2008 #

WcfTestClient

O utilitário WcfTestClient.exe que está integrado ao Visual Studio .NET não precisa ser necessariamente incializado a partir de um projeto do tipo WCF Service Library que, por sua vez, tem a finalidade de gerar uma DLL contendo o contrato e implementação do serviço e, o utilitário é utilizado para testá-los, sem a necessidade de criar um host, pois ele já faz isso.

É importante dizer que podemos iniciá-lo a partir do prompt de comando do Visual Studio, passando como parametro uma Url já existente para que possamos testá-lo, sem a necessidade de criar um projeto dummy para isso. Abaixo está o exemplo:

C:\>WcfTestClient http://IADevServer/ServicoDePublicacaoDeArquivos/

posted @ 8:18 PM | Feedback (0)

Wednesday, August 27, 2008 #

Integrando Windows Live ID ao ASP.NET

Como todos sabem, o Windows Live ID é a evolução do Passport. Antigamente, para integrar uma aplicação ASP.NET ao sistema de autenticação do Passport, além de uma SDK que voce precisa entender e customizar, havia custos envolvidos, o que fez com que a terceira forma de autenticação suportada pelo ASP.NET não vingasse.

Atualmente, o Windows Live ID torna-se muito menos complexo e mais simples de acoplar à aplicações ASP.NET e, além disso, não é mais necessário pagar para utilizá-lo. Tudo o que precisa ser feito é um cadastro prévio, que consiste nas informações a respeito da aplicação que fará o uso do Windows Live ID. Para um passo à passo de como configurá-lo, podemos seguir o um artigo que neste endereço.

Além disso, ainda temos (ainda em CTP) o Windows Live Tools, que adiciona alguns controles na barra de ferramentas do Visual Studio .NET. Este CTP contempla, entre vários controles, os controles IDLoginView, IDLoginStatus e a classe LiveMembershipProvider. Esta última, herda diretamente da classe SqlMembershipProvider e traz a possibilidade de integrar uma credencial Live a um usuário dentro da estrutura de segurança do ASP.NET.

posted @ 7:07 AM | Feedback (0)

Wednesday, August 20, 2008 #

Criação de projeto "dummy" para cliente WCF

Quando estamos fazendo testes com serviços WCF, a idéia é sempre criar um serviço e consumí-lo em alguma aplicação cliente para testar se tudo corre como esperado. Mas quando o exemplo é simples, onde queremos testar apenas algumas funcionalidades, podemos utilizar uma técnica para criar o host e também, no mesmo projeto, poderíamos consumir o serviço sem a necessidade de criar um projeto dummy para isso.

As primeiras linhas consistem na criação e configuração do host e, depois que ele estiver aberto, então podemos recorrer ao uso da classe genérica ChannelFactory<TChannel> que podemos informar o contrato que estamos utilizando/testando, o ponto de acesso (endpoint) e o binding. O método CreateChannel utiliza o endpoint e o binding especificado no construtor para criar e estabelecer o canal de comunicação.

string address = "http://localhost:9388";

using (ServiceHost host = new ServiceHost(typeof(Servico), new Uri[] { new Uri(address) }))
{
    host.AddServiceEndpoint(typeof(IContrato), new BasicHttpBinding(), string.Empty);
    host.Open();

    using (ChannelFactory<IContrato> srv =
        new ChannelFactory<IContrato>(new BasicHttpBinding(), new EndpointAddress(address)))
    {
        Console.WriteLine(srv.CreateChannel().Metodo("Israel Aece"));
    }
}

posted @ 7:08 AM | Feedback (0)

Thursday, August 14, 2008 #

Evitando a remoção do Cache

Thomas Marquardt mostra aqui uma nova funcionalidade da API de Caching do ASP.NET 3.5. Basicamente a idéia aqui é interceptar o momento antes do item ser efetivamente removido do cache.

posted @ 7:51 AM | Feedback (0)

RedirectMode

O Service Pack 1 do .NET Framework 3.5 incluiu um novo recurso, onde voce poderá definir qual será a forma de redirecionamento quando estiver com o customErrors habilitado. Agora temos um atributo chamado RedirectMode, onde voce poderá definir dois valores:

  • ResponseRedirect: exibe a página de erro mundando a Url original da página que foi solicitada.
  • ResponseRewrite: exibe a página de erro sem mudar a Url da página original que foi solicitada.

Abaixo um simples exemplo:

<customErrors mode="On" redirectMode="ResponseRewrite">
    <error statusCode="500" redirect="Erro.aspx"/>
</customErrors>

posted @ 7:36 AM | Feedback (0)

O atributo Action do WebForm

Alguém havia me solicitado isso em algum momento e agora é possível.

posted @ 7:25 AM | Feedback (0)

Melhorias no WCF com SP1

O Service Pack 1 do .NET Framework 3.5 e o Serviço Pack 1 do Visual Studio 2008 trouxeram algumas melhorias interessantes no WCF:

  • UriTemplate: agora podemos incluir um valor padrão para algum parametro que será invocado via REST. Algo como: [WebGet(UriTemplate = "RecuperarUsuarios/{quantidade=5}")].
  • Atributo DataContract/DataMember: a partir de agora não precisamos explicitamente definir quais são os objetos e propriedades que serão expostos para serviços quando estamos trabalhando com tipos complexos. Ele entende que tudo será exposto. Temos apenas que nos preocuparmos em marcar o que não queremos que seja serializado.
  • Partial Trust: possibilidade de fazer o uso do Event Log em ambiente parcialmente confiável.
  • Hosting Wizard: basicamente é a mesma funcionalidade do "Publish Web Site", mas para um serviço WCF.
  • WCF Options: quando tem um projeto do tipo Wcf Service Library na solução, nas propriedades deste projeto temos uma aba chamada WCF Options. Dentro da mesma, há uma opção chamada: Start WCF Service Host when debugging another project in the same solution. Ao marcá-la, ao rodar qualquer projeto em modo de depuração, o WCF Test Client irá iniciar para tornar o serviço disponível para que outras aplicações na mesma solução o utilizem.

posted @ 7:19 AM | Feedback (0)