Páginas

domingo, 25 de dezembro de 2011

Resumo: tipos de configuração do TFS 2010

O TFS possui os processos de instalação e configuração bem segregados. No primeiro processo, é possível escolher o que será instalado na máquina. No segundo, podemos escolher quais componentes habilitar, quais instâncias do SQL utilizar, etc. Hoje vamos falar de configuração, e abaixo estão os tipos disponíveis para configuração do TFS.

  • Basic – Permite configurar o TFS para usar o controle de versões, work-items e a parte de testes e lab management. No entanto, não permite a integração com Sharepoint e Reporting Services (cenário, por exemplo, quando instalamos o TFS num sistema operacional cliente, como o Windows 7).
  • Standard – É a configuração que deve ser escolhida ao se instalar o TFS em uma única máquina, mas funciona apenas com Windows Sharepoint Services 3.0. Não permite a escolha de um SQL Server remoto.
  • Advanced – Agora sim, permite usar SQL Server, Sharepoint e Reporting Services remotos. É nela que temos a maior flexibilidade de configuração.
  • Application-Tier Only – É usada na configuração de ambientes com alta disponibilidade (adicionando servidores em um Network Load Balance, por exemplo). Também pode ser usada em cenários em que se move a camada de aplicação do TFS para outra máquina ou em situações de recuperação de desastres.
  • Upgrade – Como o nome diz, deve ser utilizado em situações de upgrade do TFS 2005 ou 2008.
  • Configure Team Foundation Server Proxy – Para quando instalamos o TFS Proxy Server.
  • Configure Team Foundation Build Service – Para quando a máquina onde instalamos o TFS servirá como Build Agent ou Controller.
  • Configure Extensions for SharePoint Products – Configura extensões no Sharepoint para que o TFS possa utilizar (em situações de Sharepoint remoto ou web farm).

O quadro abaixo dá um resumo por cima das 4 primeiras configurações, mostrando suas principais diferenças.

 

  Basic Standard Advance Application-Tier only
Instâncias Multiple-server Single-server Multiple-server Multiple-server
Reporting Não Sim Sim Sim
Sharepoint Integration Não Sim Sim Sim
SQL em outra máquina Sim Não Sim Sim

Se pensarmos num ambiente corporativo com muito uso do TFS (servidores segregados, ou seja, dados, aplicação, Sharepoint e Reporting Services tudo sepadados), basicamente iremos utilizar a configuração Advanced para configurar o primeiro servidor de aplicação (considerando escolher e configurar o SQL Server, Sharepoint, etc.) e depois a configuração Application-Tier only, para configurar os diversos nós do load balance.

Por hoje é só.

[]’s

quinta-feira, 15 de dezembro de 2011

"The requested resource is in use" e o anti-vírus

Recentemente desenvolvemos uma aplicação que efetua o processamento de alguns arquivos enviados pela Receita Federal. Os arquivos eram lidos linha à linha e a cada conjunto de dados efetuava-se uma validação/processamento, e o resultado era colocado em banco de dados. Estes arquivos são de tamanho variável, chegando até 2 GB.

Um problema que foi relatado em ambiente de produção (cluster) é que no processamento de arquivos grandes, de vez em quando aparecia o erro "The requested resource is in use", no meio do processamento do arquivo.

