Páginas

sábado, 15 de dezembro de 2012

Versão 1.0.1 do NHilo!

Hoje o post é para falar que está disponível a primeira versão estável da biblioteca NHilo (http://nhilo.codeplex.com/), componente para se gerar chaves primárias utilizando o algoritmo hilo do NHibernate!

As mudanças que ocorreram entre a versão beta e esta são:

  • Inclusão de providers para trabalhar com MySQL e Oracle.
  • Criação de um método de extensão para retornar um valor inteiro de 32-bits ao invés de um inteiro de 64-bits.
  • Mudanças de algumas classes para o namespace raiz.
  • Correção de alguns bugs referentes ao schema de configuração.
Por enquanto é só. A biblioteca pode ser baixada tanto nosite do Codeplex quanto pelo Nuget (www.nuget.org).

[]'s

sábado, 24 de novembro de 2012

Usando o padrão Decorator (GoF)

O padrão Decorator, do livro Design Patterns: Elements of Reusable Object-Oriented Software, é útil quando precisamos adicionar um comportamento em uma classe já existente. Vamos demonstrar isso através de um exemplo de uma classe de acesso ao banco de dados onde será adicionado o uso de cache.

Primeiro temos  nossa classe que acessará o "banco de dados". Na verdade, a nossa base nada mais é que um array em memória. Optei por fazer assim para manter a simplicidade do exemplo e focarmos no que realmente é importante. Além disso, a busca demora propositadamente 3 segundos para simularmos todo o trabalho de se acessar um recurso externo à aplicação como um banco de dados.

// RepositorioConfiguracaoImpl.cs
using System.Collections.Generic;
using System.Threading;

namespace ExemploDecorator
{
    public class RepositorioConfiguracaoImpl : IRepositorioConfiguracao
    {
        private readonly Dictionary<string, string> _configuracoes = new Dictionary<string, string>()
        {
            {"REGISTROS_POR_GRID", "50"},
            {"TEMPO_SESSAO", "20"},
            {"TIMEOUT_CONEXAO_DB", "30"}
        };

        public string ObterConfiguracao(string chave)
        {
            Thread.Sleep(3000);
            return _configuracoes[chave];
        }
    }
}

Vejam que esta classe implementa uma interface, um contrato que dita quais são os métodos e suas assinaturas obrigatórias. Este é um ponto muito importante do Decorator e não pode ser esquecido.

// IRepositorioConfiguracao.cs

namespace ExemploDecorator
{
    public interface IRepositorioConfiguracao
    {
        string ObterConfiguracao(string chave);
    }
}

Agora vamos pensar. Como seria o uso dessa classe que recupera configurações para o seu cliente? Em primeiro lugar, o seu cliente deve trabalhar referenciando a interface, e não a classe concreta. Este é outro ponto importante para minimizar o impacto de se incluir uma lógica de cache nesse nosso exemplo. Vamos ver o código que consome esse repositório.

// Program.cs
using System;
using System.Diagnostics;
using Ninject;

namespace ExemploDecorator
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Iniciando testes...\n");
            var kernel = new StandardKernel(new ModuloInjecaoDependencia());
            IRepositorioConfiguracao repositorio = kernel.Get<IRepositorioConfiguracao>();

            MostrarConfiguracao("TIMEOUT_CONEXAO_DB", repositorio);
            MostrarConfiguracao("TIMEOUT_CONEXAO_DB", repositorio);
            MostrarConfiguracao("REGISTROS_POR_GRID", repositorio);
            MostrarConfiguracao("REGISTROS_POR_GRID", repositorio);
            MostrarConfiguracao("REGISTROS_POR_GRID", repositorio);

            Console.WriteLine("FIM");
        }

        private static void MostrarConfiguracao(string chave, IRepositorioConfiguracao repositorio)
        {
            Console.WriteLine("Obtendo configuração para '{0}'...", chave);
            var cronometro = Stopwatch.StartNew();
            Console.WriteLine("Configuração = '{0}'.", repositorio.ObterConfiguracao(chave));
            cronometro.Stop();
            Console.WriteLine("Tempo da chamada : {0} ms. \n", cronometro.ElapsedMilliseconds);
        }
    }
}

// ModuloInjecaoDependencia.cs
using Ninject.Modules;

namespace ExemploDecorator
{
    public class ModuloInjecaoDependencia : NinjectModule
    {
        public override void Load()
        {
            Bind<IRepositorioConfiguracao>().To<RepositorioConfiguracaoImpl>();
        }
    }
}

Nosso programa está usando o Ninject para poder resolver a interface IRepositorioConfiguracao para RepositorioConfiguracaoImpl. Como não é objetivo deste post, deixo para vocês lerem a respeito deste ótimo framework para injeção de dependência. Outro detalhe que eu gostaria de comentar sobre este ponto é que eu poderia utilizar simplesmente uma classe factory para resolver a dependência, isolando os detalhes de como se cria a instância de IRepositorioConfiguracao do seu cliente. Escolhi o Ninject por pura vontade.

Executando este código, temos o seguinte resultado, onde cada chamada ao repositório leva em torno de 3 segundos.



Bom, se o cliente está trabalhando com a interface, então podemos trocar o uso da classe RepositorioConfiguracaoImpl por qualquer classe que implemente o contrato IRepositorioConfiguracao, correto? É aqui que vamos usar o Decorator. Vamos criar uma classe que implemente IRepositorioConfiguracao e que contenha, internamente, uma campo do tipo IRepositorioConfiguracao. Essa primeira classe, que chamaremos de RepositorioConfiguracaoComCache, irá receber uma instância de  RepositorioConfiguracaoImpl no seu construtor, que será armazenada internamente. Em toda a chamada do método ObterConfiguracao de RepositorioConfiguracaoComCache, ela irá fazer a lógica de controle de cache e, se necessário, redirecionar a chamada para o ObterConfiguracao de RepositorioConfiguracaoImpl para efetivamente recuperar o valor da configuração no "banco de dados" (ou seja, se a informação está no cache, usa o dado do cache; caso contrário, vai ao banco, pega o dado, atualiza o cache e retorna a informação).

// RepositorioConfiguracaoComCache.cs
using System.Runtime.Caching;

namespace ExemploDecorator
{
    public class RepositorioConfiguracaoComCache : IRepositorioConfiguracao
    {
        private ObjectCache _cache = MemoryCache.Default;
        private IRepositorioConfiguracao _repositorio;

        public RepositorioConfiguracaoComCache(IRepositorioConfiguracao repositorio)
        {
            _repositorio = repositorio;
        }

        public string ObterConfiguracao(string chave)
        {
            if (!_cache.Contains(chave))
            {
                string valor = _repositorio.ObterConfiguracao(chave);
                _cache.Add(chave, valor, null);
            }
            return (string)_cache[chave];
        }
    }
}

Obs. Estamos usando a classe ObjectCache, disponível a partir do .NET 4.0. Se você está criando este exemplo junto da leitura deste post, e criou um projeto do tipo ConsoleApplication, lembre-se de mudar a propriedade "Target framework" do seu projeto de ".NET Framework 4.0 Client profile" para ".NET Framework 4.0".

Para que o cliente passe a utilizar a nova classe RepositorioConfiguracaoComCache, basta alterar a configuração do Ninject no módulo que criamos.

// ModuloInjecaoDependencia.cs
using Ninject.Modules;

namespace ExemploDecorator
{
    public class ModuloInjecaoDependencia : NinjectModule
    {
        public override void Load()
        {
            Bind<IRepositorioConfiguracao>().ToMethod((ctx) => new RepositorioConfiguracaoComCache(new RepositorioConfiguracaoImpl()));
        }
    }
}

Com isso, ao executarmos novamente a aplicação, temos o acesso ao repositório feito com cache (primeiro acesso mais demorado, mas os demais rápidos)!


Bem útil, se bem aplicado, não acham?

