Compartilhamento de Bindings

by Israel Aece 30. November 2009 08:25

Quando você possui múltiplos endpoints para expor um serviço, na maioria das vezes, esses endpoints possuem características diferentes, pois você poderá publicá-lo através de HTTP para consumo externo, enquanto as aplicações locais, consomem este mesmo serviço através de TCP, para uma melhor performance.

A configuração abaixo ilustra a exposição de um mesmo serviço e de um mesmo contrato através dos protocolos TCP e HTTP. Cada um dos protocolos possuem características diferentes de comunicação, e justamente por isso, exigem bindings diferentes.

<system.serviceModel>
  <services>
    <service name="App.Servico">
      <host>
        <baseAddresses>
          <add baseAddress="net.tcp://localhost:9838"/>
          <add baseAddress="http://localhost:8478"/>
        </baseAddresses>
      </host>
      <endpoint address="srv"
                contract="App.IContrato1"
                binding="netTcpBinding"/>
      <endpoint address="srv"
                contract="App.IContrato1"
                binding="basicHttpBinding"/>
    </service>
  </services>
</system.serviceModel>

Cada endpoint é composto por um endereço, binding e contrato, e cada endpoint possui seu próprio listener e channel stack, que varia de acordo com o binding escolhido. Em algumas situações, é comum ter mais do que um contrato para o mesmo serviço, onde queremos compartilhar o mesmo endereço para ambos contratos. Abaixo podemos ver um serviço que tem esse comportamento:

<system.serviceModel>
  <services>
    <service name="App.Servico">
      <host>
        <baseAddresses>
          <add baseAddress="net.tcp://localhost:9838"/>
        </baseAddresses>
      </host>
      <endpoint address="srv"
                contract="App.IContrato1"
                binding="netTcpBinding"/>
      <endpoint address="srv"
                contract="App.IContrato2"
                binding="netTcpBinding"/>
    </service>
  </services>
</system.serviceModel>

Repare que temos dois contratos (IContrato1 e IContrato2) expostos através de TCP e usando o mesmo endereço. Isso somente é possível porque o WCF reutiliza a mesma instância do binding para efetuar a comunicação, e difere a execução baseando-se nas Soap Actions. Se cada binding tiver sua configuração específica (definido através do atributo bindingConfiguration), então isso fará com que o WCF crie instâncias diferentes do mesmo binding, resultando em uma exceção tipo do InvalidOperationException sendo disparada, informando que não é permitido associar uma mesma instância binding à endereços diferentes.

Se você tiver endereços distintos, então duas instâncias distintas do binding correspondente serão criadas para atender cada um deles. Esse mesmo cuidado você precisa ter ao configurar o serviço de forma imperativa. Ao chamar o método AddServiceEndpoint da classe ServiceHost, você deverá se preocupar em passar a mesma instância do binding, como por exemplo:

using (ServiceHost host = 
       new ServiceHost(typeof(Servico), new Uri[] { new Uri("net.tcp://localhost:9838") }))
{
    NetTcpBinding b = new NetTcpBinding();

    host.AddServiceEndpoint(typeof(IContrato1), b, "srv");
    host.AddServiceEndpoint(typeof(IContrato2), b, "srv");

    host.Open();
    Console.ReadLine();
}

Tags: , , , ,

WCF

Threading em WPF

by Israel Aece 24. November 2009 21:27

Quando desenvolvemos aplicações Windows, é muito comum em algum ponto da mesma, que algumas tarefas mais complicadas e custosas sejam realizadas, que podem levar um tempo maior até que seja concluída. Independentemente do que ela faça, seja um cálculo, uma consulta em uma base de dados ou uma chamada para um serviço, se você executar esse código de forma síncrona, o usuário deverá esperar até que essa tarefa seja finalizada, para a partir daí, conseguir acessar outras áreas do sistema.

Ao rodar uma aplicação Windows, um processo é criado dentro do sistema operacional. Processo não executa nenhum código; são as threads que fazem isso. Quando o processo é criado, uma thread é criada juntamente com ele, e esta é muitas vezes chamada de main-thread (thread principal). Essa thread nasce e morre com o término do processo, ou seja, enquanto ela estiver executando alguma tarefa, o processo continuará ativo.

