segunda-feira, 26 de dezembro de 2011

Treinamento Azure: Primeira turma de Azure da Sr Nimbus!

Abrimos nossa primeira turma de treinamento Azure, voltada para o pessoal que deseja ingressar de cabeça no desenvolvimento de soluções Web para o Windows Azure. Modelamos o curso de forma que o aluno veja todos os passos necessários para sair do “File > New > Project” até realizar o deploy da aplicação. E sempre apresentando os conceitos relacionados a cada aspecto do desenvolvimento realizado.

Além desta visão “início-ao-fim”, os laboratórios criam uma aplicação que tem o SQL Azure como base de dados, e usa filas e worker roles para mostrar a escalabilidade de uma aplicação construída para tirar proveito do conceito de computação na nuvem. De quebra, a aplicação exemplo possui uma arquitetura que pode ser usada como referência para as suas primeiras aplicações Azure, de forma que você já possa ser produtivo assim que acabar o curso.

No modelo tradicional de cursos de ferramentas de desenvolvimento você vê as partes mas não o todo. É comum, neste modelo, o “momento E agora???” que o aluno passa após o curso, exatamente pela falta desta visão de um processo contínuo no uso da ferramenta. Nosso objetivo é mudar isto, ao construir uma aplicação do início ao fim e fornecer uma arquitetura simples mas que pode ser usada em aplicações “de verdade”, e não apenas em demonstrações.

A ementa do curso está aqui, e as inscrições já estão abertas!

[]s,

GB

quinta-feira, 22 de dezembro de 2011

Restrições nos nomes de filas no Azure Storage

Recebemos o erro “One of the request inputs is out of range” ao usar uma fila pra implementar comunicação entre web e worker roles rodando no Azure. Depois de revirar a Internet por algum tempo, achei esse artigo: Naming Queues and Metadata. Basicamente as regras de nomenclatura para filas são:

  1. O nome da fila deve começar com letra ou número, e só pode conter letras, números e o caracter “hífen” (-) (não é o “sublinhado”).
  2. O primeiro e o último caracter do nome deve ser letra ou número, não pode ser o hífen. Hífens consecutivos não são permitidos.
  3. Todas as letras devem ser minúsculas. (PQP. Por que não documenta isto no construtor da classe CloudQueueClient? Ou em CloudQueueClient.GetQueueReference() que recebe o nome da fila a acessar? Ou então em CloudQueue.CreateIfNotExists(), já que esse método cria a fila???)
  4. O nome da fila deve ter no mínimo 3 e no máximo 63 caracteres.

domingo, 14 de agosto de 2011

tf rollback: Como voltar os fontes para um determinado ponto do desenvolvimento

Comé que a gente volta o código da nossa aplicação pra um determinado ponto do sistema no TFS?

Bem, a ferramenta dá o suporte, mas você tem que ter processo. Tem várias formas de fazer isto. Uma delas é usando labels. Pra poder voltar em um ponto no tempo, você tem que fazer o seguinte:
  1. Combinar com a equipe. É, porque todo mundo tem que saber dessa combinação, preparar os fontes nos quais estão trabalhando para isto, e fazer checkin.
  2. Aplicar um label aos fontes. Na janela do Solution Explorer, botão direito no diretório no qual estão os fontes desejados, opção "Apply Label".
Esta label vai ser seu "ponto no tempo" para o qual você pode voltar os fontes. Depois de tocar horror no código, pra voltar pro momento no qual você colocou a label, faça o seguinte:
  1. Abra um "Visual Studio 2010 Command Prompt".
  2. (opcional) Use o comando tf labels para listar o nome da label para a qual você quer fazer o rollback:
    tf labels *@$/Projeto /owner:* /collection:http://server:8080/tfs/testcollection
  3. Mude para o diretório aonde estão os itens nos quais você deseja fazer o rollback.
  4. Rode o comando tf rollback para voltar o código para o ponto desejado:
    tf rollback /toversion:L"Solução Criada" HelpDesk /recursive
Este último comando volta os fontes para o estado em que estavam quando a label "Solução Criada" foi aplicada. O diretório atual é o diretório imediatadamente acima do diretório "HelpDesk", no qual estão os fontes do sistema. O utilitário tf faz checkout de todos os arquivos a modificar e retorna os mesmos para o estado da label "Solução Criada". Quando você fizer o checkin agora, a solução estará como estava quando a label "Solução Criada" foi aplicada.

sexta-feira, 12 de agosto de 2011

DSI e SDM