Um último comentário. Esta não é a única maneira de se implementar esse tipo de funcionalidade. Existe um paradigma de programação chamado AOP (Aspect Oriented Programming) que trata da inclusão de comportamento adicional em classes já existentes, principalmente se o comportamento não for uma regra de negócio mas sim algo relacionado a log, segurança, etc. Sobre AOP, há alguns anos eu participei de um podcast sobre o assunto http://podcast.dotnetarchitects.net/2010/05/podcast-13programacao-orientada-a-aspecto/. Vale a pena conferir pois a técnica também é bem útil e em alguns casos mais fácil de implementar que o Decorator (principalmente para sistemas construídos sem levar em conta o uso correto da Orientação a Objetos).

[]'s

quinta-feira, 27 de setembro de 2012

NHiLo - Gerador de chaves primárias (substitutas)

Hoje subi pela primeira vez um pacote Nuget para o seu repositório oficial. Trata-se de um pequeno componente de código aberto que eu fiz para permitir a geração de chaves primárias para registros sem a dependência do banco de dados para isso. Leia-se: NÃO PRECISAR MAIS DE COLUNAS DE AUTO-NUMERAÇÃO COMO IDENTITY.

O uso de colunas do tipo identity são uma das poucas coisas que eu torço o nariz sem antes saber o motivo porque foi escolhido essa abordagem para chaves substitutas. Não gosto porque esse tipo de campo dificulta se precisarmos manter bases sincronizadas, pois o ID da coluna pode ser diferente em cada uma delas.

Este componente é uma implementação isolada do algoritmo HiLo, famoso no NHibernate. Não é nada novo no mundo do desenvolvimento (e todo o crédito para os desenvolvedores do Hibernate/NHibernate), mas que não encontrei uso disponível fora desse framework de ORM. Inclusive esse foi o motivo de eu criá-lo: poder usar o HiLo em uma situação que eu não posso trabalhar com ferramentas de ORM por restrições do cliente :-( .

Ele trabalha separando uma faixa de chaves primárias que podem ser definidas pela própria aplicação. Isso quer dizer que qualquer dado é gerenciado totalmente pela aplicação, o repositório não é responsável por definir nada. Funciona assim: digamos que nossas faixas de chaves disponíveis variam de 100 em 100 registros. Grava-se numa tabela de controle o valor 1. Então a aplicação vai usando as chaves disponíveis, sempre incrementando 1 (veja que isso é feito sem interferência do repositório, chave 1, 2, 3 e assim por diante). Quando a aplicação chegar na última faixa disponível, vamos novamente para a tabela de controle e gravamos 2 (faixa de 101 até 200). Se durante o uso da aplicação outra instância da mesma necessitar de uma faixa de dados, sem problemas, ela vai nessa tabela e grava 3. A primeira instância continua usando a faixa 2, e quando necessitar de uma faixa nova, ela receberá a 4.

Bom, alguns podem argumentar que ainda assim há a dependência com o repositório, pois é nele que são armazenados os dados de controle. Sim, existe essa dependência, entretanto quem efetivamente define o valor de uma nova chave disponível não é o repositório, mas sim a aplicação.

Apesar de existir um momento em que há uma ida ao banco de dados, ela ocorre esporadicamente, e na maioria das vezes a geração da nova chave é uma operação rápida, sem ônus no desempenho da aplicação. Veja por exemplo o gráfico abaixo, que peguei de um teste que fiz. Os "picos" ocorrem de 100 em 100, exatamente no momento em que se busca um novo valor de faixa. O valor deles gira em torno de 2 milissegundos...

Gráfico mostrando poucos picos de "demora" na geração das chaves

O código de exemplo é bem simples. Basicamente criamos um factory (HiLoGeneratorFactory) e obtemos o gerador de chaves para determinada entidade.

[TestMethod]
    public void ShouldReturnAValidKey()
    {
    // Arrange
    var factory = new HiLoGeneratorFactory();
    // Act
    var generator = factory.GetKeyGenerator("dummy4");
    var key1 = generator.GetKey();
    var key2 = generator.GetKey();
    // Assert
    Assert.IsTrue(key1 > 0);
    Assert.IsTrue(key2 > 0);
}


Não é necessária nenhuma configuração adicional para o projeto funcionar, basta o arquivo config da aplicação possuir pelo menos uma string de conexão (faz sentido, pois precisamos acessar o banco de dados de alguma forma). NHiLo irá considerar sempre a última string de conexão para seu uso. Existe a possibilidade de configurar algumas coisas, como se quisermos escolher outra string de conexão ou mesmo o tamanho da faixa, mas isto vou deixar para outro post.

Além disso, foi testado apenas o uso do NHiLo no SQL Server e no SQL Server CE (a definição do tipo do repositório varia com o provider da string de conexão). A implementação para MySQL está pronta mas eu não testei, e a para Oracle ainda não começou a ser feita.


Bom, quem quiser testar a versão que está disponível no Nuget é só procurar por NHiLo, lembrando que é um pacote de pré-release (versão beta).

Tela mostrando o Nuget com o NHiLo disponibilizado.


Além disso, os fontes desse projeto estão hospedados no Codeplex, quem quiser olhar como foi feito ou mesmo contribuir é só entrar em contato comigo!


Abraços e até a próxima!

sexta-feira, 21 de setembro de 2012

Por que tá dando erro?


Hoje um colega de trabalho me mandou o seguinte código, trecho de um teste de unidade.

var objeto = null;
Assert.IsNull(objeto.atributo);

O questionamento era "por que tá dando erro?".

Que coisa, né? Tá dando erro...

[]'s


segunda-feira, 13 de agosto de 2012

Injeção de Dependência em ASP.NET Webforms

Muito do material que vemos pela Internet a respeito de Inversão de Controle e Injeção de Dependência está relacionado ao ASP.NET MVC. Hoje o meu objetivo aqui é mostrar como fazer uso de um contêiner de IoC com o "patinho feio" do ASP.NET, o Webforms.

Mas antes, vamos contextualizar o que é Inversão de Controle e Injeção de Dependências. Inversão de Controle é um conceito de programação onde é tirada a responsabilidade de uma classe em criar as suas dependências. Vejamos o código abaixo. Nele, a página ASP.NET (que é uma classe) instancia uma classe chamada SaudacaoPTBr e chama seu método Saudar para retornar uma informação qualquer. Por isso, esta página depende de SaudacaoPTBr. Perceba que para utilizar essa dependência, a página ASP.NET teve que, no seu próprio código, criar um objeto do tipo SaudacaoPTBr.

public partial class _Default : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        var saudacao = new SaudacaoPTBr();
        Label1.Text = saudacao.Saudar();
    }
}

Na Inversão de Dependência, a página ASP.NET não poderia mais fazer isso. Lhe é tirada essa responsabilidade. E nesse conceito, como seria o código que temos que usar? A Inversão de Dependência nos dá algumas opções para implementá-la:
  • Podemos usar o padrão Factory
  • Podemos usar o padrão Service Locator
  • Podemos usar Injeção de Dependência
Vamos nos focar neste último.

A primeira coisa que vamos fazer é tirar a dependência da página ASP.NET com a classe SaudacaoPTBr. Fazemos isso criando uma interface (ISaudacao) e criando uma propriedade com essa interface na nossa página ASP.NET.

public interface ISaudacao
{
    string Saudar();
}

public partial class _Default : System.Web.UI.Page
{
    [Inject]
    public ISaudacao Saudacao { get; set; }

    protected void Page_Load(object sender, EventArgs e)
    {
        Label1.Text = Saudacao.Saudar();
    }
}

A ideia aqui é fazer com que "alguém" inicialize a propriedade Saudacao com a correta instância para que ela possa ser usada no código e faça seu trabalho. Ou seja, precisaríamos de alguém que fizesse algo parecido com o código abaixo: criar uma instância da página web, preencher sua propriedade (sua dependência) com o objeto correto, e retornar essa página pronta para uso.

private _Default CriarPaginaDefault()
{
    var pagina = new _Default();
    pagina.Saudacao = new SaudacaoPTBr();
    return pagina;
}