As aplicações Windows que conhecemos, como Windows Forms, Console, Windows Services e WPF trabalham nesta mesma linha. Aplicações que possuem gráficos, como é o caso do Windows Forms e do WPF, tem um agravante: a afinidade que os controles tem com a thread principal. Quando a aplicação é iniciada, a thread principal é quem cria os controles (Form, TextBox, Label, TextBlock, etc.), e quando dizemos que há uma afinidade, isso quer dizer que podemos somente manipular esses controles, através da mesma thread que os criaram, e qualquer tentativa de fazer isso através de uma segunda thread, uma exceção do tipo InvalidOperationException será disparada.

Quando essas tarefas são finalizadas, é normal queremos exibir o resultado para o usuário, que na maioria das vezes, implica em alterar a propriedade Text de algum controle, exibir uma MessageBox, etc. Em Windows Forms, todos os controles herdam direta ou indiretamente da classe Control, que fornece um método chamado Invoke, e que dado um delegate, executa o método relacionado na mesma thread do criador. Mais tarde, com o .NET Framework 2.0, surgiu o SynchronizationContext, que facilitou bastante a atualização dos controles a partir de uma thread secundária.

Agora temos o WPF, que traz novas funcionalidades e uma forma um pouco diferente para lidar com esse tipo de problema. Vamos a partir deste artigo, explorar um pouco mais sobre o modelo de threading do WPF. A Microsoft introduziu uma série de novos tipos, espalhados por vários namespaces e que serão utilizados para conseguir atingir o nosso principal objetivo. Para iniciar, o primeiro tipo que temos que conhecer é a classe Dispatcher. Essa classe serve como um gerenciador de tarefas para serem executadas, e está sempre associada com uma determinada thread de UI. Ela mantém uma fila de tarefas que são executadas utilizando a thread qual está relacionada.

A fila que é mantida pela classe Dispatcher é priorizada, que permite especificar uma prioridade antes de enfileirar a tarefa (mais detalhes abaixo). Para alistar uma tarefa nesta fila, você poderá utilizar o método Invoke ou BeginInvoke. A diferença entre eles é clara: o primeiro executa a tarefa de forma síncrona, enquanto a segunda alista a tarefa para ser executada de forma assíncrona. E para sedimentar, ambas sempre executarão na thread ao qual o Dispatcher está vinculado.

Grande parte das classes que compõem o framework do WPF, incluindo os controles, herda direta ou indiretamente da classe abstrata DispatcherObject, que possui uma estrutura simples, ou seja, fornece uma propriedade chamada Dispatcher que retorna a instância de uma classe Dispatcher, e como já era de esperar, fornece a instância do Dispatcher que está vinculado com aquele classe/controle.

A classe DispatcherObject ainda fornece dois métodos importantes: CheckAccess e VerifyAccess. A diferença entre eles é que o primeiro retorna um valor boleano, indicando se a thread que está chamando tem direito de acesso ao Dispatcher correspondente. Já o segundo método, VerifyAccess, dispara uma exceção do tipo InvalidOperationException, caso a thread que está chamando não tiver direito de acesso ao Dispatcher. Como pudemos perceber, esses métodos vão nos auxiliar para determinar se há ou não a necessidade de atualizar o controle através da thread atual, sem que seja necessário utilizar o Dispatcher para chegar até o controle.

Para exemplificar, imagine que temos uma thread que executará algum cálculo complexo, e depois de calculado, deverá exibir o resultado em um TextBox. Como comentado acima, dentro desta thread não podemos alterar qualquer propriedade do TextBox, e para solucionar isso no WPF, vamos recorrer a propriedade Dispatcher do TextBox ("txt"), que foi herdada de DispatcherObject. Ao passar um delegate para o método Invoke, ele será executado (de forma síncrona) na mesma thread que criou o controle.

new Thread(() =>
{
    int result = 2 ^ 4 * 2 + 3 / 3;
    Thread.Sleep(3000); //Simula Processo Complexo

    txt.Dispatcher.Invoke(new Action<int>(r => txt.Text = r.ToString()), result);
}).Start();

Se desejar, podemos trocar o método Invoke por BeginInvoke, e a atualização do controle será feita em background. A vantagem desta técnica é que você pode executar a atualização do controle enquanto faz outras tarefas. É importante que você mantenha tarefas "leves" dentro do Dispatcher, pois tudo o que ele deveria fazer ali é atualizar a UI; colocar tarefas mais complexas, voltará a ter concorrência com os eventos dos controles e, consequentemente, o usuário voltará a ter os travamentos das telas do sistema, que acontecia quando trabalhávamos de forma síncrona.