A MS tá investindo num trem chamado DSI - Dynamic Systems Iniciative, que tem como um dos componentes básicos uma linguagem de descrição de sistemas distribuídos: o SDM (System Definition Model), que é basicamente um esquema XML pra descrever sistemas distribuídos, seus componentes, datacenters, e a distribuição dos sistemas em um datacenter. Essa idéia vem pelo menos desde o Windows 2003 - o paper de overview da DSI no site da MS é de 2004, e segue a seguinte idéia: hoje temos o hardware e o software básico - recursos do sistema operacional e suporte nas liguagens de programação - para substituir mainframes por redes de PC's nos datacenters. A visão da MS é poder descrever sistemas e suas subdivisões (ex: executáveis e DLL's), juntamente com datacenters (máquinas, redes, appliances, etc) e a distribuição destes sistemas nos datacenters através de uma linguagem formal. O objetivo é minimizar o esforço de instalação, monitoração e modificação destes ambientes de execução de aplicações (HW + SW), automatizando o máximo possível dessas tarefas. Aí se abre um monte de possibilidades:

  • IDE para o monitorar e modificar sistemas distribuídos.
  • Linguagem de consulta e de modificação destes sistemas.
  • Scripts para automação de deploys.
  • ...

O VS hoje já tem designers para sistemas (system Designer), seus componentes (Application Designer) (EXE, DLL, web services, etc) e para "datacenters lógicos" (Logical Datacenter Designer) (sem os servidores físicos). E faz a junção disto num modelo de deploy (Deplyment Designer) e verifica se há algum problema de configuração e comunicação na arquitetura física da sua aplicação. O uso destes designers na fase de modelagem da aplicação é muito legal porque (a) obriga você, arquiteto da aplicação, a pensar nessas coisas "mundanas" e incluí-las na sua modelagem - pra não dar problemas que só serão detectados quando a aplicação for posta no ambiente de produção, e também porque (b) envolve o pessoal de administração de rede no design da arquitetura, o que é ótimo, porque eles montaram, conhecem e controlam o ambiente de hardware no qual sua aplicação vai rodar, então eles tem que ter uma palavra no assunto.

Imagina só, criar diagramas de sistemas e de datacenters em uma ferramenta, depois apertar um botão "Deploy" e aí a ferramenta pega um monte de ISO a partir de uma "biblioteca de VM's", cria as VM's no Hyper-V, configura a comunicação entre elas, configura os serviços, e instala os componentes do sistema em cada uma delas. Pronto. Um ambiente de produção instalado e funcionando em o que? meia hora? uma hora?Tamo rapidamente caminhando pra isso... Dá uma olhada no TFS Lab Manager!

[]s,
GB

quarta-feira, 20 de julho de 2011

ReportViewer: Outra Razão Para “Sys.ArgumentNullException: Value cannot be null. Parameter name: panelsCreated[…]”

ponte-que-partiu-1 
Ponte Que Partiu

CARVALHO. PONTE QUE PARTIU!!! Perdemos uma manhã inteira caçando o erro “Sys.ArgumentNullException: Value cannot be null. Parameter name: panelsCreated[1]” que era gerado em um web form que tinha um controle ReportViewer pra mostrar relatórios na nossa aplicação. A nossa “preguiça” me parece ser um jeito bem fácil de este erro aparecer: copiar um relatório já existente para fazer um novo. Nós criamos uma cópia de um relatório já existente pra reaproveitar o trabalho de layout já feito para o relatório original. Só que a página com o relatório original funcionava ok, mas na página com o novo relatório, o controle ReportViewer mostrava um painel em branco, e o IE mostrava o seguinte erro de JavaScript:

sheila-carvalho
Sheila Carvalho

Sys.ArgumentNullException: Value cannot be null. Parameter name: panelsCreated[1]

O problema foi gerado porque o relatório original tinha 4 parâmetros, e no segunda relatório só usamos 3. O quarto parâmetro não era usado, e não tinha um valor default. E, conforme descobrimos, parâmetros para os quais não são fornecidos um valor provocam o erro acima na renderização do ReportViewer. Bastou retirar a definição do parâmetro no arquivo RDLC e o relatório foi renderizado corretamente.

segunda-feira, 4 de julho de 2011

Linq, Distinct(), IEqualityComparer e GetHashCode()