Veja aqui o conceito de Inversão de Controle em ação. A página Default.aspx não tem ideia de quem será a classe concreta que ela vai utilizar para obter uma saudação, apenas conhece o contrato que ela deve seguir.

Bom, o código de exemplo acima não funciona. Não somos nós que criamos as instâncias das páginas web, é o próprio runtime do ASP.NET que faz isso. Felizmente o ASP.NET permite definirmos novas classes de factory (fábrica) para criar as instâncias das páginas. Assim, o runtime utilizará a classe factory que nós definirmos. Isso é feito com dois passos:
  1. Criar uma classe factory que siga o contrato da interface IHttpHandlerFactory.
  2. Registrar o uso dessa interface no web.config.
Abaixo está nossa implementação da interface IHttpHandlerFactory. Note que ao invés de implementar cada um dos três métodos que ela possui, estou fazendo herança da classe PageHandlerFactory. Bom, eu não estou afim de ter que codificar tudo que é necessário para instanciar uma página ASP.NET, então vou apenas estender o método já existente que faz isso, apenas adicionando a dependência da página.

public class DIPageFactory : PageHandlerFactory
{
    public override IHttpHandler GetHandler(HttpContext context, string requestType, string virtualPath, string path)
    {
        var pagina = (Page)base.GetHandler(context, requestType, virtualPath, path);
        if (virtualPath == "/default.aspx")
            (pagina as _Default).Saudacao = new SaudacaoPTBr();
        return pagina;
    }
}

Agora vai como fica no web.config.

<httphandlers>
      <add path="*.aspx" type="ExemploDIWebForms.Codigo.DIPageFactory, ExemploDIWebForms" verb="*">
    </add>
</httphandlers>

Pronto, assim toda vez que qualquer página ASP.NET for criada, essa criação passará por este método. E desta maneira, podemos incluir a criação das dependências necessárias que a página possui. É isso, gostaram? Não? Acharam feio o if que coloquei para adicionar a dependência quando a página for a Default.aspx? É..., realmente não é legal deixar as coisas cravadas assim. Vamos melhorar o código, e para isso precisamos de alguém que faça a atribuição das dependências de forma correta, para cada página que formos usar.

Quem fará esse trabalho de ligar a interface (contrato) com a classe concreta (implementação) será um contêiner de Injeção de Dependência. E para esse trabalho vamos escolher, dentre vários frameworks disponíveis, o Ninject. Mas poderia ser qualquer outro (Spring.NET, Unity, Castle Windsor), o que importa aqui é apresentar o conceito.

A ideia é a seguinte: vamos precisar que, quando uma classe tiver uma dependência que seja ISaudacao, ela seja automaticamente preenchida com um objeto SaudacaoPTBr. Para isso, precisamos criar uma forma de mapear o contrato (interface) para a classe concreta (implementação). Vamos usar um módulo do Ninject (uma classe que herda de NinjectModule). Veja que estamos fazendo uma sobrecarga do método Load, especificando que quando encontrarmos uma interface ISaudacao, ela deve ser preenchida com SaudacaoPTBr.

public class ModuloInjecoes : NinjectModule
{
    public override void Load()
    {
        Bind<ISaudacao>().To<SaudacaoPTBr>();
    }
}

Agora podemos deixar genérico a nossa classe factory! Basta usar o contêiner do Ninject para que ele resolva os contratos para nós.

public class DIPageFactory : PageHandlerFactory
{
    public override IHttpHandler GetHandler(HttpContext context, string requestType, string virtualPath, string path)
    {
        var handler = (Page)base.GetHandler(context, requestType, virtualPath, path);
        var kernel = new StandardKernel(new ModuloInjecoes());
        kernel.Inject(handler);
        handler.InitComplete += (sender, e) =>
        {
            foreach (Control controle in handler.Controls)
                InicializarUserControls(kernel, controle);
        };
        return handler;
    }

    private void InicializarUserControls(IKernel kernel, Control controle)
    {
        if (controle is UserControl)
            kernel.Inject(controle);
        foreach (Control filhos in controle.Controls)
            InicializarUserControls(kernel, filhos);
    }
}

O contêiner é a classe StandardKernel, do Ninject. Quando nós a instanciamos, passamos para ela uma instância de ModuloInjecoes, aquela classe que criamos e que explica para o framework de IoC qual classe deve-se criar para cada interface. Depois disso, chamamos o método Inject do contêiner. Ele irá então procurar por todas as dependências que foram decoradas com o atributo Inject (se alguém estava se perguntando porque tinha esse atributo no segundo trecho de código, aí está a razão), e inicializá-las corretamente.

Um detalhe importante. O Ninject sozinho não inicializa as dependências dos controles filhos da página ASP.NET. Isso quer dizer que se tivermos um User Control dentro da nossa página e que também possua uma dependência, ele não será inicializado. Resolvemos isso criando um método que percorre todos os controles filhos da página, recursivamente, para que quando se encontrar um User Control, que seja chamado o método Inject do Ninject para que suas dependências sejam preenchidas. No caso de uma Master Page, ela também é considerada como um User Control, então nosso método InicializarUserControls irá funcionar para elas também. E esse método é então chamado no evento InitComplete da página (só tomem cuidado que deve-se colocar, dentro do User Control, código que execute apenas a partir do seu evento Load, se for colocado no Init as dependências ainda não terão sido inicializadas, então você tomará um erro de "Object not set to an instance of an object").

Indo além


Eu não fui a primeira pessoa a precisar usar injeção de dependência. Procurando um pouco mais pela internet, encontrei uma extensão do Ninject exatamente para trabalhar com WebForms: https://github.com/ninject/ninject.web.

Ela segue o mesmo conceito de interceptar os eventos que ocorrem no pipe line de construção das páginas pelo ASP.NET. Entretanto, eles usam módulos http que são configurados ao invés do factory de páginas (veja a classe NinjectHttpModule). Outra diferença é que essa extensão se baseia em classes base para os user controls, páginas e master pages. De qualquer maneira, é uma implementação já pronta e é interessante considerá-la ao invés de escrever um novo (ou seja, estou falando que o meu é "reinventar a roda", e na verdade é mesmo!).

Outra consideração que eu gostaria de fazer ao meu exemplo é sobre a forma que deixei para que as dependências sejam injetadas. Usei propriedades. Existem críticas em relação a essa abordagem, pois com propriedades o desenvolvedor não sabe quais dependências ele deve injetar. Isso difere de uma injeção via construtor, onde você obrigatoriamente tem que passar as dependências senão não será possível criar o objeto. Outra justificativa de ter usado injeção de dependência por propriedade é que nós não temos controle do código que cria os user controls, apenas temos acesso a eles depois de criados.

Abaixo alguns links de discussões a respeito:
[]'s

quarta-feira, 13 de junho de 2012

Paginação de registros no SQL Server

Hoje vou comentar uma solução para paginar dados no SQL Server. A paginação é útil quando precisamos retornar uma grande quantidade de informações pouco à pouco.

Vamos supor que tenhamos que trafegar 100.000 registros através de uma chamada de web service. Dependendo da rede e do tamanho dos dados, podemos ter problemas se tentarmos passar todos esses dados de uma vez só (problemas do tipo cair a rede no meio da transferência e perdermos todo o trabalho). Ao invés disso, podemos tentar passar 5.000 em 20 vezes, ou seja, em 20 páginas. Isso também é válido quando precisamos apresentar muita informações na tela do usuário, e ao invés de trazer todos os registros, trazemos eles pouco à pouco de forma que permita a análise da pessoa.


Nas situações acima, poderíamos carregar todos os registros em memória e nós mesmos fazermos a paginação, só que isso é ruim visto que vamos alocar todas as informações na memória sendo que só iremos utilizar uma pequena fração dela. Para resolver isso, podemos aplicar uma paginação no nível onde as informações estão armazenadas: o banco de dados.

Isso pode ser feito com uso da função ROW_NUMBER, disponível desde a versão 2005 do SQL Server. Vamos imaginar uma tabela chamada cidades, com apenas uma coluna chamada nome, como na imagem abaixo.