Quando você opta por utilizar o método BeginInvoke, ele retorna uma instância da classe DispatcherOperation. Basicamente, este objeto representa uma espécie de "ponteiro" para a operação que está sendo executada. Essa classe fornece uma série de membros interessantes, e entre eles temos:

  • Dispatcher: O Dispatcher relacionado.
  • Priority: Uma das opções definidas no enumerador DispatcherPriority, que define a prioridade da operação.
  • Result: Retorna um System.Object com o resultado da tarefa (isso quando ela retornar algum resultado).
  • Status: Uma das opções definidas no enumerador DispatcherOperationStatus, que define o status atual da operação (Pending, Aborted, Completed ou Execution).
  • Abort: Método que aborta a operação que está sendo executada.
  • Wait: Quando invocado, fará um "join" na thread atual, aguardando até o término da operação. Opcionalmente você pode especificar um timeout.
  • Aborted: Evento que é disparado quando a operação é abortada.
  • Completed: Evento que é disparado quando a operação foi finalizada.

Com a instância do DispatcherOperation em mãos, podemos utilizar duas formas para chegar até o resultado, que é via eventos ou através de polling. Utilizando o modelo de eventos, podemos nos vincular ao evento Completed, e quando a operação for finalizada, esse evento será automaticamente disparado. Já o polling consiste em testar, de tempo em tempo, se a operação finalizou ou não. Abaixo temos os dois exemplos de utilização:

new Thread(() =>
{
    int result = 2 ^ 4 * 2 + 3 / 3;
    Thread.Sleep(3000); //Simula Processo Complexo

    DispatcherOperation op =
        txt.Dispatcher.BeginInvoke(new Action<int>(r => txt.Text = r.ToString()), result);

    op.Completed += (o, args) => MessageBox.Show("Finalizou Tudo");
}).Start();


new Thread(() =>
{
    int result = 2 ^ 4 * 2 + 3 / 3;
    Thread.Sleep(3000); //Simula Processo Complexo

    DispatcherOperation op =
        txt.Dispatcher.BeginInvoke(new Action<int>(r => txt.Text = r.ToString()), result);

    //Faz Algo...

    while (op.Status != DispatcherOperationStatus.Completed)
    {
        if (op.Wait(TimeSpan.FromSeconds(5)) == DispatcherOperationStatus.Completed)
        {
            //Finalizou a Atualização da UI
            break;
        }
    }
}).Start();

Tanto o método Invoke quanto o BeginInvoke possui versões (overloads) destes métodos que permitem especificar uma prioridade, e para isso, utilizamos uma das doze opções definidas pelo enumerador DispatcherPriority. Há algumas opções interessantes, como por exemplo Inactive, que permite você alistar a operação, mas que não será processada. De qualquer forma, na maioria das vezes a opção Normal (que também é o padrão), já será o suficiente. Abaixo um exemplo de como podemos proceder para especificar a prioridade de uma operação:

txt.Dispatcher.BeginInvoke(new Action<int>(r => txt.Text = r.ToString()), DispatcherPriority.Normal, result);

Uma vez que você tem operações alistadas no Dispatcher, o runtime irá determinar quando executá-las, dependendo da prioridade definida. A classe Dispatcher também fornece métodos para abortar todas as operações pendentes de processamento. Para isso, recorremos aos métodos InvokeShutdown ou BeginInvokeShutdown (a diferença entre eles já é sabida). A classe Dispatcher ainda fornece um evento chamado ShutdownFinished, que é disparado quando shutdown do Dispatcher for completamente finalizado, e com isso, você poderá tomar alguma decisão.

Conclusão: Como vimos, ambas tecnologias (Windows Forms e WPF) possuem os mesmos comportamentos. Como trabalhar com ambientes multi-threading não é uma tarefa fácil, a Microsoft introduziu no WPF uma forma diferente de se trabalhar para a atualizar de UI através de uma segunda thread. Além de um modelo suavemente diferente, há algumas melhorias internas que garantem que isso acabe sendo executado de uma forma melhor. E para finalizar, essa técnica visa sempre tornar a aplicação mais amigável para o usuário, sem que eles tenha experiências ruins.

Tags: ,

Async | WPF

Dectetando a desconexão

by Israel Aece 23. November 2009 07:22