Disclaimer: esse artigo é sobre um detalhe tão específico do Framework, que vale mais pelas fotos do que pelo conteúdo. Mas se você for uma das 19 pessoas no mundo que querem usar Distinct() em Linq para evitar instâncias repetidas no seu resultado, além das belas paisagens o código pode te ajudar.
GB no Big BenEstou eu encalhado aqui no Galeão, voltando de uma semana nas Európias – eu e Minha Dona (letra maiúscula que não sou doido) fomos conhecer Paris e Londres. Nosso vôo pra Brasília só sai daqui a 6 horas; eu não tô fazendo nada mesmo, então pensei em escrever um pouco sobre Linq, Distinct(), IEqualityComparer e GetHashCode().
Uma aplicação nossa lê de um arquivo um monte de registros de produtos pra dentro de uma List<Produtos>. Aí eu fui jogar estes produtos na tabela de Produtos no BD, mas como havia registros repetidos no arquivo, apareceram vários registros para o mesmo produto no banco.
“Bem, é só fazer o DISTINCT na lista”. Ok. Fui olhar o método Distinct() do Linq e achei duas assinaturas: uma recebe uma expressão lambda cujo resultado, ao ser avaliado para cada registro da lista, define os registros distintos; e outra recebe uma instância de IEqualityComparer, a qual nem dei uma segunda olhada.
Pra gerar minha lista de produtos distintos, escrevi:
// 1ª tentativa: Distinct(p => p) pra retornar uma lista de produtos sem repetidos 
List<Produto> listaSemRepetidos = listaProdutos.Distinct(p => p);
Vênus de Milo no Louvre - Grandes MiércolesEstranhamente, isto fez com que *todos* os objetos em listaProdutos aparececem em listaSemRepetidos. Pensando melhor, faz sentido. Distinct() tem que ver se os objetos são iguais para decidir se eles são repetidos, então deve chamar Equals() – qualquer classe tem Equals(), que é herdado de System.Object - e na implementação default de Equals() dois objetos são iguais se apontam para a mesma instância, e não se seus campos contem o mesmo valor.
Antes de ir escovar bit e partir pra uma implementação customizada de Equals() na minha classe Produtos, resolvi dar uma olhada com mais calma na segunda assinatura do Distinct(), aquela que recebe uma instância de IEqualityComparer. Esta interface contem dois métodos: bool Equals(<T>, <T>) e int GetHashCode(). “Morreu”, pensei eu. “É só implementar a interface e na implementação do Equals(<T>,<T>), comparar os campos das instâncias recebidas”. Minha implementação ficou assim:
public class ComparadorProdutos : IEqualityComparer<AcessoDados.Entidades.Produto>
{ 
    public bool Equals(AcessoDados.Entidades.Produto p1, AcessoDados.Entidades.Produto p2) 
    {
        // Trata os casos nos quais há objetos nulos 
        if(p1 == null && p2 == null) return true;
        if(p1 == null && p2 != null) return false;
        if(p1 != null && p2 == null) return false;
        // Se nenhum dos dois é nulo, retorna true se os campos “importantes” contém o mesmo valor
        return 
        (
            p1.CodigoProduto == p2.CodigoProduto &&
            p1.DataInicial == p2.DataInicial &&
            p1.DataFinal == p2.DataFinal &&
            p1.CodigoNCM == p2.CodigoNCM &&
            p1.Descricao == p2.Descricao &&
            p1.UnidadeMedida == p2.UnidadeMedida &&  
            p1.AliquotaICMS == p2.AliquotaICMS && 
            p1.AliquotaIPI == p2.AliquotaIPI &&
            p1.ReducaoBaseCalculoICMS == p2.ReducaoBaseCalculoICMS &&
            p1.BaseCalculoICMSSubstTrib == p2.BaseCalculoICMSSubstTrib &&
            p1.IdCliente == p2.IdCliente  
        );
    }

    // Retorna qualquer coisa em GetHashCode() pq não vou usar esse trem mesmo
    public int GetHashCode(AcessoDados.Entidades.Produto p)
    {
        if(p == null) return -1;
        else return p.GetHashCode();
    }
}
Na implementação do GetHashCode() coloquei qualquer leseira, porque o que o Distinct() ia usar mesmo era o Equals(). Testei com um programinha console, tudo ok, agora é só chamar o Distinct() passando uma instância de ComparadorProdutos.
// 2ª tentativa: Passando uma implementação de IEqualityComparer para Distinct() 
List<Produto> listaSemRepetidos = listaProdutos.Distinct(new ComparadorProdutos());
Torre de Londres - Show!!!E de novo voltaram *todos* os registros. Caraca. Testei de um lado, do outro, volta pra aplicaçãozinha console pra ver se o Equals() não tem alguma falha, e tudo ok. Só não funciona. Finalmente entreguei os pontos e fui consultar A Fonte De Toda A Sabedoria. E realmente encontrei um artigo “LINQ: Distinct() does not work as expected”, de outro sofredor que passou pela mesma novela. Mas antes de recorrer ao Google, ele colocou um breakpoint em cada um dos métodos da sua implementação de IEqualityComparer e viu que, em vez de chamar Equals(), o método Distinct() chama GetHashCode() para verificar se os objetos recebidos são iguais!!! Se os objetos tem o mesmo valor para GetHashCode() então eles são considerados iguais, e é assim que o Distinct() separa objetos "repetidos" e "únicos". Ele até cita exemplos de boas implementações para o GetHashCode(), mas a melhor dica quanto a isto é que o próprio .NET Framework já fornece uma boa implementação: a usada para tipos anônimos. Então na classe ComparadorProdutos mudei a minha implementação do GetHashCode() para retornar o hash code de um tipo anônimo com os campos da classe Produto que eu queria comparar:
    public int GetHashCode(AcessoDados.Entidades.Produto p) 
    {
        if(p == null) 
            return 0;
        else 
            return new 
            {
                p.CodigoProduto,
                p.DataInicial,
                p.DataFinal,
                p.CodigoNCM,
                p.Descricao,
                p.UnidadeMedida,
                p.AliquotaICMS,
                p.AliquotaIPI,
                p.ReducaoBaseCalculoICMS,
                p.BaseCalculoICMSSubstTrib,
                p.IdCliente 
            }.GetHashCode();
    }