A paginação é feita usando o exemplo de código abaixo, onde estamos especificando o tamanho da página (quantidade de registros que serão retornados) e o número da página.

DECLARE @TamanhoPagina INT;
DECLARE @NumeroPagina INT;

SET @TamanhoPagina = 10;
SET @NumeroPagina = 25;

WITH Paginado AS (
 SELECT ROW_NUMBER() OVER(ORDER BY nome) AS linha, nome
  FROM cidades WITH (NOLOCK)
 )
SELECT TOP (@TamanhoPagina) linha, nome
FROM Paginado p
WHERE linha > @TamanhoPagina * (@NumeroPagina - 1);

Veja que estamos criando um conjunto de dados temporário chamado Paginado, a partir do SELECT usando a função ROW_NUMBER() de forma que ela retorne o número da linha de cada registro (é importante notar também que essa função exige o uso da instrução OVER, onde especificamos a ordenação a ser usada).

Depois disso, fazemos um simples SELECT sobre esse conjunto de dados temporário, tomando cuidado para retornar apenas a quantidade de acordo com o tamanho da página (instrução TOP). E para finalizar, no WHERE deste último SELECT especificamos qual página será retornada. Por exemplo, supondo que o tamanho da página é 10, se quisermos a página 1 (registros de 1 até 10), o resultado da expressão @TamanhoPagina * (@NumeroPagina - 1) retornará 0, então iremos trazer todos os registros de índice maior que zero (lembre que estamos limitando a quantidade através do TOP). Se quisermos a página 2 (registros de 11 até 20), então a expressão @TamanhoPagina * (@NumeroPagina - 1) retornará 10, e iremos trazer os registros de índice maior que 10, e assim por diante.


Com isso, implementamos uma paginação no nível do banco de dados, sem necessidade de processá-la em memória ou mesmo trazer todos os registros de uma vez ao usuário.

[]'s

domingo, 13 de maio de 2012

"Sacaneando" a OOP - chamando métodos não públicos em .NET

Olá, pessoal.

Hoje vou demonstrar aqui como burlar as regras da Orientação a Objetos em relação ao escopo, ou seja, conseguir chamar um método privado fora da sua classe, por exemplo.

A ideia desse post veio de uma necessidade que tive em acessar o conteúdo do viewstate de uma página ASP.NET a partir de um user control. ViewState é um artifício criado para o ASP.NET Webforms para se permitir simular a manutenção de estado entre as várias requisições à páginas web (visto que tradicionalmente a abordagem de trabalho com web é stateless). Vamos dar um exemplo. Suponha que eu tenha um grid em uma página ASP.NET, e então eu queira definir uma ordenação para uma coluna qualquer desse grid. Logicamente, na próxima vez que eu quiser fazer uma requisição para essa página, vou querer que essa ordenação que eu fiz continue. É nesse caso que entra o viewstate, pois será nele que essa informação de ordenação será armazenada.

Na prática, o viewstate é um campo do tipo hidden, no qual o ASP.NET armazena uma série de informações. Veja na imagem abaixo um exemplo de como o viewstate fica presente no HTML.


Como citamos no exemplo, o grid armazenou a sua ordenação dentro do viewstate. Apesar do viewstate ser único para uma página, dentro do ASP.NET cada controle tem um próprio objeto no qual ele armazena o seu estado (cada controle tem seu objeto ViewState). É no final do processo de renderização da página que o ASP.NET pega todos esses objetos de viewstate e consolida em um só, jogando o resultado no controle hidden __VIEWSTATE, mas enquanto isso não ocorre, cada viewstate de cada objeto é isolado um do outro. A propriedade ViewState (do tipo StateBag), presente no objeto System.Web.UI.Control, do qual classes como Page e UserControl herdam, tem seu escopo como protected.

É por essa característica que tive que recorrer a uma "alternativa técnica" (para não falar gambiarra) de forma a acessar o viewstate de uma página (objeto System.Web.UI.Page) a partir de um UserControl (objeto System.Web.UI.UserControl). Vejamos o código abaixo.

    public partial class WebUserControl1 : System.Web.UI.UserControl
    {
        protected void Button1_Click(object sender, EventArgs e)
        {
            //this.Page.ViewState["TESTE_PAGINA"] = TextBox1.Text;    // dá erro
            var viewstate = ObterVIewStatePagina();
            viewstate["TESTE_PAGINA"] = TextBox1.Text;
        }

        private StateBag ObterViewStatePagina()
        {
            PropertyInfo property = typeof(Page).GetProperty("ViewState",
                BindingFlags.NonPublic | BindingFlags.Instance);
            return (StateBag)property.GetValue(this.Page, new object[] { });
        }
    }

Como comentado no código de exemplo, se tentarmos acessar a propriedade ViewState de this.Page a partir do UserControl, teremos problemas. O código não compila, obviamente. Mas então entra em ação o método ObterViewStatePagina. Nele, em primeiro lugar obtemos via reflection a referência à propriedade de nome ViewState da página. Perceba que passamos para o método GetProperty, além do nome da propriedade, qual é o binding flags que queremos que esse método utilize como filtro para recuperar a referência via reflection. Passamos BindingFlags.NonPublic exatamente para que sejam retornar métodos de escopo private e protected. BindingFlags.Instance também é necessário pois a propriedade ViewState é da instância da classe (não é static).

Como resultado, temos essa referência, que podemos obter o valor ao chamar o método GetValue, passando como parâmetro exatamente o objeto Page do qual queremos obter o StateBag. A partir disso, podemos fazer qualquer operação em cima do viewstate da página.

No exemplo pegamos uma propriedade, mas é possível chamar métodos, alterar valores de atributos, etc.

Três observações:

  1. Essa técnica pode ser usada como alternativa para testar métodos não-públicos em testes de unidade sem o uso da classe PrivateObject.
  2. Isso tem que ser usado com bastante cuidado. Se um método ou propriedade não está público, é porque quem programou a classe não quis expor essa informação. Tenha em mente que em próximas versões dessa classe, o programador pode mudar a forma como ele escreve o código, a assinatura do método não-público pode mudar, o próprio método pode sumir ou passar a fazer algo que você não quer.
  3. E por último, voltando a falar de viewstate, tome cuidado com seu uso, pois quanto mais informação você armazena nele, mais pesada a página fica.

segunda-feira, 2 de abril de 2012

sábado, 31 de março de 2012

"Novidades" no Visual Studio 11 Beta - Microsoft Fakes

Olá, pessoal!

Hoje vou falar de uma das "novidades" que vieram na versão beta do Visual Studio 11: Microsoft Fakes. O Fakes é um framework de geração de objetos dublês para testes, ou seja, ele serve para criar classes que possam substituir outras para facilitar a execução de testes de caixa branca. Testes de unidade devem verificar apenas o comportamento da classe que estão testando, e não de várias classes. Por exemplo, se para testar uma classe de negócio precisamos da sua classe de dados, para retornar algumas informações, então isso não é um teste de unidade...

É mais fácil mostrar a utilidade do Fakes com um exemplo. Antes de tudo, você deve baixar a versão beta do Visual Studio 11 neste link http://www.microsoft.com/visualstudio/11/en-us.

Como exemplo, eu criei um projeto do tipo "Unit Test Project", para C#. Vou colocar o próprio código da classe de negócios que será testada neste projeto. Numa solução real, isso não poderia ser feito, mas como é apenas uma demonstração, eu tomei a liberdade de cometer essa atrocidade.

No projeto, eu criei uma classe chamada ClasseDeNegocio. Essa classe será responsável por excluir um arquivo do disco caso ele tenha sido criado há mais de 2 meses. Abaixo está o código dessa classe, com o método ExcluirArquivoAntigo.

using System;
using System.IO;

namespace ExemploMicrosoftFakes
{
    public class ClasseDeNegocio
    {
        public void ExcluriArquivoAntigo(string arquivo)
        {
            // verifica se o arquivo existe
            if (File.Exists(arquivo))
            {
                // verifica se é mais velho que 2 meses atrás (regra do sistema)
                if (File.GetCreationTime(arquivo) < DateTime.Now.AddMonths(-2))
                {
                    // sendo assim, exclui
                    File.Delete(arquivo);
                }
            }
        }
    }
}