Um cenário muito comum em serviços, é a capacidade que os mesmos tem de conseguir invocar algum método do lado do cliente, em resposta ou notificação à algum acontecimento que ocorreu. Isso é conhecido no WCF como comunicação duplex, onde você cria um contrato e que o cliente será obrigado a implementá-lo para que, eventualmente, seja notificado de que algo aconteceu. Para maiores detalhes em como implementar isso, consulte esse artigo ou este vídeo.

Para extrair o canal de comunicação entre o serviço e o cliente, utilizamos o método genérico GetCallbackChannel<T> da classe OperationContext. Esse método retorna uma espécie de proxy, que também implementa a interface de callback, especificada através do parâmetro genérico T. Como a ideia é armazená-lo para mais tarde invocar, pode acontecer deste cliente já não estar mais online, e o canal de comunicação já encontra-se em um estado "inválido".

Se você não se atentar à esse detalhe e, incondicionalmente, invocar o callback, você receberá uma exceção do tipo CommunicationObjectAbortedException, informando que o canal de comunicação com o respectivo cliente foi abortado. Antes de mostrar como dectectar a desconexão, precisamos conhecer um novo tipo fornecido pelo próprio WCF, e que tem um papel extremamente importante dentro deste framework: a interface ICommunicationObject.

Essa interface define um contrato para todos os objetos de comunicação dentro do WCF, gerenciando as conexões que são relizadas através dos métodos Open, Close e Abort (e suas versões assíncronas). Essa interface ainda fornece uma propriedade chamada State, que retorna uma das opções definidas no enumerador CommunicationState. Além disso, ela ainda fornece eventos que são disparados tão logo quando o canal entrar em um estado diferente. Entre os eventos, temos: OpenedClosed e Faulted.

Apesar do método GetCallbackChannel<T> retornar a instância tipo genérico especificado em T, ele pode ser convertido em ICommunicationObject, onde você terá acesso à todos os membros que vimos acima, correspondentes ao cliente que você deseja se comunicar. Tendo acesso a está interface, você tem duas opções para identificar a desconexão. A primeira delas é antes de invocar o callback, avaliar a propriedade State, e se ela ainda estiver aberta, você pode invocar sem maiores problemas. O exemplo abaixo ilustra isso:

ICallback callback = OperationContext.Current.GetCallbackChannel<ICallback>();

if (((ICommunicationObject)callback).State == CommunicationState.Opened)
    callback.OnCallback("Algum Parametro");

A segunda opção consiste em assinar o evento Closed, ou dependendo da situação, o Faulted, que serão disparados quando o canal de comunicação for fechado ou quando ele entrar em um estado falho, respectivamente. Dessa forma você pode ser notificado quando isso acontecer, e tormar alguma decisão em cima disso. O código abaixo ilustra como proceder para assinar o evento Closed:

((ICommunicationObject)callback).Closed += (sender, args) => Console.WriteLine("Fechou!");

Esse tipo de técnica é comumente utilizada quando armazenamos do lado do serviço, a referência para um ou vários clientes, como é o caso de aplicações de chat ou algum modelo de publish-subscribe.

Tags:

WCF

Serviços TCP no Silverlight

by Israel Aece 20. November 2009 21:07

A cada dia que o passa, o Silverlight tem ganhado cada vez mais espaço nos sites que são construídos ao redor do mundo. Motivo para a Microsoft evoluir essa tecnologia rapidamente, adicionando cada vez mais funcionalidades dentro dele.

Os adeptos ao Silverlight não se limitam à aqueles construtores de sites institucionais, que na maioria das vezes, são criados para campanhas publicitárias e com poucas funcionalidades dinâmicas. Ele também está atingindo as empresas desenvolvem os softwares para uso interno, justamente porque a Microsoft incorpora nele, funcionalidades que permitem o uso em aplicações do tipo LOB (Line of Business).

Uma das novidades mais recentes, que afeta principalmente o uso de aplicações Silverlight em intranets, é que a partir da versão 4.0, teremos a possibilidade de referenciar e consumir serviços construídos em WCF, através do protocolo TCP (net.tcp).

Como disse no parágrafo acima, esse tipo de protocolo é útil em intranets, já que não haverá restrições de eventuais firewalls, e mesmo que tenha, tudo é negociável. É importante dizer, que as aplicações Silverlight estão condicionadas à utilizarem o seguinte intervalo de portas: 4502 à 4534, ou seja, somente conseguirá acessar um serviço WCF exposto através de uma delas.