Se duas instâncias da classe Produto tem os mesmos valores nestes campos, seus hash codes serão iguais. E aí meu Distinct(new ComparadorProdutos) finalmente funcionou.

Cris me ajudando na redação do post no Galeão
Cris me ajudando na redação do post no Galeão

É, nada como 6 horas de aeroporto pra gastar…
[]s,
GB

quarta-feira, 22 de junho de 2011

Contando e apagando linhas no Azure Storage Emulator

Eu *sei* que a gente não deve fazer isto. Estamos importando lotes relativamente grandes de linhas para tabelas dentro do Storage Emulator do Azure. Durante os testes, várias vezes temos que contar as linhas importadas durante a execução da importação, e apagar as linhas das tabelas entre os testes. O certo seria fazer isto usando a API de acesso ao Azure Table Storage (ATS), mas é lento e tem que lidar com o tal do continuation token toda vez que a tabela tem mais de 1.000 linhas. Então criei uma função e uma procedure no banco do Storage Emulator pra facilitar minha vida.

O que a gente não deve fazer é acessar diretamente o BD do Storage Emulator. Ele é um BD simples, mas é sempre não-recomendado acessar as estruturas internas de qualquer ferramenta, pois elas podem mudar de uma versão pra outra, e aí seus "caminhos alternativos" (também conhecidos como "bacalhaus") páram de funcionar.

Todas as "tabelas" que você cria no Storage Emulator são guardadas na verdade em uma única tabela chamada "TableRow" no banco instalado pelo Storage Emulator. Este banco deve ter um padrão de nomenclatura, na minha máquina o nome dele é "DevelopmentStorageDb20090919". Ou é o dia da release do SDK do Azure, ou é o dia que uma filha minha quebrou o braço - ultimamente parece que eu vivo no hospital consertando essas criaturas.

Bem, a estrutura da tabela é:

CREATE TABLE [dbo].[TableRow](
   [AccountName] [varchar](24) NOT NULL,
   [TableName] [varchar](63) NOT NULL,
   [PartitionKey] [nvarchar](256) NOT NULL,
   [RowKey] [nvarchar](256) NOT NULL,
   [Timestamp] [datetime] NOT NULL,
   [Data] [xml] NULL,
)

A coluna TableName define a que "tabela" do ATS a linha pertence, PartitionKey e RowKey você já sabe o que é senão provavelmente não estaria lendo esse post, e Data (veja o tipo: xml) contém uma "tripa" XML com os valores para cada coluna da tabela.

Aí fica fácil. A função pra contar linhas de uma tabela do ATS é:

CREATE FUNCTION NumeroLinhas(@nomeTabela varchar(200))
RETURNS int
AS
BEGIN
 RETURN (SELECT COUNT(*) FROM TableRow (NOLOCK) WHERE TableName = @nomeTabela)
END
GO

A procedure para apagar os registros de uma tabela é:

CREATE PROC ApagaRegistros(@nomeTabela varchar(200))
AS
 DELETE FROM TableRow WHERE TableName = @nomeTabela
GO

Agora você pode contar as linhas de uma tabela no Storage Emulator executando

SELECT dbo.NumeroLinhas('Produtos')

e apagar as linhas desta tabela com

ApagaRegistros 'Produtos'

Bem mais rápido que fazer pela API. Claro que "só faça isso em casa": no seu ambiente de testes.



PS: Não é um cenário comum, mas as funções não levam em conta quando há mais de uma conta registrada. Para estes casos, o "WHERE" na função e na procedure devem fazer referência também à coluna "AccountName", para que seja especificada a conta na qual as operações devem ser feitas.

sexta-feira, 17 de junho de 2011

Porque não vou comprar o WinC++ na madrugada do seu lançamento

Humm… Sexta-feira. 17:50. Silencio em casa, as meninas ainda não chegaram da escola. Estou lendo uma thread de discussão sobre o WinC++ – a implementação que a Microsoft irá lançar da nova versão do C++.

Após ler a thread, que – como sempre em discussões de linguagens, desde o VB x Delphi – descambou para tons um pouco mais irritados, dado que o assunto sempre é discutido com um fervor beirando o religioso, enviei os pontos principais com meus comentários para nossa lista interna, que reproduzo aqui:

Parece só geek tentando mostrar que conhece mais que o outro. Discutir linguagem de programação pra mim é igual discutir qual é o tipo de calçado que vai otimizar sua ida à padaria comprar pão. Se você mora na Sibéria, tem que ter uma bota quente e anti-derrapante; se você mora em Nápoles e a padaria fica do outro lado do Vesúvio você tem que ter uma bota a prova de fogo. Mas fora essas situações específicas, qualquer calçado que você escolher dá conta do serviço. As questões importantes são outras: como eu chego na padaria? Ela tá aberta a essa hora? E etc.

Se linguagem fosse *o* fator decisivo no desenvolvimento  de projetos, desenvolvedor ganhava mais que gerente de projetos. Vai vir um C++ novo, vai ter o seu nicho, mas se 95% das empresas que desenvolvem software adotarem C++ novo no lugar das suas atuais linguagens de programação, duvido que vão aumentar significativamente o desempenho de entregas. Agora se investirem em processo e gerenciamento deste processo, aí sim eu aposto que vai haver ganhos.