Em resumo, este método verifica se um determinado arquivo existe e se ele é mais velho que dois meses. Se for, então ele é excluído. Veja um detalhe: ele utiliza vários métodos estáticos de classes do .NET Framework, como File e DateTime. Como poderíamos isolar este tipo de chamada, colocando um objeto dublê de testes? A resposta está no "novo" framework de testes da Microsoft, o Fakes. Veja os três testes de unidade abaixo, que testam as seguintes situações:
  1. Se o arquivo não existe, então ele não deve ser excluído.
  2. Se o arquivo existe e é mais velho que dois meses, então deve ser excluído.
  3. Se o arquivo existe mas não é mais velho que dois meses, então deve ser mantido.

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.QualityTools.Testing.Fakes; // para ShimsContext
using System.IO.Fakes;  // para o ShimFile
using System.Fakes; // para o ShimDateTime


namespace ExemploMicrosoftFakes
{
    [TestClass]
    public class TesteDeUnidade
    {
        [TestMethod]
        public void ClasseDeNegocio_ConsultarConteudoArquivo_NaoDeveExcluirArquivoInexistente()
        {
            using (ShimsContext.Create())
            {
                // Arrange
                bool excluido = false;
                ShimFile.ExistsString = (s) => false;
                ShimFile.DeleteString = (s) => excluido = true;
                var alvo = new ClasseDeNegocio();
                // Act
                alvo.ExcluriArquivoAntigo("xxx");
                // Assert
                Assert.AreEqual(false, excluido);
            }
        }

        [TestMethod]
        public void ClasseDeNegocio_ConsultarConteudoArquivo_DeveExcluirArquivoMaisAntigoQue2MesesDaDataAtual()
        {
            using (ShimsContext.Create())
            {
                // Arrange
                ShimDateTime.NowGet = () => DateTime.Parse("2011-04-01");
                bool excluido = false;
                ShimFile.ExistsString = (s) => true;
                ShimFile.GetCreationTimeString = (s) => DateTime.Parse("2011-01-01");
                ShimFile.DeleteString = (s) => excluido = true;
                var alvo = new ClasseDeNegocio();
                // Act
                alvo.ExcluriArquivoAntigo("xxx");
                // Assert
                Assert.AreEqual(true, excluido);
            }
        }

        [TestMethod]
        public void ClasseDeNegocio_ConsultarConteudoArquivo_NaoDeveExcluirComAte2MesesDaDataAtual()
        {
            using (ShimsContext.Create())
            {
                // Arrange
                ShimDateTime.NowGet = () => DateTime.Parse("2011-04-01");
                bool excluido = false;
                ShimFile.ExistsString = (s) => true;
                ShimFile.GetCreationTimeString = (s) => DateTime.Parse("2011-03-01");
                ShimFile.DeleteString = (s) => excluido = true;
                var alvo = new ClasseDeNegocio();
                // Act
                alvo.ExcluriArquivoAntigo("xxx");
                // Assert
                Assert.AreEqual(false, excluido);
            }
        }
    }
}

Vejam as classes ShimDateTime e ShimFile, elas são o segredo para fazer os testes funcionarem. Vamos olhar a linha ShimFile.ExistsString = (s) => false;. O que estamos fazendo aqui é substituir (isso mesmo, substituir) o método File.Exists para que ele tenha outro comportamento. No caso, o que queremos  é que ele simplesmente retorne false para que possamos testar o cenário onde o arquivo não existe, por isso estamos passando para ShimFile.ExistsString um delegate que retorne false.

Ainda comentando sobre o primeiro exemplo, como podemos garantir que nossa classe de negócio realmente funciona e quando o arquivo não existe ela realmente não faz a exclusão? Para isso, podemos interceptar também o método File.Delete, fazendo com que caso ele seja chamado, nós ajustamos o valor da varíável excluido para true. Ao final do teste, quando fazemos o "Assert", se excluido continuar com o valor false, quer dizer que o método File.Delete não foi chamado, confirmando que o cenário está ok. Se File.Delete foi chamado, então há algo de errado com nossa classe de negócios...

Ok, você entendeu que as classes Shim servem para interceptar as chamadas de métodos reais, mas de onde elas vêm? Bom, para que essas classes estejam disponíveis, é necessário criá-las. Mas calma, quem vai fazer esse trabalho é o Visual Studio. Veja como é simples: basta clicar com o botão direito sobre o componente do qual queremos interceptar os métodos de suas classes, e escolher a opção "Add Fakes Assembly". No nosso exemplo, nós adicionamos as classes Shim para todos as classes do namespace System do próprio .NET Framework (File e DateTime vão junto neste pacote...).

Após esse passo, veja que foi criada uma pasta chamada Fakes, contendo os arquivos mscorlib.fakes e System.fakes. São esses arquivos que são usados para a criação dos assemblies fakes. Note que com isso, duas novas referências aparecem no projeto: mscorlib.4.0.0.0.Fakes.dll e System.4.0.0.0.Fakes.dll.

Bom, agora só falta executar os métodos de testes para você acreditar no que eu estou dizendo. Para isso, basta ir na janela Unit Test Explorer do Visual Studio 11 beta, e clicar em "Run All". Outra opção para fazer isso é clicar com o botão direito sobre a classe de testes e clicar em "Run Unit Tests".


Com isso, vimos como é fácil criar testes de unidade no Visual Studio, isolando as nossas dependências e permitindo que os testes realmente validem apenas um pedaço do código. Mas ainda sobram algumas perguntas a serem respondidas...

Eu já não vi isso antes?