Uma das principais características do protocolo TCP, é que possibilita a comunicação duplex, ou seja, permite que o serviço se comunique com o cliente, através de callbacks. Isso já era possível no Silverlight, através do Polling Duplex, que nada mais é do que uma especialização do protocolo BasicHttpBinding, mas que simula callbacks em cima do protocolo HTTP.

Outra grande vantagem do protocolo TCP quando comparado com o HTTP, é a performance, e mesmo em aplicações Silverlight, essa vantagem é bastante significativa, e um dos grande responsáveis por isso, é a codificação binária. Neste momento, para usar este protocolo, você deve abrir mão da segurança em nível de transporte, que não é suportado.

Os tipos necessários para acessar um serviço WCF através de TCP, estão contidos no assembly System.ServiceModel.NetTcp.dll, localizado em %Program Files%\Microsoft SDKs\Silverlight\v4.0\Libraries\Client. Mas isso não quer dizer que você terá uma classe chamada NetTcpBinding, assim como há no .NET Full. O Silverlight utiliza um CustomBinding, composto com os seguintes bindings elements: BinaryMessageEncodingBindingElement e TcpTransportBindingElement. Ao referenciar o serviço TCP em uma aplicação Silverlight 4.0, a IDE do Visual Studio 2010 já faz as seguintes entradas no respectivo arquivo de configuração:

<configuration>
    <system.serviceModel>
        <bindings>
            <customBinding>
                <binding name="NetTcpBinding_IContrato">
                    <binaryMessageEncoding />
                    <tcpTransport
                        maxReceivedMessageSize="2147483647"
                        maxBufferSize="2147483647" />
                </binding>
            </customBinding>
        </bindings>
        <client>
            <endpoint
                address="net.tcp://localhost:4502/srv"
                binding="customBinding"
                bindingConfiguration="NetTcpBinding_IContrato"
                contract="Servico.IContrato"
                name="NetTcpBinding_IContrato" />
        </client>
    </system.serviceModel>
</configuration>

Ao contrário de outros tipos de comunicação e de aplicações, não basta simplesmente termos o cliente e o serviço funcionando para que eles possam se comunicar. Mesmo através de TCP, este protocolo também está condicionado às políticas de cross-domain.

Para comunicação HTTP, tudo o que precisamos é definir um arquivo XML na aplicação, que permite o acesso. Para suportar a comunicação através de TCP, o Silverlight resolve a questão da restrição de cross-domain de uma forma mais rebuscada. A Microsoft criou uma template de projeto chamada Silverlight TCP Socket Policy, que pode ser acessada a partir das templates Online do Visual Studio 2010 ou através do Visual Studio Gallery, neste endereço.

Essa template dá origem à um projeto do tipo Console, que expõe o arquivo de cross-domain (definido no arquivo SocketPolicy.cs), definindo o intervalo de portas que é permitido que aplicações Silverlight utilizem. Ao rodar as aplicações, certifique-se também de que este projeto esteja rodando, caso contrário, você receberá uma exceção do tipo CommunicationException, informando que você não tem permissões para efetuar o acesso ao serviço, sugerindo que talvez esteja faltando o arquivo de cross-domain.

Conclusão: Apesar de ainda estar em sua versão Beta, o Silverlight 4.0 traz uma série de melhorias e novas funcionalidades, que facilitará cada vez mais a criação de aplicações LOB, e com certeza, uma das principais limitações até então, era o consumo de serviços WCF através do protocolo TCP, que é fator determinante em aplicações deste tipo.

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

Caching via Providers

by Israel Aece 16. November 2009 13:08

Assim como o membership, roles, profile, webparts ou health monitoring, a versão 4.0 do ASP.NET também permitirá trabalhar com o caching seguindo essa mesma arquitetura, ou seja, através de provider models. Com isso, teremos uma maior flexibilidade para elegermos o nosso repositório de caching, que nem sempre será a memória do próprio servidor onde a aplicação está hospedada. Provavelmente será através deste recurso que a Microsoft irá acoplar o "Velocity" ao pipeline do ASP.NET.

Para customizar, tudo o que precisamos fazer é criar uma classe que herde da classe abstrata OutputCacheProvider, e sobrescrever os seus métodos autoexplicativos: Add, Get, Remove e Set, assim como é mostrado abaixo.

public class TextCacheProvider : OutputCacheProvider
{
    private static readonly string PATH;

    static TextCacheProvider()
    {
        PATH = HttpContext.Current.Server.MapPath("~/CachedPages/");
    }

    public override object Add(string key, object entry, DateTime utcExpiry) { } 