Isso daí também me parece a Microsoft fazendo o que ela faz melhor: barulho pra vender. Lembram das filas nas lojas toda vez que a MS lança uma versão nova do Windows? Há vários debates sobre a qualidade técnica da M$, mas acho que ninguém duvida da capacidade de marketing dela...

No artigo do The Register sobre o C++, uma coisa me chamou a atenção. “E enquanto novas linguagens nasceram desde então, C++ permaneceu. Java é a única a ganhar em popularidade […]”. Realmente, estas duas linguagens são que nem o U2: agradam a mais de 20 anos, continuam agradando, e parece que continuarão. O que faz uma linguagem de programação ser bem sucedida? Vamos ver alguns pontos:

  • Facilidade de aprendizado e simplicidade no uso. Grande parte dos desenvolvedores são, em parte ou totalmente, auto-didatas. Quero abrir a IDE, pegar uns walkthrough’s no Google, começar com o “Hello World” e rapidamente estar fazendo minha agendinha telefônica, com tela Web e acesso a BD.
  • Riqueza de recursos. “Linguagem de programação” hoje não é só a linguagem em si. E a IDE, o quê ela provê de recursos? É fácil instalar a aplicação, depois que ela está pronta? As bibliotecas são ricas, ou tenho que construir um monte de coisa “na mão”? ORM? TDD? ALM?
  • Portabilidade. Nesse mundão sem porteira de hoje, tem hardware de tudo que é jeito. Tem um monte de sistemas operacionais. Quer um dos fatores de sucesso do Java? Taí.
  • Marketing. Bem, isso não é só pra linguagem de programação, né?

bulabulaNo final das contas, a “corrida” entre as linguagens de programação é como aquela brincadeira de pular carniça que a gente fazia quando era criança. X implementa 1, 2 e 3 na sua linguagem; Y vai e copia 1 e 2, implementa 3 melhorado, e ainda coloca 4 e 5. X atualiza sua linguagem pra colocar 6, além de incluir as últimas novidades de Y (sempre com outro nome ;-). E por aí vai… Vide Java e Microsoft.NET:

  • Olhando mais de perto para alguns aspectos de arquitetura do .NET:
    • Common Language Runtime: ambiente de execução das aplicações .NET. (Java: Java Virtual Machine).
    • MSIL: Código intermediário, independente de harware, resultante da compilação das linguagens de alto nível. (Java: bytecode)
  • Agora o Java:
    • É um C++ simplificado, portável e com ótima penetração de marketing no mundo open source.
    • Não sou especialista em Java, mas certamente a linguagem deve ter puxado conceitos bem sucedidos de outras.

Isto é lógico. É esperado. E, de várias formas, é bom.

Ou seja: legal que vem aí um novo C++. Espero que ele seja mais fácil de aprender e simples de usar do que seu predecessor. O WinC++ certamente incorporará a riqueza de recursos das outras linguagens do .NET Framework. Com a popularização dos tablets e os investimentos da Microsoft no Windows Phone, certamente veremos algum mecanismo para aumentar a portabilidade desta linguagem. E o marketing, esse não tenho dúvidas: será bem feito. Até eu já estou fazendo marketing pra ela…

Mas essa linguagem vai mudar minha vida? Hard to believe. Vou ficar sentado confortavelmente no meu C# pra esperar no que vai dar (acredite ou não, o trocadilho apareceu sem querer), e na próxima vez que eu chegar em casa e as meninas ainda não tiverem chegado da escola, quem sabe eu não faço o meu “HelloWorld.wincpp”… :-)

[]s,
GB

Isso é uma sujeira no monitor ou é o FireFox?

Ao testar uma aplicação Azure nossa em vários browsers, abri a mesma no FireFox. Então vi uma “sujeirinha” no monitor e estou distraidamente mexendo na aplicação com uma mão e tentando limpar a sujeirinha com a outra:

sujerinha-fechadaTão vendo a “sujerinha” ali na barra superior da janela?

“Uai”, pensei. “Essa sujeirinha não sai nem com cuspe no dedo?” (depois de fazer a referida nojeira). Então levei o mouse até lá e olha a surpresa ao clicar na sujeira:

sujerinha-aberta
A “sujeirinha” abriu!!!

Poizé, a "sujeirinha” era o atalho pra o “Grupo de abas” do FireFox. Nem sei pra quê funciona, mas ô interfacezinha mal-feita essa! Nessa era de WIndows 7, iPhone, WPF e SilverLight, o pessoal voltou a bolar mecanismos de interface meio “mirabolantes”. Minha política pra construção de telas é: atenha-se ao padrão. Sim, tem situações específicas nas quais uma interface mais bem bolada encanta e facilita, mas na maioria dos casos o usuário vai precisar de menos treinamento (e menos helpdesk) se ele encontrar as funcionalidades nas suas telas da mesma forma que ele as encontra em Word, Excel, e outras aplicações que ele está acostumado a usar. Cuidado pra sua interface não ficar parecendo uma sujerinha no monitor… ;-)