Sim, interceptação e substituição de métodos não são novidades no Visual Studio. O framework Moles (http://research.microsoft.com/en-us/projects/moles/ e http://galorebr.blogspot.com.br/2011/07/artigo-de-moles-net-magazine-87.html) já fazia isso! É praticamente a mesma coisa! Foi por isso que no título desse post eu coloquei a palavra novidades entre aspas...

Mas uma diferença que notei está relacionado a forma com que a interceptação é feita. Enquanto no Moles era necessário um processo exclusivo para rodar o código dos testes de unidade (Microsoft.Moles.VsHost.x86.exe), e dessa forma poder usar o profiler para interceptar as chamadas no .NET, no Fakes é utilizado o profiler do IntelliTrace para isso. Na minha opinião isso é uma boa melhoria, pois ficamos com apenas um profiler ao invés de dois. Além disso, até onde testei, a execução do Fakes não tem alguns problemas que existiam com o Moles: às vezes davam alguns erros na execução dos testes, e o processo Microsoft.Moles.VsHost.x86.exe não era finalizado, gerando problemas na hora de executar os testes novamente.

Bom, na própria página do Moles há a mensagem de que ele será descontinuado para a entrada do Microsoft Fakes. Mas aparentemente não há muitas dificuldades em converter o código que usava Moles para usar o Fakes. O seguinte link mostra com mais detalhes como utilizar o Shim do Fakes para outras situações, além dos Stubs (que também já estavam disponíveis no Moles): http://msdn.microsoft.com/en-us/library/hh549175(v=vs.110).aspx.

O código de exemplo que fiz para este post pode ser baixado em https://github.com/fabiogouw/Exemplos

[]'s

terça-feira, 27 de março de 2012

Annoying Manager

Olá, pessoal.

Há algum tempo eu vinha querendo criar alguma coisa e disponibilizar como código aberto.

Hoje finalmente fiz isso, e o fiz com uma idéia de aplicativo que eu tive lá por 2004, mas nunca cheguei a concluir: uma ferramenta para ajudar a controlar as atividades que vou fazendo durante o dia, algo que me forçasse a manter um histórico de tudo o que vou fazendo. Na época esse aplicativo iria se chamar "C.H.A.T.O", que era um acrônimo de "Controle de Horários, Atividades e Tarefas Operacionais" (sim, forcei para que pudesse ser um acrônimo com sentido, rs).

Hoje essa ferramenta se chama Annoying Manager (Gerente "Pentelho"), e está disponível no Codeplex, site de projetos abertos mantido pela Microsoft. O endereço é http://codeplex.com/annoyingmanager/.

A idéia é a seguinte: um aplicativo que executa em segundo plano e de tempos em tempos aparece para questionar o que você está fazendo no exato momento. Você dá uma descrição da atividade para ele, e então ele "dorme" por mais alguns minutos. Ao final, você tem uma listagem de tudo o que fez.

O programa está em versão beta. Utilizando-o no dia à dia, já identifiquei alguns problemas e melhorias que podem ser feitas, e pretendo implementá-las logo. Uma das coisas novas que tenho em mente é migrar a interface para WPF, e outra é buscar as atividades de um servidor TFS.

Convido quem quiser a participar desse projeto, afinal ele é open-source. O código-fonte está disponível no mesmo endereço que passei, bem como o download do executável e uma lista de discussões.

[]'s

sexta-feira, 23 de março de 2012

Relato de Caso: Uso de cache em aplicação ASP.NET

Olá, pessoal.

Hoje vou falar sobre cache, mas quero fazer algo diferente dos muitos materiais que se encontra na Internet. Não numa visão de codificação prática, mas sim conceitualmente, explicando o resultado que tive ao implementá-lo em uma aplicação ASP.NET que possuía sérios problemas de desempenho.

Primeiramente, qual é o conceito de cache? A idéia do cache é ter uma cópia de um conjunto de dados para acesso rápido. Dessa forma não oneramos o repositório original, cujo acesso é mais lento que o cache, ao fazer buscas.

A seguinte imagem mostra como um algoritmo de cache comumente é feito. Alguém faz uma requisição ao cache, buscando determinada informação. Caso essa informação não exista no cache, ele vai ao repositório original para carregá-la e o insere no cache. A partir disso, qualquer busca por esta informação no cache vai trazer o dado armazenado, ao invés de efetuar a pesquisa no repositório “lento”.



A vantagem do cache está em melhorar o desempenho de pesquisas. O principal desafio dessa abordagem é controlar a validade do cache: pode ser que logo após a informação ser armazenada no cache, ela ser atualizada no repositório original. Nesse caso, que for acessar o cache irá receber uma versão antiga. Isso pode ser problema para algumas situações, e existem diversas formas de contornar este problema. Uma delas que minimiza esse efeito indesejado é estabelecer um tempo máximo de vida do cache, ou seja, depois de determinado tempo, um objeto armazenado no cache é descartado e uma nova consulta no repositório original quando o objeto for novamente requisitado. Essa é a abordagem que utilizei com o cache do ASP.NET, e que serviu bem para a situação que trabalhei.

O cenário que tínhamos era o seguinte. Uma aplicação era disponibilizada na Internet para acesso de vários estabelecimentos comerciais e seus procuradores para receber avisos e notificações. Pelo grande volume atual de acessos, esse sistema não estava mais escalando e ficava indisponível constantemente. Quando cheguei, a primeira coisa que fiz foi tirar um dump de memória, e com isso constatei que havia muitos objetos do tipo Estabelecimento carregados no processo do IIS. Depois de uma análise de código, entendemos que essa quantidade excessiva de objetos era porque se fazia muitas consultas ao banco de dados buscando este tipo de objeto. As informações armazenadas neles, por exemplo CNPJ, razão social e data de fundação, não eram dados que recebiam muitas atualizações (alias, é bem difícil que recebam), e por isso optamos por colocar uma rotina de cache para esses objetos. Utilizamos o CNPJ do estabelecimento como índice de busca. Outro detalhe importante é que essas informações de estabelecimentos eram para somente leitura. Com isso, problemas de concorrência no uso desses objetos praticamente não existem.

Como o sistema já estava dividido numa arquitetura que, apesar de anêmica, possuía a divisão clara de componentes na qual as classes de acesso a dados já estavam bem separadas, decidimos colocar o cache neste ponto. Usamos sim o cache do ASP.NET dentro da camada de acesso a dados, no entanto isolamos essa chamada à infra-estrutura do ASP.NET de forma que se esse sistema mude de plataforma, possamos acoplar outra solução de cache (como o AppFabric ou NCache, por exemplo) de forma simples.

Feito isso, testamos e implantamos em produção. Felizmente deu tudo certo e a aplicação voltou a atender os usuários!

Fizemos algumas medições para ver como o uso do cache melhorou o desempenho. Podemos ver isso no gráfico abaixo.


Este gráfico é o Performance Monitor do Windows. Nós pedimos para ele acompanhar alguns contadores de desempenho referentes ao cache do ASP.NET: Cache API Entries, Cache API Hits e Cache API Misses (os três primeiros contadores que aparecem). Para poder explicar melhor o que cada um deles representa, vamos recorrer ao desenho que comentamos agora há pouco. Veja a nova versão abaixo.



  • Cache API Misses representa a quantidade de vezes que buscamos um determinado objeto no cache e ele não se encontra. Neste caso, temos que ir no repositório "lento".
  • Cache API Entries representa a quantidade de objetos que foram recuperados do repositório original e que estão atualmente armazenados no cache.
  • Cache API Hits representa quantas vezes fomos até o cache e achamos a informação que queríamos já pronta para nosso uso. É neste momento que vemos o ganho de desempenho.
Com essas informações na cabeça, vamos analisar o gráfico. Vejam que a linha azul (Cache API Hits) é a que mais cresce, enquanto as outras ficam relativamente constantes. Isso indica que o cache está sendo bem utilizado, pois temos uma mesma informação (linha vermelha, o Cache API Entries) que está sendo consumida inúmeras vezes.

Alguns podem perguntar da relação entre a linha vermelha e a linha rosa (Cache API Misses), pois na teoria toda vez que não encontramos uma informação no cache (miss), nós a carregados do repositório. Neste caso, os valores de ambos os contadores deveriam ser iguais (um para um). O que acontece nesse caso é que objetos do cache que já tiveram seu tempo de vida expirado são removidos, fazendo com que seja necessária uma nova busca para uma informação que já foi pesquisada há tempos.

O que eu gostaria de falar para vocês é que nesta situação que tivemos, o uso de cache foi imprescindível para um bom desempenho do sistema. Tenham sempre em mente que é muito provável que seja vantajoso utilizar uma estratégia de cache quando temos a mesma informação sendo consumida várias vezes. É claro que cada caso requer uma análise dos problemas colaterais que podemos ter, como maior uso de memória e consumo de dados desatualizados, mas eu acredito que o custo, ao menos da análise, valha a pena.

Aqui seguem alguns links com mais informações sobre cache:
Até a próxima!

segunda-feira, 19 de março de 2012

Criando e testando objetos com construtores privados

Imaginem a seguinte situação. Temos uma classe qualquer. Essa classe possui uma dependência. Além disso, vamos supor que a criação desse objeto seja uma coisa complexa, ou seja, não podemos simplesmente deixar que essa classe seja instanciada por um new.

// MeuObjeto.cs
    public class MeuObjeto
    {
        public IMinhaDependencia Dependencia { get; set; }

        internal MeuObjeto()
        {
            // internal para garantir que apenas
            // a factory possa criar este objeto
        }

        public int RetornarCalculo(int a)
        {
            return Dependencia.Calcular() + a;
        }
    }

    // IMinhaDependencia.cs
    public interface IMinhaDependencia
    {
        int Calcular();
    }

Temos então uma classe factory para criar uma instância desse objeto. Problema resolvido, correto? Se eu precisar de uma instância dessa classe, simplesmente chamo sua factory.

// MeuObjetoFactory.cs
    public class MeuObjetoFactory
    {
        public MeuObjeto CriarObjeto()
        {
            return new MeuObjeto()
            {
                Dependencia = new MinhaDependencia()
            };
        }
    }

    // MinhaDependencia .cs
    // faz de conta que é um objeto muito custoso para criar
    public class MinhaDependencia : IMinhaDependencia
    {
        public int Calcular()
        {
            return 1;
        }
    }

Do ponto de vista funcional, funciona que é uma beleza. Mas como podemos testar unitariamente a classe MeuObjeto? Neste caso, teríamos duas opções:

  1. Usamos a factory no nosso teste de unidade para criar o objeto. O problema nesse caso é que nosso teste de unidade passaria a testar a classe MeuObjeto, a factory MeuObjetoFactory além de obrigatoriamente criar uma instância da dependência MinhaDependencia, o que implica que a testaríamos a sua criação também. Isso deixou de ser um teste de unidade.
  2. Alteramos o escopo de MeuObjeto e deixamos seu construtor público. Com isso, podemos instanciá-lo diretamente, sem usar nada do factory nem criar sua dependência, e passar um objeto dublê de teste (mock) para simular o comportamento da dependência. Só que com isso, qualquer desavisado poderia instanciar diretamente MeuObjeto, e era exatamente isso que queríamos evitar quando fizemos seu construtor com escopo internal (visível apenas dentro do assembly que ele reside) e criamos a factory.
E agora, qual opção seguir?

Bom, temos uma terceira opção, pegando o que há de bom em cada cenário acima, sem suas desvantagens!

Vamos usar uma classe chamada PrivateObject, disponível no namespace Microsoft.VisualStudio.TestTools.UnitTesting. Com ela, podemos criar e chamar métodos de qualquer classe, mesmo que esses métodos sejam privados. Olhem só a classe de testes abaixo.

// UnitTest1.cs
    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        [Ignore]
        public void TestMethod1()
        {
            //var obj = new MeuObjeto(); -- isso dá erro de compilação
        }

        [TestMethod]
        public void TestMethod2()
        {
            // Arrange
            var mock = new Mock<IMinhaDependencia>();
            mock.Setup(m => m.Calcular()).Returns(1);
            var po = new PrivateObject(typeof(MeuObjeto));
            var obj = (MeuObjeto)po.Target;
            obj.Dependencia = mock.Object;
            // Act
            var resultado = obj.RetornarCalculo(2);
            // Assert
            Assert.AreEqual(3, resultado);
        }
    }