    public override object Get(string key) { }

    public override void Remove(string key) { }

    public override void Set(string key, object entry, DateTime utcExpiry) { }
}

Depois desta classe criada, que seguirá as regras de caching customizadas, precisamos configurá-la para poder ser utilizada, e para isso recorremos ao arquivo Web.config. Tudo o que precisamos fazer para que essa nova classe funcione, é acoplá-la a execução, definindo-a como sendo o provider padrão de caching. O trecho de código abaixo ilustra essa configuração:

<?xml version="1.0"?>
<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.0"/>
    <caching>
      <outputCache defaultProvider="TextCacheProvider" enableOutputCache="true">
        <providers>
          <clear/>
          <add name="TextCacheProvider" type="TextCacheProvider"/>
        </providers>
      </outputCache>
    </caching>
</configuration>

E se desejar escolher, em tempo de execução, um dos providers de acordo com uma condição específica, você poderá fazer isso através do método GetOutputCacheProviderName, que é exposto pela classe HttpApplication (Global.asax), e retorna uma string que representá o provider a ser utilizado.

Tags: , ,

ASP.NET

Compatibilidade dos Providers

by Israel Aece 9. November 2009 05:28

Quando utilizamos qualquer uma das funcionalidades que são expostas pelo ASP.NET (membership, roles, profile, webparts ou health monitoring), geralmente recorremos ao SQL Server para armazenar essas informações. E para isso, tudo o que precisamos fazer é executar o utilitário aspnet_regsql.exe, que nos permite informar uma base de dados e as funcionalidades que desejamos instalar.

Uma vez instalado, fazemos todo o desenvolvimento em cima deste banco, e quando chega o momento de instalar a aplicação no servidor, muitas vezes um script com a estrutura da base de dados é gerado, e mais tarde, executado neste servidor que hospedará a aplicação. O problema é que o script somente levará a estrutura e não os dados. Com isso, ao executar a aplicação a partir do servidor, teremos a seguinte mensagem sendo disparada:

The 'System.Web.Security.SqlMembershipProvider' requires a database schema compatible with schema version '1'.  However, the current database schema is not compatible with this version.  You may need to either install a compatible schema with aspnet_regsql.exe (available in the framework installation directory), or upgrade the provider to a newer version.

Entre as tabelas que o utilitário cria para suportar as funcionalidades, temos a tabela aspnet_SchemaVersions. Essa tabela armazena a versão atual que está instalada, e é uma informação obrigatória, que o runtime do ASP.NET utiliza, e quando ela não estiver presente, o ASP.NET não conseguirá determinar qual versão é e, consequentemente, não rodará, disparando a mensagem que vimos acima.

Uma das opções que temos para resolver isso, é gerar um script com cláusulas INSERT INTO, criando um registro para cada funcionalidade habilitada, assim como podemos notar no exemplo abaixo:

INSERT INTO [dbo].[aspnet_SchemaVersions] ([Feature], [CompatibleSchemaVersion], [IsCurrentVersion]) 
    VALUES (N'common', N'1', 1)

INSERT INTO [dbo].[aspnet_SchemaVersions] ([Feature], [CompatibleSchemaVersion], [IsCurrentVersion]) 
    VALUES (N'health monitoring', N'1', 1)

INSERT INTO [dbo].[aspnet_SchemaVersions] ([Feature], [CompatibleSchemaVersion], [IsCurrentVersion]) 
    VALUES (N'membership', N'1', 1)

INSERT INTO [dbo].[aspnet_SchemaVersions] ([Feature], [CompatibleSchemaVersion], [IsCurrentVersion]) 
    VALUES (N'personalization', N'1', 1)

INSERT INTO [dbo].[aspnet_SchemaVersions] ([Feature], [CompatibleSchemaVersion], [IsCurrentVersion]) 
    VALUES (N'profile', N'1', 1)

INSERT INTO [dbo].[aspnet_SchemaVersions] ([Feature], [CompatibleSchemaVersion], [IsCurrentVersion]) 
    VALUES (N'role manager', N'1', 1)

Mas, o ideal é utilizar a opção -exportonly do utilitário aspnet_regsql.exe. Ao rodar este comando, ele também gerará um script SQL para adicionar ou remover as funcionalidades (e não executará), mas incluindo tudo o que é necessário, inclusive as respectivas inserções na tabela acima mencionada. De posse deste script, tudo que tem a fazer é rodar no servidor de destino.

Tags:

ASP.NET

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