[]s,
GB

quarta-feira, 27 de abril de 2011

Como usar arquivos de teste durante testes unitários no VS 2010

Estamos testando a importação de arquivos de NFe's (Notas Fiscais Eletrônicas). Para construir testes que façam isto, precisei de um arquivo contendo uma NFe, e também dos XSD's pra validação do XML da NFe.

Quando você executa testes dentro do VS, ele cria um diretório chamado "TestResults" debaixo do diretório da sua solução, e pra cada execução de um teste, cria um subdiretório chamado "usuário_máquina AAAA-MM-DD hh_mm_ss", e dentro desse subdiretório cria um outro chamado Out, e é dentro desse Out que ficam os assemblies com o código do seu teste e o código a testar. Então qualquer arquivo que seja necessário durante os testes tem que ser copiados para este diretório.


Estrutura dos diretórios de execução dos testes, com os arquivos de testes já copiados para lá.

Achei algumas formas de fazer isso (Test > Edit Test Settings > blablabla.testsettings, opção Deployment; ou  [Propriedades do Arquivo] > Build Action = Content e Copy To Output Directory = Copy Always), mas a única coisa que funcionou mesmo foi o uso do atributo [DeploymentItem]:


Como usei em um método de teste, os arquivos só são copiados para o diretório de execução dos testes quando este método rodar. Mas pelo Help da MSDN, ele pode ser usado também em classes, o que (suponho) faz com que os arquivos sejam copiados para o diretório de execução para qualquer teste da classe.

Timeout em testes unitários no Visual Studio 2010

Nossos testes de unidade na aplicação Azure que estamos construíndo tem que limpar um monte de tabelas no Storage Emulator, que não é nenhuma Brastemp em termos de performance. Em algumas situçãoes a limpeza destas tabelas levava quase 6 segundos, e sendo 10 segundos o tempo máximo default para a execução de um teste, vários testes estavam gerando erro de timeout.

Uma possível solução é aumentar o tempo máximo de execução. No menu Test > Edit Test Settings > (sua configuração de testes.testsettings), selecione a opção Test Timeouts, e na opção Mark an individual test as failed if its execution time exceeds forneça o tempo que você deseja para timeout dos testes.

PS: Não faça como eu, que quis colocar 30 segundos e coloquei 30 minutos.
Só percebi quando estava escrevendo este post...

terça-feira, 19 de abril de 2011

Treinamento Azure: MSDN Code Sample for Cloud

Bem, este não é um "Treinamento", mas vale a pena colocar nas referências de treinamento para Azure: A MSDN disponibiliza uma biblioteca de código-fonte, e há uma seção para o Azure: http://code.msdn.microsoft.com/site/search?f%5B0%5D.Type=Platform&f%5B0%5D.Value=Cloud. Tem coisas muito interessante lá, e o legal é que dá pra ver como elas são implementadas. Particularmente vou dar uma checada no projeto CSAzureTableSt​oragePaging, que mostra como fazer paginação em cima de dados armazenados no ATS (em aplicações MVC). Teremos que fazer isto em breve...

segunda-feira, 18 de abril de 2011

Teste Unitário de Aplicações 64 bits no Visual Studio

Tivemos que ler um arquivo Excel na nossa aplicação Azure. Sendo uma aplicação Azure, ela necessariamente tem que ser 64 bits.

(…huuummmm… Mentira. Você pode ativar a execução de aplicações 32 bits em um IIS 64 bits de acordo com esse cara aqui ó)

Ok. Eu achei que uma aplicação no Azure tinha que ser 64 bits, então quando fomos ler um arquivo do Excel, instalamos o Provider OleDb de 64-bits para o Office 2010, que permite coisas muito legais, tais como usar um DataAdapter pra executar um comando tipo “SELECT * FROM $Planilha1” e jogar esses dados dentro de um DataSet.

O problema foi quando executei os testes unitários de acesso ao arquivo Excel. Os testes sempre falhavam com a exceção “The 'Provider=Microsoft.ACE.OLEDB.12.0' provider is not registered on the local machine”. Isto porque por default os testes unitários no Visual Studio 2010 rodam em um ambiente de 32 bits. Como o provider é de 64 bits, ele não é reconhecido pelo código dos testes.

Imaginei as coisas mais malucas pra fazer estes testes rodarem, como p.e. fazer uma aplicação console 64 bits que chamasse o componente a testar, disparar essa aplicação de um teste, e depois verificar os resultados. Perdi uma manhã inteira tentando executar essa aplicação console de forma sincrona, pois eu só podia checar os resultados depois que o processo acabasse.

(“PS”: Você sabia que é um parto em .NET fazer um código que dispara um processo e espera pelo fim da execução desse processo? Poizé, eu não.)