O método TestMethod1 é só para mostrar que não funciona instanciar direto um objeto com construtor não visível (privado ou como no nosso caso, internal). Vamos olhar o método TestMethod2, que é mais interessante para nós. A primeira coisa que ele está fazendo é criar um objeto dublê de teste usando o framework Moq. Não é escopo deste post explicar mocks ou o Moq, então aqui está um link para mais informações http://code.google.com/p/moq/wiki/QuickStart. Mas explicando de forma bem rápida, o que estamos fazendo é criar um objeto que possua os mesmos métodos da interface IMinhaDependencia, e ajustando o comportamento que ele terá quando formos chamar o seu método Calcular (no nosso exemplo, estamos cravando que seu retorno será o valor 1). Com isso, "enganamos" a instância de MeuObjeto, fazendo com que ele acredite que esteja trabalhando com uma implementação concreta.

Bom, voltando ao assunto original, chegamos agora na parte onde efetivamente criamos o objeto de construtor não-visível. Simplesmente criamos uma instância de PrivateObject passando o tipo que queremos que ele crie. De forma "mágica", ele nos cria esse objeto (internamente ele deve usar .NET Reflection para criar a instância, mas como eu não tenho certeza disso, considero que é por mágica). Assim, conseguimos pegar a sua referência acessando a propriedade Target de PrivateObject. Com isso, podemos continuar nosso teste de unidade, passando o mock que foi gerado e testando a sua funcionalidade dentro do teste de caixa-branca.

Por hoje é só. Em breve eu disponibilizo os fontes desse exemplo em algum repositório público (ainda estou decidindo se eu uso o BitBucket ou o GitHub).

[]'s

ATUALIZAÇÃO: Disponibilizei o exemplo em https://github.com/fabiogouw/Exemplos. Decidi deixar minha conta no Github para coisas públicas, e o Bitbucket para coisas pessoais.

sexta-feira, 9 de março de 2012

ORA-01843: not a valid month

Estou aqui de novo para comentar problemas relacionados ao desenvolvimento com Oracle, e como eles foram resolvidos.

Na equipe que trabalho entrou uma pessoa nova, e precisamos instalar o cliente do Oracle para que ele pudesse trabalhar. Após feita a instalação do Oracle Instant Client, quando ele começou a executar o sistema na sua máquina, o seguinte erro apareceu: ORA-01843: not a valid month. Detalhe que quando a mesma rotina era executada na minha máquina, eu não tinha esse problema.

Após algumas pesquisas, identificamos a causa. No banco de dados que estamos utilizando, a configuração de formato de data está parametrizado para dia/mês/ano:


select * from nls_database_parameters;

NLS_LANGUAGE                          AMERICAN
NLS_TERRITORY                          AMERICA
NLS_CURRENCY                          $
NLS_ISO_CURRENCY                  AMERICA
NLS_NUMERIC_CHARACTERS  .,
NLS_CHARACTERSET                  WE8ISO8859P1
NLS_CALENDAR                         GREGORIAN
NLS_DATE_FORMAT                 DD-MON-RR
NLS_DATE_LANGUAGE                  AMERICAN
NLS_SORT                                         BINARY
NLS_TIME_FORMAT                         HH.MI.SSXFF AM
NLS_TIMESTAMP_FORMAT         DD-MON-RR HH.MI.SSXFF AM
NLS_TIME_TZ_FORMAT                 HH.MI.SSXFF AM TZR
NLS_TIMESTAMP_TZ_FORMAT DD-MON-RR HH.MI.SSXFF AM TZR
NLS_DUAL_CURRENCY                $
NLS_COMP                                        BINARY
NLS_LENGTH_SEMANTICS        BYTE
NLS_NCHAR_CONV_EXCP        FALSE
NLS_NCHAR_CHARACTERSET    AL16UTF16
NLS_RDBMS_VERSION               11.2.0.2.0


No entanto, na máquina do desenvolvedor não havia configuração de como o cliente do Oracle trataria as informações. Desse modo, ele estava considerando o retorno de formato de data como no padrão mês/dia/ano. Quando a rotina era executada, ela retornava uma data, digamos 16/02/11. No formado brasileiro, teríamos o 16 como dia, mas lendo isso no formado padrão, 16 seria o mês, o que não existe. Por isso ocorria o erro de not a valid month.

A correção foi incluir uma parametrização no registro do Windows informando que era para ser considerado o formato brasileiro. Ou seja, incluir a propriedade NLS_LANG com valor BRAZILIAN PORTUGUESE_BRAZIL.WE8MSWIN1252 dentro da chave HKEY_LOCAL_MACHINE\SOFTWARE\ORACLE\KEY_OraClient11g_home1 (este caminho pode variar, dependendo de como foi feita a instalação do cliente).


Mas muito importante, tivemos que reiniciar a máquina para essa parametrização surtir efeito. Depois disso, tudo funcionou ok.

Obs. Eu não sei se essa é a forma correta de corrigir o problema, mas foi o que funcionou. Se alguém conhecer um jeito mais elegante, por favor, comente aqui.

ATUALIZAÇÃO: 23/05/2013

Com uma máquina nova, novamente tive que recorrer à Internet para resolver este problema. Infelizmente, não foi possível utilizar esta mesma solução, pois na estação que estou usando o Oracle não está instalado com o instalador, mas simplesmente copiando os binários e ajustando o caminho nas variáveis de ambiente do Windows (Path). Por isso, não estava disponível a chave no registry que comentei acima.

A solução? Criar uma nova variável de ambiente, chamada NLS_LANG, onde coloquei novamente o valor BRAZILIAN PORTUGUESE_BRAZIL.WE8MSWIN1252. Reiniciei e tudo funcionou.

[]'s

quarta-feira, 7 de março de 2012

TransactionScope e Invalid operation. The connection is closed no Oracle

Tudo na vida acontece por uma razão, e quando a gente não entende a razão considera que é magia.