Procurando na internet sobre este problema, encontrei uma thread em um fórum de SQL Server onde a pessoa tinha o mesmo problema (http://www.sqlservercentral.com/Forums/Topic625264-265-1.aspx), só que com os arquivos MDF do próprio SQL Server. Uma das sugestões dadas era verificar se o anti-vírus não estava mexendo com esses arquivos.

Dito e feito, esse era o problema para nós também. Após colocar os arquivos que eram processados na lista de exclusão do anti-vírus, o problema não voltou a ocorrer. Existe um KB da Microsoft que sugere alguns cuidados ao utilizar anti-vírus em clusters: http://support.microsoft.com/kb/250355/en-us.

[]'s

quarta-feira, 23 de novembro de 2011

Dica rápida: debug desabilitado no Visual Studio

Hoje estava trabalhando em um projeto e precisei debuggar uma solução. Só que o debug estava desabilitado, tanto o botão de "triângulo deitado verde" quanto o F5. Achei estranho, mas acabei percebendo o porquê: o projeto que estava ajustado como StartUp Project era do tipo Modeling Project. Foi só mudar isso para um projeto que gera um executável, como um Console Application ou Web, que o debug voltou a ficar disponível.

Projetos de banco de dados também fazem o debug ficar indisponível.

Óbvio, mas não tão claro...

[]'s

quarta-feira, 2 de novembro de 2011

Pex + TDD

Olá, pessoal!

Na última edição da .NET Magazine (nº 90), saiu meu artigo falando sobre a ferramenta Pex e seu uso dentro do TDD.

imageO Pex (http://research.microsoft.com/en-us/projects/pex/) é uma ferramenta de geração de código de testes automatizada, ou seja, ela gera as unit tests para você a partir do código-fonte já desenvolvido. Em um primeiro momento não vemos como isso pode se relacionar com TDD, já que a idéia desta técnica é escrever testes antes do código do programa que estamos desenvolvendo. Realmente ferramentas como o Pex não têm utilidade neste cenário quando utilizado o conceito de testes baseado em exemplos. Testes baseados em exemplos são aqueles nos quais nós temos uma entrada de dados e um resultado conhecido, e testamos o nosso código para garantir que o processamento produza a saída esperada.

Para fazer sentido o uso de ferramentas de geração de testes automatizada, precisamos de um conceito de testes de unidade diferente. Precisamos pensar nossos testes como teorias.

Podemos considerar que uma teoria é falsa se encontrarmos alguma condição que a invalide. Por exemplo, se tivermos uma teoria que “a cor dos olhos de uma pessoa é sempre igual para ambos os olhos”, basta que encontremos uma pessoa com heterocromia para rejeitarmos a idéia. É baseado neste conceito que o Pex pode ser utilizado numa abordagem TDD. Ao criarmos um teste de unidade parametrizável (ou seja, que receba dados de entrada), o Pex pode explorar o código em busca de situações de entrada que façam nosso programa falhar (encontrar furos no código que levem a bugs), e assim podemos refatorá-lo (da maneira que o TDD dita).

É claro que no artigo (http://www.devmedia.com.br/post-22581-Geracao-automatica-de-testes-numa-abordagem-TDD.html) eu explico isso melhor e com mais detalhes, não vou fazê-lo aqui para que os editores da revista fiquem bravos comigo, rs. Mas segue um link onde essa idéia de testes com teorias é explorado: http://shareandenjoy.saff.net/tdd-specifications.pdf.

[]’s e até a próxima!

quarta-feira, 26 de outubro de 2011

Como fazer undo-checkout em arquivos de um outro usuário

Hoje tivemos que remover os arquivos que estavam em checkout para uma das pessoas que trabalham com a gente. Só que o detalhe é que a máquina que essa pessoa usou para fazer o checkout, e consequentemente as informações que ficam no workspace, não estavam mais disponíveis (a máquina foi trocada, e a antiga sabe-se lá onde está).
Então, como desfazer os checkouts existentes?
A maneira que encontramos foi, utilizando um usuário administrador dentro do team project, fazer a remoção do checkout através de linha de comando (tf.exe).
Primeiramente, buscamos o nome do workspace que estava associado ao usuario, através do comando abaixo. Retornaram duas ocorrências, que são a máquina antiga e a máquina nova. Essa informação será importante depois.

C:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE>tf workspaces /owner:acroberto
Collection: tfs.intra.cliente.com.br\TPC
Workspace     Owner     Computer      Comment
------------- --------- ------------- -----------------------------------------
ESTACAO18     acroberto ESTACAO18
ESTACAO19     acroberto ESTACAO19


Em seguida, fizemos uma listagem dos arquivos que estavam em checkout no team project (chamamos de SISTEMA). Veja que jogamos o resultado da consulta em um arquivo texto dentro do c:\.

C:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE>tf status $/SISTEMA /recursive /user:* /server:tfs.intra.cliente.com.br\TPC >> c:\tf.log 

Ignoring the /server option.


Agora vem o comando importante. Usando o nome do workspace + login do usuário + diretório base a partir de onde os arquivos com checkin se encontravam, executamos o comando tf undo. Note o uso do parâmetro /recursive, que faz com que seja feita a busca em todas as subpastas do diretório inicial.

C:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE>tf undo /workspace:ESTACAO18;acroberto $/SISTEMA/CLIENTE-SISTEMA-interno /recursive

Pronto, os arquivos foram liberados, efetuando o undo-checkout. Para conferir, rodei novamente o comando tf status, para pegar todos os arquivos em checkout. Só sobrou um que eu mesmo estava trabalhando.


C:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE>tf status $/SISTEMA /recursive /user:* /server:tfs.intra.cliente.com.br\TPC 
Ignoring the /server option. 
File name         Change User    Local path
----------------- ------ ------- ----------------------------------------------
$/SISTEMA/CLIENTE-SISTEMA-interno/CLIENTE.SISTEMA.Batch
AtualizarDados.cs ! edit falgouw C:\Documents and Settings\FALGOUW\My Documents\Projetos\SISTEMA\CLIENTE-SISTEMA-interno\CLIENTE.SISTEMA.Batch\AtualizarDados.cs
1 change(s)

[]’s

Pérola: como dificultar o tratamento de erro de aplicações

Vamos dar uma olhada no método abaixo, que foi pego de uma situação real (mudei os nomes de variáveis para evitar que reclamem que o coloquei aqui). Nesta aplicação, há uma necessidade de sincronizar dados de usuários, buscando dados do sistema A e atualizando no sistema B.

public static void AtualizarUsuarios()
{
    try
    {
        using (BatchFacade batchFacade = new BatchFacade())
        {
            List<Usuario> usuariosSistemaA = batchFacade.ListarUsuariosSistemaA().GetRegistros<List<Usuario>>();
            List<Usuario> usuariosSistemaB = batchFacade.ListarUsuariosSistemaB().GetRegistros<List<Usuario>>();
            foreach (Usuario usuarioA in usuariosSistemaB)
            {
                Usuario usuarioB = usuariosSistemaA.Find(delegate(Usuario u)
                {
                    return u.SIAP.Equals(usuarioA.Codigo.ToString());
                });
                if (usuarioB != null && UsuarioModificado(usuarioA, ref usuarioB))
                {
                    batchFacade.AtualizarUsuario(usuarioB);
                }
            }
        }
    }
    catch
    {
        throw new ApplicationException("Falha ao atualizar os dados dos usuários");
    }
}

Esta aplicação possui um tratamento de erro genérico, que grava no eventlog todos os erros que ocorrem no sistema. Agora imaginem que ocorra um erro durante o processamento deste método: neste caso, a execução irá cair no block catch, e uma exception do tipo ApplicationException será lançada, com o texto "Falha ao atualizar os dados de usuários".
Agora imaginem vocês, com um erro em ambiente de produção, tendo apenas essa mensagem para descobrir o problema que está acontecendo. É nessas horas que a gente tem vontade de matar quem programa nas coxas, apenas se importando em entregar a funcionalidade e nem ligando para a pessoa que irá cuidar dessa tranqueira do sistema.

O simples fato de obter a exception original e passá-la como inner exception da ApplicationException que estamos lançando já ajuda, pois na rotina de tratamento de erros genérico podemos pegar os detalhes que ajudarão em muito identificar o problema. Neste caso, o problema era uma informação que vinha faltando no banco de dados.

[]'s

domingo, 23 de outubro de 2011

Limitações ao se trabalhar com o TFS 2010

Ao se trabalhar com o TFS, existem algumas limitações que devem ser levadas em consideração na hora de criar team projects ou work items. É importante conhecer isso para não ter problemas na hora de decidir como estruturar seus projetos (Nenhuma limitação listada é impeditiva para se trabalhar com o TFS, é só se preparar e evitar as situações que elas possam ocorrer ou serem necessárias).
[]’s

Renomear um Team Project

Renomear team projects
Após a criação de um team project (repositório de código-fonte, atividades do projeto, etc.) não é possível renomeá-lo. Uma vez escolhido o nome, será este para sempre, então tome cuidado ao escolher. Caso contrário, se realmente você quiser “renomear”, terá que criar um novo team project e migrar todo o conteúdo do anterior…

Mover work items entre Team Projects e Team Project Collections

Mover work items entre team projects
Não é possível mover work items entre team projects diferentes, visto que os process templates entre eles podem ser diferentes.

Mover Team Projects entre Team Project Collections

Mover team projects entre team project collections
Da mesma forma que os work items, não é possível mudar de lugar um team project. Isso porque pode ocorrer problemas com os IDs gerados para os artefatos armazenados, como work items e changesets.

Gerenciar work items no Excel ou Project ou tirar relatório de vários Team Projects

Manipular ou visualizar informações de diversos team projects ao mesmo tempo
Não é possível gerenciar os work items de vários team projects ao mesmo tempo em uma mesma planilha do excel, por exemplo. A visualização dos work items deve ser feita team project por team project. O mesmo vale para relatórios no sharepoint, por exemplo. Um de cada vez…

Rastrear merges entre vários Team Projects

Ver histórico de merges entre diferentes team projects
Não é possível visualizar o histórico para merges que são feitos entre branchs de team projects distintos. Mesmo porque, não é recomendado trabalhar dessa forma.

Links de work itens entre diversos Team Projects

Associar work items entre team projects
Por último, cada team project é uma unidade isolada. Isso quer dizer que não é possível relacionar work items entre team projects diferentes.

quarta-feira, 19 de outubro de 2011

Uso de static em OOP: escolhendo a arma correta

Já vi sistemas desenvolvidos em linguagens orientadas a objetos em que todos os métodos das classes eram estáticos. Uma vez perguntei para uma pessoa o motivo dela estar programando dessa maneira. Ela não soube me responder.

Bom, por que não devemos colocar static em todos os métodos? Para mim, a melhor resposta é comparar o uso de métodos static com o uso de baionetas.

Baioneta montada em um M-16

Antes do uso da pólvora, as guerras eram travadas geralmente em combate corpo-à-corpo, com o uso de objetos cortantes como espadas ou machados.

No final do século XVIII, na França, começaram a ser produzidas as primeiras baionetas. Baioneta é uma arma branca de curto alcance que pode ser presa na ponta de um fuzil. Dessa maneira, em situações especiais como combate próximo (trincheiras) ou simplesmente falta de munição, os soldados possam utilizar suas armas de fogo como lanças.

Mas é claro que atualmente, na maioria das situações, é preferível utilizar fuzis com o objetivo que eles foram criados: disparar projéteis!

E o que isso tem a ver com orientação a objetos e uso de métodos estáticos? Antes da orientação a objetos, os programas eram codificados levando-se em consideração o paradigma procedural, que é basicamente o jeito que trabalhamos quando usamos métodos estáticos: sub-rotinas manipulando estruturas de dados. Isso é diferente da orientação a objetos, onde as sub-rotinas e estruturas de dados são “mesclados” em objetos.

Pois bem, utilizar métodos estáticos em programação orientada a objetos (descontroladamente, que fique bem claro) é como se estivéssemos retrocedendo e utilizando uma ferramenta de forma arcaica, sem obter seus principais benefícios. É a mesma coisa que utilizar um rifle equipado com uma baioneta como lança sendo que você tem munição de sobra e está em campo aberto! O uso desregrado de métodos estáticos mata o que a OOP tem de melhor: polimorfismo, herança, etc. É claro que em casos especiais iremos precisar de métodos estáticos, mas isso tende a ser exceção.

Concluíndo, o uso de métodos estáticos em linguagens orientadas a objetos como C# ou Java não é errado, desde que se saiba o que está fazendo. Dê preferência ao uso de métodos de instância em OOP para tirar o melhor proveito, mas principalmente dê preferência ao bom-senso…

[]’s

terça-feira, 18 de outubro de 2011

Pérola: como remover caracteres de uma string

Encontrei este código no qual o desenvolvedor queria remover os pontos, traços e barras de um CNPJ formatado (de "12.345.678/0001-00" para "12345678000100"):

string estab = string.Empty;
char[] delimitadores = new char[] { '.', '/', '-' };
string[] partes = lblCNPJEstabelecimento.Text.Split(delimitadores, StringSplitOptions.RemoveEmptyEntries);
for (int i = 0; i < partes.Length; i++)
    estab = estab + partes[i];
} 

Bizarro...

Para remover uma única ocorrência, o método Replace é indicado: http://msdn.microsoft.com/en-us/library/fk49wtc1.aspx

Outra alternativa é utilizar regular expressions:
http://msdn.microsoft.com/en-us/library/system.text.regularexpressions.regex.replace%28v=VS.100%29.aspx

O link abaixo possui um extension method para remover vários caracteres: http://www.csharptricks.com/blog/2006/10/remove-characters-from-string-cnet.html

Mantenham o código simples e limpo...

[]'s

segunda-feira, 17 de outubro de 2011

Nota rápida: Concatenando valores com caractere de separação em C#

Abaixo está um exemplo rápido de como concatenar um array de strings em uma única string, separando esses valores por um caractere.

[TestMethod]
public void TestarConcatenacao01()
{
    var arr = new[] { "s1", "s2" };
    var s = string.Join(";", arr);
    Assert.AreEqual("s1;s2", s);
}

[TestMethod]
public void TestarConcatenacao02()
{
    var arr = new string[] { };
    var s = string.Join(";", arr);
    Assert.AreEqual(string.Empty, s);
}

[TestMethod]
public void TestarConcatenacao03()
{
    var arr = new[] { "s1" };
    var s = string.Join(";", arr);
    Assert.AreEqual("s1", s);
} 

string.Join substituí com uma linha um código que acabei de ver, onde eram feitos um foreach e vários condicionais para conseguir o mesmo objetivo (umas 5 linhas, no mínimo).

[]'s

sexta-feira, 23 de setembro de 2011

Regras para siglas

Eu tinha o costume de colocar um 's no final de siglas, como CPF ou CNPJ, para indicar plural (ex.: "As ONG's têm feito um trabalho excelente na região Nordeste."). ERRADO!!!

A regra gramatical para plural de siglas, quando a concordância permitir, é simplesmente acrescentar a letra "s", sem o apóstrofo. Ou seja, a frase acima fica "As ONGs têm feito um trabalho excelente na região Nordeste.".

É importante para quem trabalha com exatas não descuidar do conhecimento de lingua portuguesa, pois já dizia Chacrinha: "Quem não se comunica, se trumbica". Comunicação é tudo, e é sempre bom fazer as coisas da maneira correta.

Este link tem mais informações sobre o uso correto de siglas na língua portuguesa: http://www.soportugues.com.br/secoes/abrev/abrev9.php.

[]'s

terça-feira, 20 de setembro de 2011

Membros ThreadStatic e ASP.NET thread switching: cuidados ao usar

Tudo começou com uma dúvida referente como a classe TransactionScope mantém referência à um objeto de transação durante todo o seu escopo de atuação, mesmo quando são feitas chamadas à outros métodos sem a passagem explícita desse objeto de transação como um parâmetro desse método, por exemplo.

Olhando o código-fonte do TransactionScope através de ferramentas de disassembler, como o Redgate .NET Reflector ou Telerik JustDecompile (como eu amo esses aplicativos!), percebi que o contexto de transação é armazenado em um membro decorado com o atributo ThreadStatic, ou seja, é praticamente uma "variável global" esclusiva de uma thread, que não pode ser acessada por outra thread.



O problema ocorre quando trabalhamos no ASP.NET. Durante o processamento de uma página em ASP.NET, pode (leia-se: "pode", não que ocorrerá sempre!) uma mudança da thread atual (thread switching). Então, uma página que começa a ser processada pela thread 1 pode terminar com a thread 2! Quem define isso é a plataforma ASP.NET, que tenta dar um uso eficaz de recursos.

Bom, o meu medo era que uma aplicação ASP.NET usando TransactionScope pudesse gerar alguma inconsistência de banco de dados no momento que uma transação estivesse ativa e a thread eventualmente mudasse (http://www.hanselman.com/blog/ATaleOfTwoTechniquesTheThreadStaticAttributeAndSystemWebHttpContextCurrentItems.aspx). 

Mas uma pesquisa na internet me mostrou que esse evento de troca de thread é menos comum de acontecer do que eu imaginava. Geralmente ele ocorre quando há algum processamento assíncrono, por exemplo, usando as funcionalidades do async no ASP.NET 2.0 (http://msdn.microsoft.com/en-us/magazine/cc163725.aspx) ou na inicialização da página. Ou seja, praticamente esse problema pode ocorrer num mal uso de processamento assíncrono no ASP.NET com TransactionScope: quem em sã consciência abriria uma transação durante um processamento assíncrono?

Por isso, fiquei mais tranquilo ao usar transações controladas pelo TransactionScope em aplicações ASP.NET. Mas claro, se eu precisar guardar alguma informação de contexto, usaria o HttpContext.Current.Items ao invés de um membro estático marcado com ThreadStatic.

Alguns links esclarecedores:
[]'s

sexta-feira, 16 de setembro de 2011

Conversões e desconversões

Mais uma pérola que pode ser relacionada com meu outro post Vícios de linguagem de programação:

return (Int32.Parse(diff.Days.ToString()) < Int32.Parse(configuracao.Parametro.ToString()));

Primeiro detalhe: a variável diff é do tipo TimeSpan, e a sua propriedade Days já é um inteiro. Por que motivo ela é transformada numa string para depois fazer um novo parse para inteiro?

Segundo detalhe: a propriedade Parametro da variável configuracao já é uma string. Não há necessidade de chamar o método ToString().

Tem coisa que dá medo...

quarta-feira, 14 de setembro de 2011

"".GetType() vs. typeof(string)

No ASP.NET Webforms é possível incluir um javascript para ser renderizado na tela através do método RegisterStartupScript a partir da propriedade ClientScript do objeto Page. Este método então irá, na hora de "cuspir" o HTML, incluir o script de forma com que ele seja executado assim que a página seja carregada no browser.

Os dois primeiros parâmetros são um Type e uma string, representando um tipo qualquer e uma chave que são usados para identificar exclusivamente um script, de forma a identificar a duplicidade. Para o primeiro parâmetro, podemos passar o próprio tipo da página onde este script será usado. Mais detalhes em http://msdn.microsoft.com/en-us/library/asz8zsxy.aspx.

Bom, no exemplo abaixo, acabou-se usando o tipo como a própria string. Até aí "tudo bem", não vou discutir qual seria o valor correto para passar para este método, mas o que eu fiquei "besta" foi da ocorrência de "".GetType(), de forma a obter uma instância de Type que represente uma string. Poxa,se for para fazer isso, que pelo menos seja passando typeof(string).
Page.ClientScript.RegisterStartupScript("".GetType(), "", "alert('Mensagem qualquer de alerta do sistema!');", true);
Obs. Internamente, no CIL, typeof chama o método Type.GetTypeFromHandle.

[]'s

domingo, 11 de setembro de 2011

Vícios de linguagem de programação

Você conhece as expressões “acabamento final”, “elo de ligação”, “subir para cima”, “metades iguais”, “protagonista principal”, entre outras?
É o chamado pleonasmo, uma redundância em uma expressão que pode ser uma figura de linguagem (ou seja, o autor quer enfatizar alguma coisa ou ser irônico) ou um vício de linguagem.
Mas se temos um pleonasmo em um código-fonte, dificilmente isso foi feito propositalmente. Vamos ver um exemplo “clássico” disso. Vejam o código abaixo.
var lista = new List<string>();
// preenche a lista...ou não
bool existencia = lista.Count > 0;
if (existencia == true)
{
// faz alguma coisa...
}

Veja o conteúdo do condicional: existencia == true. Mas o tipo da variável existencia já não é um bool? E no final das contas, um resultado do tipo bool não é o que define a expressão que fica no condicional if? Sendo assim, o código poderia ser reescrito simplesmente como:
var lista = new List<string>();
// preenche a lista...ou não
bool existencia = lista.Count > 0;
if (existencia)
{
// faz alguma coisa...
}

Não existe a necessida de comparar o conteúdo da variável existencia com a constante true. Fazê-lo é uma redundância totalmente desnecessária.

Fica a “conclusão final” do post: sempre estejam atentos às repetições no código que não agregam nenhum valor. Procurem sempre deixar o código o mais enxuto possível, sem prejudicar a clareza. Maior quantidade de linhas de código não é critério de qualidade ou produtividade! Evitem o pleonasmo na hora de programar.

[]’s

quarta-feira, 7 de setembro de 2011

Nova graduação!

Faixa laranja do Krav-Magá

Passados mais de um ano (http://galorebr.blogspot.com/2010/04/primeira-graduacao.html), recebi nova graduação no Krav Magá: faixa laranja! O exame foi no dia 12 de Agosto, mas a cerimônia de entrega da faixa foi neste último final de semana, no Colégio Assunção em São Paulo.

Parabéns ao nosso mestre Duda e a todos que se esforçaram para subir mais este nível! Agora é treinar e evoluir o condicionamento físico para o próximo desafio daqui a aproximadamente dois anos.

Kidá!

domingo, 7 de agosto de 2011

Considerações sobre frameworks corporativos

Um dos objetivos mais desejados pelos gerentes no desafio de desenvolver sistemas é a reutilização de componentes com a finalidade de economizar trabalho e tempo (ou seja, dinheiro). Com essa idéia na cabeça e uma boa dose de boa vontade, os desenvolvedores e arquitetos procuram criar ferramentas e componentes que possam ser compartilhados dentro da corporação (frameworks técnicos). Em muitos casos, apesar do esforço, esse objetivo não é alcançado, e acabamos citando o ditado “de boas intenções o inferno está cheio”…

Não estou dizendo que a reutilização é um conceito ruim, ao contrário, é claro para a indústria de software prova a necessidade e importancia dela a cada dia. No entanto, gostaria de compartilhar as minhas experiências em relação à esse assunto para ajudar a evitar as armadilhas que aparecem quando decidimos criar um framework corporativo. Trabalhei bastante tempo criando e mantendo esse tipo de software, e acho que tenho algumas boas histórias…

Separei três principais bases na qual um framework corporativo deve se basear, e consequentemente quais são os problemas que geralmente encontramos caso esses pré-requisitos não sejam seguidos.

A ferramenta certa para o problema errado

A primeira base que temos que adotar para a construção de um framework corporativo é o valor ao desenvolvimento, ou seja, o quanto esse componente agrega ao processo de criação de software. Em linhas gerais, posso resumir isso em quanto a solução proposta pelo framework se adequa ao problema da equipe de desenvolvimento e de quanto dinheiro é economizado com o seu uso.

Um dos primeiros erros ao se decidir criar um framework corporativo é começar com a solução, e não com o problema a ser resolvido. Nos achamos superiores ao imaginar que conseguimos criar uma bala-de-prata que irá matar um lobisomem que ainda nem existe (e nem sabemos se será um lobisomem ou outro tipo de monstro). Já vi projetos de ERP em que antes de se ter requisitos de negócio já se estava codificando uma biblioteca de componentes utilitários ao projeto. Não! Vamos entender melhor primeiro o que precisa ser resolvido, e focar no problema e não em criar o componente mais bonito do mundo cuja maior parte (senão todo) não é utilizado. Com os requisitos mais bem definidos, podemos olhar para o mercado e ver se já não existe uma opção pronta e que seja viável tecnicamente e financeiramente.

Além disso, é preciso estar aderente às estratégias de crescimento da empresa (papel do arquiteto), pois de nada adianta as horas gastas se mais para frente todo o trabalho se torna obsoleto (por exemplo gastar esforço em um framework de aplicações desktop sendo que o direcionamento da empresa é ter seus sistemas em web). Por outro lado, precisamos ter um compromentimento da parte dos gestores (gerentes, diretores, etc.) em ter recursos alocados para a evolução e continuidade do que foi feito.

Um último detalhe. Precisamos saber vender o framework construído para outras equipes. Somos os fornecedores deles, e eles não estão fazendo nenhum favor em utilizar o que criamos. Reitero, temos que agregar valor para os outros (TI é sempre assim, trabalhar para suportar o trabalho de outras pessoas).

Como enfrentar as mudanças?

Decidimos que temos que criar um framework corporativo. Ótimo, vamos colocar a mão na massa. Criamos uma primeira versão do componente que atenda uma equipe de sistema X. Fazemos um happy hour para comemorar o uso com sucesso dessa equipe. Outro time Y vê e também quer utilizar o framework, mas o problema que eles têm para resolver é um pouco diferente do primeiro. Vamos ter que alterar nossa recém-criado ferramenta… e agora, é fácil? Podemos sair mexendo? E se quebrarmos alguma coisa que a primeira equipe usava?

Aqui destaco a maturidade, comprometimento e grande conhecimento que as pessoas que constrõem esse tipo de software devem ter. Precisamos de um framework que possa ser adaptador para novas situações de forma fácil! Precisamos de componentes que tenham contratos de uso bem definidos! Não estou falando nenhum conceito fora do comum, é a pura aplicação da orientação a objetos e as boas práticas associadas (isolamento dos detalhes da implementação; princípio Open/Closed – código que permite extensão mas é fechado para modificação, etc.).

A idéia aqui não é ter um BDUF (Big Design Up Front, ou seja, tentar prever todos os problemas e já contemplar a solução logo de primeira), mas precisamos usar nossa experiência para que pelo menos os problemas que já passamos na vida possam ser endereçados de uma forma mais elegante e de fácil manutenção.

Outro ponto importante: o nosso framework precisa ter uma fácil distribuição e minima dependência de outros componentes. Isso facilita quando temos que distribuí-lo para uso de outras pessoas, como fornecedores externos (fábrica de software).

Para este caso, tenho uma experiência feliz. Cuidávamos de um framework de processamento assíncrono, que funciona da seguinte forma: um sistema possui um componente .NET no COM+ (sim, COM+, legado…); ao invés da parte cliente desse sistema chamar este componente diretamente, ele acionava nosso framework, passando o nome do componente deles, o nome do método e os valores dos seus parâmetros para a execução, e nosso componente enfileirava a execução e colocava para rodar assím que possível. Uma vez identificamos um problema neste framework, que segurava referência de dll .NET quando era passado um parâmetro de tipo definido pelo cliente (que não fazia parte dos tipos do CLR). Quando era necessário atualizar o sistema, não era possível mexer na dll. O ponto de problema era a manipulação do objeto contendo as informações do que seria executado diretamente pelos clientes do framework. Com o simples uso de poliformismo, fizemos a alteração no sistema de forma a tratar este caso sem quebrar o contrato dos vários sistemas que utilizavam nosso framework. Este foi um ponto legal, pois era inviável o retrabalho das muitas equipes de sistema em adaptar o uso caso fosse necessário mudar a forma como se utilizava o framework.

Conhecimento é a chave

Temos vários níveis de experiência dos desenvolvedores dentro de uma empresa, desde o mais escovador de bits até o que ainda não sabe ligar um computador (sim, eles existem e provavelmente hoje ganham mais que o seu pai ganhava quando trabalhava para sustentar você). Vou citar um caso. Há algum tempo introduzimos um framework para desenvolvimento web ASP.NET, uma moldura (uma master page, para ser mais exato) na qual as demais equipes de sistemas colocavam suas páginas. O ganho principal neste caso eram controles já embutidos na solução (menus, controle de acesso, etc.), e uma certa padronização visual. De repente, começamos a receber reclamações que este framework não funcionava direito, e estava atrapalhando o desenvolvimento das equipes. Ao analisar melhor o caso, descobrimos que na verdade o erro que os desenvolvedores estavam tendo na chamada aos seus próprios serviços WCF, por falta de conhecimento desse framework da Microsot. Mas como eles não sabiam interpretar os erros, acabavam tomando por verdade que a causa dos seus problemas era o famigerado framework. E eis que surge a famosa “muleta”. Tivemos que parar o que estávamos fazendo e provar por A mais B que o erro era com eles. Para ajudar, o coordenador dessa equipe não tinha competência técnica para entender o problema, e comprava a briga deles sem conseguir compreender que o erro estava com eles. Infelizmente (e é muito difícil para mim falar isso), temos que aceitar a realidade que hoje trabalharemos com muitas pessoas que vão deixar a desejar no quesito de conhecimento, principalmente porque atualmente o mercado de TI está aquecido e dá bons salários para pessoas que não têm muito estudo ou aptidão para a coisa.

O que aprendemos aqui é que um framework corporativos precisam ser de fácil utilização, com mensagens claras de erros que ocorram, sejam eles por uma configuração/uso incorreto pelo desenvolvedor, ou pelo próprio código do desenvolvedor. Em uma outra situação, num framework corporativo para acesso a base de dados SQL Server, vimos que simplesmente colocando um tratamento de erro mais claro (mostrando detalhes do erro, qual base de dados, servidor, etc.) a quantidade de reclamações sobre a qualidade do framework caiu drasticamente, pois os próprios desenvolvedores conseguiam identificar e resolver os problemas.

Mas mesmo assim, é essencial que haja treinamento contínuo da ferramenta. Uma boa documentação com exemplos práticos é essencial. Indo mais fundo, entendo que seja necessário uma equipe dedicada ao suporte aos desenvolvedores, tirando esta tarefa de quem cria e mantém o framework. Se o framework seja utilizado por fornecedores externos, é necessário que haja um ponto focal desse fornecedor para o suporte. As responsabilidades precisam estar muito bem definidas e clara para todos.

image

E ae, vamos construir um framework?

Na real? Eu estou fora dessa, rs. Acho que hoje estou meio que parecido com veteranos de guerra que qualquer barulho no quintal de casa já acham que são vietcongs. Talvez eu mude de idéia se todos os pontos de problema que eu apresentei acima sejam devidamente endereçados, principalemte na questão do treinamento (esse foi nosso principal calcanhar de Aquiles)…

Mesmo porque acredito que já exista no mercado uma boa solução para todos os tipos de problemas que geralmente encontramos no desenvolvimento de sistemas. Não precisamos criar algo novo. Volto no ponto que disse que precisamos agragar valor na criação de software, mesmo que isso signifique que não vamos desenvolver nada. Manter um framework corporativo é caro, e é preciso muita certeza de que ele é realmente necessário.

Termino o post com mais um ponto de atenção que não coloquei anteriormente: a cultura da empresa. Se a cultura da empresa for contratar pessoas com nível de conhecimento extremamente distinto (gente muito boa e gente muito ruim tecnicamente) e não prover um crescimento profissional, aí é que o barco afunda de vez. Provavelmente a estratégia dela é querer que as pessoas boas sejam “gurus” que preparem o processo de desenvolvimento de forma que qualquer um (a mão-de-obra “barata” e “facilmente substituível”) possa desenvolver. A tendência é que toda a responsabilidade de qualquer problema caia sobre a equipe sênior, pois é dela a tarefa de prever todo e qualquer erro que alguém possa cometer e não deixá-lo acontecer (já ouvi um coordenador de desenvolvimento tentar isentar a culpa da sua equipe de juniores pois eles “ganhavam muito pouco para poder saber programar direito”). Construir um framework numa empresa assim é um tiro no pé.

Esse foi um resumo das minhas experiências nesta questão, e espero que sejam úteis para alguém.

[]’s

sábado, 23 de julho de 2011

Artigo de Moles - .NET Magazine 87

image

Pessoal, esse mês saiu a .NET Magazine edição 87 contendo um artigo meu sobre o Moles.

O Moles é uma ferramenta de apoio de testes feita pela Microsoft que permite a criação de objetos dublês como stubs, além de permitir que qualquer método em .NET (inclusive os do próprio CLR) sejam interceptados e substituídos por um método escrito por você! Isso quer dizer que você pode fazer com que o DateTime.Now retorne sempre uma data fixa. Para que isso é importante? Para poder testar métodos isolando as duas dependências. O artigo explica os conceitos dessa ferramenta e como utilizá-la para que você escreva testes de unidade de forma mais eficiente.

O artigo online está em http://www.devmedia.com.br/post-21606-Introducao-ao-framework-de-testes-Microsoft-Moles.html, e a revista está na banca mais próxima da sua casa.

[]’s e boa leitura

quarta-feira, 20 de julho de 2011

Executar procedure no Oracle que retorne dados como um cursor do tipo OUT

Nota rápida: no Oracle, stored procedures que retornam dados de tabelas (result sets) vem na forma de cursores (diferente do SQL Server que vem direto). Para poder ver o resultado da execução de uma procedure de SELECT no Oracle, usa-se o comando abaixo:

    var refCur REFCURSOR;
    execute NOME_PACKAGE.NOME_PROCEDURE(283, 3507, :refCur);
    print :refCur;

Sendo que a nossa procedure de exemplo tem três parâmetros: dois inteiros e um cursor do tipo OUT (abaixo está o detalhamento da sua declaração):

    PROCEDURE NOME_PROCEDURE(pID_X IN TB_ABC.ID_X%TYPE,
                                          pID_Y IN TB_XYZ.ID_Y%TYPE,
                                          pCURSOR_SAIDA OUT CURSOR_SAIDA);
[]'s

quarta-feira, 15 de junho de 2011

Procedures no Santander

Galera do noSQL, uma certeza eu tenho, o banco Santander usa stored procedures no seu bankline...

Alguém precisa falar para eles da boa prática de segurança que é não mostrar detalhes de erros para os usuários. Principalmente em sistemas expostos na Internet e que cuidam do dinheiro dos outros...

sexta-feira, 27 de maio de 2011

Testando métodos privados

Me deparei com uma thread interessante sobre unit test no StackOverflow (melhor site para postar dúvidas na minha opinião):

A dúvida era como testar unitariamente métodos privados.

Destaco duas respostas técnicas: utilizar o atributo InternalsVisibleToAttribute (http://devlicio.us/blogs/derik_whittaker/archive/2007/04/09/internalsvisibleto-testing-internal-methods-in-net-2-0.aspx), que faz com que tipos fiquem visíveis para unit tests, mas que acabam deixando "rastros" nos assemblies quando compilados mesmo em modo Release; utilizar AccessPrivateWrapper (http://www.amazedsaint.com/2010/05/accessprivatewrapper-c-40-dynamic.html), que encapsula o acesso a métodos privados através do dynamic do C# 4.0.

Mas a melhor resposta, na minha opinião, não é nenhuma dessas e sim uma conceitual: métodos privados não deveriam ser alvo de testes unitários.

Sugiro dar uma olhada no fórum: http://stackoverflow.com/questions/250692/how-do-you-unit-test-private-methods

[]'s

terça-feira, 24 de maio de 2011

Configurando conexão com Oracle através de TNS - básico

Havia trabalhado com Oracle alguns anos atrás, e pra mim esse SGBD tem muita coisa desconhecida... Precisei pegar um projeto onde que usa esse banco de dados. Meu primeiro "desafio" foi entender como fazer para configurar o client do Oracle para poder conectar a aplicação ASP.NET na base de dados. Isso deve ser o básico do básico para quem trabalha com Oracle, mas como pra mim é novidade, decidi deixar isso documentado no blog já que a idéia original dele é ser uma knowledge base pessoal (apesar que eu admito, não sou tão frequente com os posts como eu gostaria de ser).

Bom, em primeiro lugar, eu precisei baixar os componentes client do Oracle. Isso eu fiz baixando os componentes do Instant Client e os colocando na pasta C:\oracle\instantclient_10_2\ (o link de onde baixei está logo abaixo). Esse Instant Client é um conjunto de componentes que permite a execução de aplicações que consomem bases Oracle sem a necessidade de fazer a instalação completa do cliente, através de setup, CD, etc.

Em seguida, eu precisei entender como configurar um arquivo chamado tnsnames.ora. Que raios que era isso? Bom, trata-se de um arquivo contendo as definições de endereços de servidores para a conexão. O arquivo tnsnames.ora me foi enviado por e-mail,e para configurá-lo foi necessário os seguintes passos:
  1. Colocar este arquivo no disco local. Acabei colocando em C:\oracle\instantclient_10_2\network\admin\. Note que é importante este arquivo estar dentro da pasta network\admin (o que vem antes pode variar).
  2. Criar uma variável de ambiente chamada ORACLE_HOME. Como valor, coloquei o caminho C:\oracle\instantclient_10_2 (pasta onde se encontrava o diretório network\admin).
Pronto, com isso minha aplicação funcionou!

Links interessantes:
Obs. Se alguém que conhece mais o Oracle e quiser acrescentar alguma coisa, por favor comente!

[]'s

ATUALIZAÇÃO (18/01/2012): Olá. Um detalhe que é legal citar aqui também é quando temos uma aplicação rodando no IIS que acessa o Oracle e dá erro ORA-12154: TNS:could not resolve the connect identifier specified. Nas ocorrências que tive disso, bastou dar permissão de leitura para o usuário que roda o ASP.NET para ele poder ler o arquivo tnsnames.ora lá na pasta onde este arquivo se encontra.

sexta-feira, 15 de abril de 2011

O pior é impossível?–parte 4

Esse é o maior encadeamento de IF’s que eu já vi em vida…, parece um ASCII art.

Detalhe que até agora eu não consegui ententer o que o cara quis fazer…, mas acho que também não vale muito a pena.

clip_image002

clip_image002[5]

clip_image002[7]

clip_image002[9]

clip_image002[11]

Este post foi um oferecimento da AntiIfCampaign:

I have joined Anti-IF Campaign

sábado, 19 de março de 2011

Webcamps em São Paulo

Nesta última sexta-feira, tive a oportunidade de participar do Webcamps, um evento itinerante da Microsoft que passou por Sâo Paulo, no Senac de Santo Amaro. Tivemos apresentações de Phil Haack, do time do ASP.NET MVC, e Drew Robbins, evangelista técnico.

Neste dia, foram as apresentações técnicas, e hoje sábado, que infelizmente não pude participar, foram sessões práticas.

Nas apresentações de sexta, ouvimos os dois convidados conversando sobre fundamentos e tópicos avançados do ASP.NET MVC, Entity Framework e Nuget (falarei mais a respeito logo). Apesar de algumas coisas abordadas nas palestras terem sido o básico dessa tecnologia, é uma oportunidade ímpar ouvir das pessoas que trabalhar diretamente na sua criação. As apresentações de Phil foram muito boas, mas eu achei que o Drew deixou a desejar em algumas partes, pois parecia que ele estava perdido em determinados momentos…

Além deles, Murilo Maciel Curti apresentou features do IE9 + HTML 5 bem interessantes. Um dos links que ele passou com conteúdo para vermos foi http://www.beautyoftheweb.com, que contém alguns exemplos de sites de “vanguarda”.

Como disse, hoje sábado foi o dia do “mão na massa”, com a oportunidade dos participantes fazerem os “hand-ons” contando com a presença do pessoal para ajudar em dúvidas. O material de hoje está disponível em http://trainingkit.webcamps.ms/, para que não pode participar, como eu.

Da agenda que estava proposta, ficou faltando uma apresentação sobre oData, que foi substituída por uma do Nuget. Aliás, falando nesta ferramenta, nem coloquei neste blog que na .NET Magazine 82 saiu um artigo meu referente à ela.

Pronto, agora que fiz meu momento-publicidade, vamos voltar ao conteúdo da palestra sobre o Nuget. Em resumo, o Nuget é uma ferramenta open-source que permite encontrar e instalar nos nossos projetos do Visual Studio bibliotecas criadas por terceiros, por exemplo o NHibernate. O objetivo dela é facilitar, para que não precisemos ficar procurando na internet a última versão delas, etc. Um dos itens interessantes que foram apresentados sobre o Nuget é a facilidade com que temos para publicar nossos próprios pacotes no repositório oficial. Basta ir no site http://www.nuget.org/, se cadastrar e você pode fazer o upload de um pacote contendo os componentes que você criou, disponibilizando para o mundo inteiro! Também foi mostrada a ferramenta Nuget Explorer (beta), que serve para visualizar os dados de um pacote para identificar seu conteúdo e os metadados associados.

Outra coisa que eu aproveitei e perguntei para o Phil era em relação à disponibilização de um comando de reparação de pacotes instalados, caso alguma referencia seja removida sem querer. O Nuget ainda não possui uma funcionalidade dessa, sendo necessário dar o comando de remover o pacote e readicioná-lo. Bom, sendo uma ferramenta open-source, qualquer um pode criar esta e outras funcionalidades e submeter para aprovação.

Enfim, foi um evento bem legal!

[]’s

domingo, 27 de fevereiro de 2011

jQuery Templates e combos preenchidas

Estou usando o plugin de template do jQuery em uma tela de um sistema que estou construindo, e encontrei o seuginte problema, que mostro no exemplo abaixo.

Vamos supor que temos uma página que contém uma tabela usada para edição de informações. Lista de cidades e estados, por exemplo.

image

Nesta listagem, conforme vamos precisando de novas linhas, vamos incluindo informando o nome da cidade e selecionando o estado na combo. Abaixo está o código que faz essa inclusão:

<!DOCTYPE html>
<html>
<head>
    <script src="http://code.jquery.com/jquery-latest.min.js"></script>
    <script src="http://ajax.microsoft.com/ajax/jquery.templates/beta1/jquery.tmpl.min.js"></script>
</head>
<body>
    <script id="templateLinha" type="text/x-jquery-tmpl">
    <tr>
        <td>
            <input type="text" value="${Cidade}" />
        </td>
        <td>
            <select>
                <option value="GO">GO</option>
                <option value="SP">SP</option>
                <option value="PR">PR</option>
                <option value="RJ">RJ</option>
            </select>
        </td>
    </tr>
    </script>
    <script language="javascript">
        $(document).ready(function () {
            $("#btnAdd").click(function () {
                var cidades = [{ Cidade: $("#txtCidade").val(), Estado: $("#ddlEstado").val()}];
                $("#templateLinha").tmpl(cidades).appendTo("#listaCidades").find("select").val(function (index, value) {
                    return cidades[index].Estado;
                });
            });
        });
    </script>
    <input id="txtCidade" type="text" />
    <select id="ddlEstado">
        <option value="GO">GO</option>
        <option value="SP">SP</option>
        <option value="PR">PR</option>
        <option value="RJ">RJ</option>
    </select>
    <button id="btnAdd">
        Adicionar cidade</button>
    <br /><br />
    <table border="1">
        <thead>
            <tr>
                <td>
                    Cidade
                </td>
                <td>
                    Estado
                </td>
            </tr>
        </thead>
        <tbody id="listaCidades">
            <tr>
                <td>
                    <input type="text" value="Santo André" />
                </td>
                <td>
                    <select>
                        <option value="GO">GO</option>
                        <option value="SP" selected>SP</option>
                        <option value="PR">PR</option>
                        <option value="RJ">RJ</option>
                    </select>
                </td>
            </tr>
        </tbody>
    </table>
</body>
</html>



Perceba que no código estou utilizando a funcionalidade de template para a inclusão da nova linha. Dentro da tag <script> chamada “templateLinha”, tenho todo o modelo que será utilizado para esta inclusão. Note que trata-se apenas de uma nova linha na tabela, contendo uma caixa de texto e uma combo. Veja que no lugar do valor da caixa de texto, eu coloquei ${Cidade}. É a partir da substituição desse campo que o modelo da linha será preenchido automaticamente (e veja também que, pela característica de um objeto do tipo <select>, não conseguimos fazer essa substituição, e este será nosso problema.



No código que é executado no clique do botão, eu obtenho os valores que serão passados ao template, transformados em um array de objetos, e chamo a função abaixo, que faz com que os dados sejam aplicados no template “templateLinha”, e anexados no objeto “listaCidades”.



$("#templateLinha").tmpl(cidades).appendTo("#listaCidades");

No entanto, ao executar, vemos que a combo não é preenchida com o valor que queremos (ela fica com o valor padrão, que é o primeiro da lista, “GO”). Isto ocorre pois não temos como substituir um valor dentro da tag <select> para que seja ajustado o valor da combo (diferentemente de um objeto do tipo <input>, que é apenas substituir o conteúdo da propriedade “value”).



image



Neste caso específico, a resolução não pode ser feita apenas com o template do jQuery (minto, até consegue usando a funcionalidade de “if” do plugin de Template do jQuery, mas se usar desta maneira terá que construir o código da combo na mão, e não poderá, por exemplo, utilizar um helper de geração de HTML como o método Html.DropDownList() do ASP.NET MVC).



Bom, para resolver este problema de uma forma elegante, temos que contar com a função .val(xxx) do jQuery, só que não passando um valor mas sim uma função que retorne este valor. Com isso, podemos fazer uma iteração nos valores do array, obtendo o dado correto. Veja o trecho do código alterado abaixo (destacado em azul).



<script language="javascript"> 
    $(document).ready(function () {
        $("#btnAdd").click(function () {
            var cidades = [{ Cidade: $("#txtCidade").val(), Estado: $("#ddlEstado").val()}];
           $("#templateLinha").tmpl(cidades).appendTo("#listaCidades").find("select").val(function (index, value) {
                return cidades[index].Estado;
            });
        });
    });
</script>

image


Desta maneira, logo após a inclusão da linha na tabela, já fazemos a correção dos valores da combo.


[]’s e até a próxima!