Mas no final das contas, o Visual Studio já tem suporte para execução de testes em 64 bits. Nas configurações de teste (menu Test > Edit Test Settings > [sua configuração de teste].testsettings), você pode ir em “Hosts” e na opção “Run tests in 32 or 64 bit process” selecionar “Run tests in 64 bit process on 64 bit machine”. Se seu Windows é de 64 bits, o ambiente no qual os testes são executados será também de 64 bits. Assim os testes usando o provider de 64 bits rodaram sem problemas.

image

domingo, 6 de fevereiro de 2011

SocketSniff: Examinando o tráfego em localhost

Estava eu debugando uma página em AJAX e precisei ver o que saía e chegava do browser. Tem o Fiddler, mas você tem que colocar ele como proxy HTTP do browser, e tem mais algumas opções, só que tem que fazer isso, aquilo, e o outro… Aí encontrei essa ferramentazinha show: NirSoft SocketSniff. Uma aplicação Windows que se enfia no meio da pilha TCP (mais especificamente na WinSock.dll), e mostra os pacotes que estão passando por lá. Simples e efetivo. Bom pra debugar chamadas a serviços WCF também.

PS. Curiosidade: você sabe o que é WinSock.dll? Já programou pra ela? A gente usava no Windows 3.11 pra fazer comunicação TCP peer-to-peer.

PS2. “Windows 3.11”? Ih, deixa pra lá, que isso vai longe…

terça-feira, 25 de janeiro de 2011

Treinamento Azure: Windows Azure Training Kit

*Excelente* recurso de formação no Windows Azure. Sugiro como o primeiro material de estudo para quem está iniciando na área, mas mesmo aqueles que conhecem a plataforma podem tirar proveito destes treinamentos, ao tomar conhecimento de recursos “não tão famosos”, mas muito úteis, do Azure. Foco especial nos hands-on-labs, que mostram de forma rápida e pontual como executar várias tarefas de desenvolvimento e deploy no Azure.

Windows Azure Platform Training Kit - December 2010 Update
http://www.microsoft.com/downloads/en/details.aspx?FamilyID=413e88f8-5966-4a83-b309-53b7b77edf78&displaylang=en

Treinamento Azure: Security Essentials

Série de webcasts cobrindo diversos aspectos de segurança no Azure, desde uma visão geral das tecnologias de segurança no Azure, até detalhes avançados tais como autenticação claims-based e integração com AD, segurança no Azure Storage Services e SQL Azure, e melhores práticas de segurança para aplicações rodando no Azure.

Windows Azure Platform Security Essentials Series
http://www.msdev.com/Directory/SeriesDescription.aspx?CourseId=168

Treinamento Azure: Journey to the Cloud ISV Webinar Series

Esta série de webcasts mostram a visão da iniciativa de Computação na Nuvem do ponto de vista dos ganhos que o cloud computing pode trazer para uma organização. São webcasts curtos (15-30 minutos), com bom conteúdo para quem quer vender serviços de computação na nuvem, seja para clientes externos ou como estratégia de obtenção de recursos dentro de sua própria organização.

Journey to the Cloud ISV Webinar Series
http://www.msdev.com/Directory/SeriesDescription.aspx?CourseId=167

segunda-feira, 17 de janeiro de 2011

Linq: Join em Múltiplas Colunas

image

A consulta Linq que retorna as notas fiscais e respectivos itens é:

var resultado = 
(
from nf in contexto.NotasFiscais
join inf in contexto.ItensNotasFiscais
on new { campo1 = nf.CNPJ, campo2 = nf.Numero } equals { campo1 = inf.CNPJ, campo2 = inf.NumeroNF }
select new { nf.CNPJ, nf.Numero, inf.NumeroItem }
).ToList();


Você pode usar tipos anônimos para implementar a condição de ligação usando várias colunas. O pega é que se você usa tipos anônimos, as propriedades dos objetos dois lados do operador equals na cláusula join tem que ter o mesmo nome, ou será gerado um erro de compilação (The type of one of the expressions in the join clause is incorrect.  Type inference failed in the call to 'Join'). Faz sentido quando você pára pra pensar, mas não evita você ficar 2 horas olhando aquele join pensando o que está errado, até descobrir a causa…

sexta-feira, 14 de janeiro de 2011

Repositório de Dados no Windows Azure: Table Service ou SQL Azure?

O Azure oferece basicamente 2 tipos de repositórios: o ATS (Azure Table Service) e o SQL Azure. O ATS é um conjunto de tabelas de estrutura “frouxa”, o que significa que, na mesma tabela, você pode gravar linhas com estrutura de colunas distintas. A restrição téorica de tamanho no ATS é de 100 TB; o ATS é bem mais barato que o SQL Azure em termos de custo de espaço de armazenamento usado, e ainda tem um esquema de particionamento dos dados voltado para aumento da performance de acesso – os dados de uma mesma tabela podem ser divididos em partições, e estas partições podem estar em máquinas diferentes, efetivamente paralelizando o acesso aos dados – e isto tudo é feito automagicamente, quando o Azure “nota” que uma partição está sendo muito acessada e/ou ficou muito grande.