Digo isso por um problema que estava tendo em uma aplicação ASP.NET que usa banco de dados Oracle. Em determinada funcionalidade, um processamento com gravação, ocorria um problema que impedia a gravação dos registros. O erro era:

System.InvalidOperationException: Invalid operation. The connection is closed.
   at System.Data.OracleClient.OracleConnection.GetOpenInternalConnection()
   at System.Data.OracleClient.OracleConnection.get_ErrorHandle()
   at System.Data.OracleClient.OracleDataReader.ReadInternal()
   at System.Data.OracleClient.OracleDataReader.Read()
   at Microsoft.Practices.EnterpriseLibrary.Data.Oracle.OracleDataReaderWrapper.Read()

Simplesmente eu não entendia a causa desse erro ( neste momento chamado de "magia"), pois o código que rodava essa consulta (sim, consulta, pois você pode ver que na stacktrace estamos usando o DataReader do OracleClient) funcionava de forma isolada sem problemas. Essa conclusão eu cheguei após gerar um windows service e colocar para ficar rodando este método a cada 30 segundos, já que minha primeira desconfiança era algum problema esporádico de comunicação com o servidor Oracle. Bom, esse windows service ficou rodando por horas, sem apontar nenhum problema.

No final do dia, uma coisa me chamou a atenção: processamentos pequenos funcionavam sem problema, os que gravavam muitos dados é que geravam o erro. E mais, todo o código que era executado estava envolvido por um TransactionScope... Na altura do campeonato, resolvi testar tirando a transação, ou seja, passando no seu construtor a opção Suppress (http://msdn.microsoft.com/en-us/library/ms172152(v=vs.90).aspx):

using(var trans = new TransactionScope(TransactionScopeOption.Suppress)) 
{
    // ...
} 

Dito e feito. Após essa alteração, o código funcionou.

Mas não deixei a rotina assim. Para não ficar com ela de forma que pudesse dar alguma inconsistência na gravação do banco, eu mantive a transação ativa só que coloquei um tempo de timeout maior (http://msdn.microsoft.com/en-us/library/9wykw3s2.aspx). Também funcionou e resolveu o problema.

Em resumo, o problema do Invalid Operation dava quando o tempo de transação padrão, que é 60 segundos, expirava no meio do processamento. Isso impactava também a parte das leituras que eram feitas na base de dados. O problema que eu classifiquei como "magia" poderia ser traduzido como uma descrição de erro não clara. Se logo no começo eu tivesse tomado um erro cuja descrição falasse algo de transação, ficaria mais claro para ir direto na raiz do problema. Mas fica a dica de que tudo tem explicação, só depende de quão fundo você pode e quer ir.

Em tempo, essa rotina que estava com problemas poderia ser melhor escrita. Ou seja, antes de iniciar a transação de gravação, ler e processar tudo o que fosse necessário, deixando apenas para gravar os dados no banco em um último momento e com transação somente nesta parte. Assim que possível vamos fazer isso.

[]'s

quinta-feira, 16 de fevereiro de 2012

Mau uso do TryParse...

Abaixo está uma pérola onde vemos o uso incorreto do método TryParse. Temos uma string, lida de um arquivo, que contém informação de data, e é necessário colocá-la em um objeto do tipo DateTime. Veja como foi feito abaixo.

public DateTime ObterDataControle()
{
    DateTime dt = DateTime.MinValue;

    if (File.Exists(_caminhoArquivo))
    {
        StreamReader sr = new StreamReader(_caminhoArquivo);
        try
        {
            string dateString = sr.ReadLine();

            if (DateTime.TryParse(dateString, out dt))
                dt = Convert.ToDateTime(dateString);
        }
        finally
        {
            sr.Close();
        }
    }
    return dt;
}

O método TryParse retorna um bool, que indica se foi possível converter a string para o determinado tipo. Só que quando é possível efetuar a conversão, este método já coloca o valor no parâmetro que ele recebe de output. Não é necessário fazer (como foi feito no código) uma nova conversão do valor. A linha dt = Convert.ToDateTime(dateString) é totalmente desnecessária.

[]'s

segunda-feira, 9 de janeiro de 2012

Impressões da 70-512

No final do ano passado (alias, feliz 2012 para todos!) eu fiz a prova de certificação da Microsoft 70-512, de administração do TFS. Felizmente ocorreu tudo ok, e eu passei! E agora vou colocar neste espaço aqui alguns comentários e impressões que eu tive dela, para servir de dica para que irá fazê-la em breve.

Bom, em primeiro lugar, eu vou passar como eu me preparei para a prova:

  1. Antes mesmo de pensar em fazer essa prova, eu tive oportunidade de assistir um treinamento de ALM com meu amigo Victor Cavalcante. Boa parte do conteúdo deste treinamento caiu na prova. Se tiverem oportunidade, procurem algum treinamento com pessoal capacitado como ele.
  2. Já quando eu estava com a idéia de fazer a prova (meta da empresa), eu encontrei o livro Professional Team Foundation Server 2010. É um bom livro, apesar que eu achei que algumas partes deixaram a desejar, como a parte de Lab Management e configuração de servidores Proxy do TFS. Mas a visão geral da parte de instalação e configuração e uso de branchs compensa. No final, o livro é um bom material para estudo dessa prova.
  3. Encontrei também alguns links com material online para estudo, como este aqui http://dfaoliveira.wordpress.com/2010/10/08/exame-70-512/ do Daniel Oliveira. Tem bastante links interessantes.
  4. Baixei a máquina virtual que a Microsoft disponibiliza para testes, pois com ela tive um ambiente já pronto para brincar um pouco: http://www.microsoft.com/download/en/details.aspx?id=22267.

Basicamente a estratégia de estudo que eu segui foi essa. Agora vou comentar um pouco da prova.

A 70-512 é uma prova bem conceitual, você não precisa saber detalhes da administração para fazê-la. Por exemplo, lembro de uma questão onde era dado um cenário onde se devia tirar um team project de uma team project collection e passar para outro. A solução neste caso seria fazer um split (http://msdn.microsoft.com/en-us/library/dd936158.aspx), e bastava saber esse conceito para ter a resposta. Não precisava saber dos detalhes que tem nesse último link que passei, apenas uma visão geral.

Outra coisa que pega bastante a parte conceitual da coisa são as questões que envolvem instalação e configuração. São várias onde é dado um cenário (a pessoa instalando um TFS em várias máquinas, fazendo upgrade, etc) e é questionado quais opções de configuração escolher. Isso foi um assunto que eu bloguei alguns dias antes da prova (http://www.galorebr.blogspot.com/2011/12/resumo-tipos-de-configuracao-do-tfs.html). Pré-requisitos de servidores, como o uso do WSS 3.0 para integração mínima com Sharepoint, também são cobrados.

Caiu uma questão de restauração de desastres relacioada ao Reporting Services, mais especificamente sobre a necessidade de fazermos um backup da symmetric key. Mas em geral as questões ficam com foco mesmo no TFS, não na configuração da infra necessária.

Outro tipo de questão que cai bastante é relacionado ao uso de branch e merge. Geralmente a questão vai envolver a escolha da alternativa que mostra como disponibilizar certas alterações de código feita em uma branch em uma outra branch, sem o uso de um baseless merge (http://msdn.microsoft.com/en-us/library/bb668976.aspx). Basicamente escolha a questão que explique um caminho de “merges” desde a branch de origem até a de destino.

Sugiro também dar uma “decorada” nas opções disponíveis quando enfileiramos um build, por exemplo para ativar a execução de testes, impacto dos testes ou Code Analysis. Caem também alguns comandos (lembro de uma questão relacionada ao “tf /lock”). Ah, também foram cobrados conceitos de shelveset e cloak, sendo que a questão dá um cenário por exemplo desenvolvedor querendo que alterações ainda não finalizadas ficassem no backup do TFS ou evitar que arquivos desnecessários sejam baixados na máquina durante o processo de get latest version.

Por enquanto ficam essas dicas, mas se eu lembrar de mais coisas eu atualizo este post.

Boa sorte para todos que forem fazer essa prova.

[]’s