Isto tudo torna o ATS uma opção atrativa para quem está modelando a camada de dados de uma aplicação no Azure, MAS… É, sempre tem o tal do “MAS”. Leia a seguir alguns “MAS” que encontramos no nosso trabalho com o ATS.

1. O ATS é bem mais barato, que o SQL Azure, mas é mais doído de usar.

O custo atual de 1GB de dados no Azure Table Service é de US$ 0,15/mês. Um BD de 1GB no SQL Azure custa US$ 9,99/mês. Mas o ATS tem um monte de restrições que tornam a produtividade baixa e o código complicado. Por exemplo:

  • Linq to ATS não tem JOIN, GROUP BY, subqueries e um monte de outras coisas às quais já estamos acostumados. Olha a “listinha” dos operadores não suportados aqui. Você ou vai ter que trazer os dados do ATS pra dentro da sua aplicação e usar Linq To Objects, com o prejuízo óbvio de trafegar um monte de dados que não serão necessários, ou inventar algum meio maluco de fazer isso de dentro do ATS, usando p.e. duplicação de dados.
     
  • O ATS não suporta transações.
    - Tem um modo restrito chamado de Entity Group Transactions (EGT), mas as restrições são severas a ponto de torná-lo inaplicável, dependendo da sua aplicação. P.e., este modo é restrito a transações envolvendo no máximo 100 entidades (100 “registros”), o que pode torná-lo inútil em uma aplicação de importação de arquivos, aonde podemos inserir um monte registros de uma vez em uma transação.

2. Se você escolheu o ATS, gaste espaço em disco sem pena, se isso for facilitar sua programação.

Sendo o custo atual de 1GB de dados no ATS de US$ 0,15/mês, é ridículo não criar desnormalizações. Se você *duplicar* seus dados, seu custo passará de 15 para 30 cents/mês por GB. Em 10GB de dados, você irá pagar a mais 3 dólares/mês. Por exemplo:

  • Como Linq to ATS não tem suporte a JOIN’s, se você tem um relacionamento 1-N que é muito acessado, grave os dados como uma única entidade, repetindo, para cada entidade do lado N, os valores da entidade do lado 1. Exemplo: você tem uma tabela de Vendas e de DetalheDasVendas; em Vendas tem os dados de cada venda, e em DetalhesDasVendas tem a lista de itens vendidos em cada venda. Considere criar uma única tabela VendasDetalhes, com as colunas das duas tabelas reunidas nela:

image

      Dá dor na alma ver uma modelagem desta, e realmente ela exige um pouco mais na hora de codificar o inclui-altera-exclui. Mas a flexibilidade de pesquisa que ela dá é muito maior do que a modelagem normalizada. Por exemplo:
      - O JOIN é ridiculamente fácil de implementar, porque, na verdade, ele já está implementado. 
      - O tráfego de dados é minizado (minha preocupação é com desempenho e não com gasto, pois tráfego de dados no ATS também é na casa de cents/GB/mês).
      Como exercício, tente pensar no algorítmo sem usar JOINs para ler das 2 tabelas as vendas de 2010 que incluíram “BANANA” como item. Ok, dá até pra implementar. Agora lembre que você tem cem mil registros de vendas em 2010, cada um com uma média de 5 itens, e faça um chute alto de quantas idas-e-vindas a aplicação teve que fazer no ATS para recuperar os dados. Em seguida, faça essa conta para a tabela “aglutinada” e veja a diferença. A aplicação fica mais leve nas consultas, e você perde menos tempo desenvolvendo algoritmos de JOIN que deveriam ser feitos pelo repositório de dados, e não por seu código…

E há outras aventuras mais de uso do ATS. Não é que o ATS seja ruim; ele, como toda ferramenta deve ser, é bom para a finalidade para a qual foi projetado: listas massivas de dados que exigem performance no acesso. Ele não foi construído para ser um BD relacional. Talvez a Microsoft inclua funcionalidade para tal em versões futuras; mas meu conselho é, se possível, usar o SQL Azure para suas aplicações transacionais, e deixar o ATS para aquela coleção de dados que você sabe que será gigantesca. “SQL Azure se possível” significa que o limite de tamanho máximo do SQL Azure tem que ser respeitados. As opções atuais são de bancos de no máximo 1, 10 ou 50GB. Mas lembre que as aplicações podem usar os dois mecanismos, então nada impede de uma aplicação de digitalização de documentos guardar seus dados “normais” em uma instância do SQL Azure, e os documentos digitalizados no Azure Blob Service. Vai ser seu trabalho, como arquiteto da aplicação, decidir a “dose certa” a usar de cada um dos mecanismo de armazenamento de dados que o Azure oferece.

Microsoft SQL Azure Home Page: http://www.microsoft.com/en-us/sqlazure
Portal de informações, vídeos, artigos e outros sobre o SQL Azure.

MSDN: Using the Windows Azure Storage Services: http://msdn.microsoft.com/en-us/library/ee924681.aspx
Links para artigos com descrição dos 4 mecanismos de armazenamento nativos do Windows Azure: Table Services (tabelas), Blob Services (dados binários), Queue Services (filas) e os Windows Azure Drives.