Asp.Net MVC - Enviando dados para o servidor com KnockoutJS e JQuery    

Olá pessoal.

Já faz um tempo que falei de KnockoutJS. Hoje vou falar mais uma vez para tirar uma dúvida recorrente aqui no blog: Como enviar dados do meu modelo KnockoutJS para uma Action do MVC.

Vou fazer um exemplo simples e objetivo. Como o título do post diz, a ideia é mostrar apenas o envio dos dados para o servidor, como obter do servidor não será abordado (quem sabe em outro post).

Vamos explicar como será a aplicação na imagem abaixo:

Imagem 1 – Tela de cadastro.

Imagem 1 – Tela de cadastro.

A tela é divida em três partes:

  1. Na primeira parte é exibido um formulário onde você pode digitar dados de contato e clicar em adicionar
  2. Na segunda parte são exibidos os contatos já adicionados. Você pode clicar no link Remover no final de cada linha para remover o item já adicionado.
  3. Na terceira parte você pode clicar no botão Salvar. Apenas após clicar nesse botão seus dados adicionados a lista serão enviados para o servidor.

A aplicação é bem simples, mas é o suficiente para mostra como enviar dados do JavaScript para o servidor.

Vamos agora ver como a tela foi feita.

   1:  <h3>Cadastro:</h3>
   2:   
   3:  <label for="nome">Nome</label>
   4:  <input id="nome" name="nome" type="text" data-bind="value:Nome" /><br />
   5:  <label for="telefone">Telefone</label>
   6:  <input id="telefone" name="telefone" type="text" data-bind="value:Telefone" /><br />
   7:  <label for="email">E-mail</label>
   8:  <input id="email" type="email" name="email" data-bind="value:Email" /><br />
   9:  <input type="button" data-bind="click:adicionarContato" value="Adicionar >>" />
  10:   
  11:  <table>
  12:      <thead>
  13:          <th>Nome</th>
  14:          <th>Telefone</th>
  15:          <th>E-mail</th>
  16:          <th></th>
  17:      </thead>
  18:      <tbody data-bind="foreach: Contatos">
  19:          <tr>


  21:              <td><input type="text" data-bind="value:Telefone" /></td>
  22:              <td><input type="text" data-bind="value:Email" /></td>
  23:              <td><a href='#' data-bind='click: MeuModelo.removerContato'>Remover</a></td>
  24:          </tr>
  25:      </tbody>
  26:  </table>
  27:  <input type="button" value="Salvar" data-bind="click:salvarContato" />

Esse é todo o HTML utilizado para fazer a tela acima. Da 1 à 9 crio o formulário para adicionar contatos. Da linha 11 à 26 crio a tabela que é exibido os registros adicionados, e por fim na linha 27 adiciono o botão para salvar no servidor.

Repare que em todos os campos estou adicionando o atributo data-bind do KnockoutJS. Para os botões informo que vou utilizar o evento click para invocar o respectivo método do meu modelo (abaixo). Para os campos (input) informo no data-bind que desejo que o atributo value seja preenchido  com o respectivo valor.

Para utilizar o KnockoutJS, posso fazer o download direto no site http://knockoutjs.com ou apenas utilizar o Nuget. No meu caso vou utilizar o Nuget com o comando abaixo no Visual Studio:

PM> Install-Package knockoutjs

Uma vez executado, todos os arquivos necessários para utilizar o Knockout estarão na pasta de Scripts do meu site:

Imagem 2 – Scripts do Site

Imagem 2 – Scripts do Site.

Também vou adicionar a referência para o Jquery e para a biblioteca do KnockoutJS que acabamos de instalar:

<script type="text/javascript" src="@Url.Content("~/Scripts/jquery-1.7.1.min.js")"></script>
<script type="text/javascript" src="@Url.Content("~/Scripts/knockout-2.2.1.js")"></script>

Agora vamos ao JavaScript necessário para fazer minha aplicação funcionar:

   1:  <script type="text/javascript">
   2:  var MeuModelo = {
   3:      Nome: ko.observable(),
   4:      Telefone: ko.observable(),
   5:      Email: ko.observable(),
   6:      Contatos: ko.observableArray([]),
   7:      adicionarContato: function () {
   8:          this.Contatos.push({ Nome: this.Nome(), Telefone: this.Telefone(), Email: this.Email() })
   9:          this.Nome("");
  10:          this.Telefone("");
  11:          this.Email("");
  12:      }
  13:      , removerContato: function (contato) {
  14:          MeuModelo.Contatos.remove(contato);
  15:      }
  16:      , salvarContato: function () {
  17:          var dados = ko.toJS(MeuModelo.Contatos);
  18:          $.ajax({
  19:              type: "POST",
  20:              contentType: "application/json",
  21:              url: "Home/SalvarContato",
  22:              data: JSON.stringify({ contatos: dados }),
  23:              traditional: true,
  24:              success: MeuModelo.sucesso,
  25:              error: MeuModelo.falha
  26:          });
  27:      }
  28:      ,sucesso: function () { alert("Sucesso");}
  29:      ,falha: function () { alert("Falha"); }
  30:   
  31:      };
  32:   
  33:  ko.applyBindings(MeuModelo);
  34:  </script>

No JavaScript acima estou criando um ViewModel, ou seja, um modelo para gerenciar minha View. Vamos explicar linha por linha:

  • Da linha 3 a 5 estou criando as propriedades observáveis para os três campos do formulário que adiciona os contatos.
  • Na linha 6 estou criando o Array observável que conterá os registros da lista da tela.
  • Entre as linhas 7 e 12 crio o método que será chamado quando for clicado no botão Adicionar. Esse método adiciona os valores atuais das propriedades criadas entre as linhas 3 e 5 ao Array Contatos. Uma vez que o Array muda no JavaScript, a tela atualiza automaticamente. Esse é o KnockoutJS em ação. Para finalizar o método, entre as linhas 9 e 11 eu limpo os campos onde os valores foram digitados.
  • Entre as linhas 13 e 15 eu implemento o método responsável por remover um item do array e atualizar a tela quando clicado no link Remover na lista.
  • Entre as linhas 16 e 29 eu crio o método responsável por enviar os dados ao Servidor. Primeiramente, na linha 17 eu transforme o meu Array observável em um Array puro de JavaScript utilizando o método do KnockoutJS chamado ko.toJS. Em seguida invoco o método ajax do JQuery, invocando o método POST e definido que o conteúdo será do tipo “application/json”. Para enviar os dados ao servidor, eu transformo meus dados em uma string JSON, encapsulando o Array em um campo com o mesmo nome do parâmetro da Action. (Linha 22)
  • Por fim, na linha 33 eu aplico o Binding entre o meu modelo JavaScript e a minha página HTML.

Agora, por último, devemos ver como criar a Action em meu Controller MVC.

É bem simples. Primeiro crio um ViewModel de Contato em meu C#, e em seguida crio uma Action que recebe um um array desse ViewModel:

   1:  namespace Mvc_Knockout.Models
   2:  {
   3:      public class Contato
   4:      {
   5:          public string Nome { get; set; }
   6:          public string Telefone { get; set; }
   7:          public string Email { get; set; }
   8:      }
   9:  }

E a Action:

   1:  [AcceptVerbs(HttpVerbs.Post)]
   2:  public JsonResult SalvarContato(Contato[] contatos)
   3:  {
   4:      foreach (var contato in contatos)
   5:      {
   6:          //Lógica para salvar
   7:      }
   8:      return Json(contatos);
   9:  }

Informo que minha Action receberá um Post, e não um simples Get, pois informei na chamada Ajax que enviaria um Post. Uma vez que os dados estiverem no servidor, você pode utilizar a lógica e ferramenta que achar melhor para persistir, como NHibernate ou Entity Framework.

Veja abaixo o print dos meus dados no servidor após clicar no botão Salvar. (Os mesmos dados que estão na tela da imagem da tela (Imagem 1))

 

Imagem 3 – Dados no Servidor.

Imagem 3 – Dados no Servidor.

É isso pessoal. Por hoje era isso.

Até o próximo.

Fonte do Projeto:

Mvc+Knockout.zip (3,01 mb)

18. February 2013 23:57 by Frederico B. Emídio | Comments (45) | Permalink

O que significa TDD e qual é o seu benefício    

Olá pessoal!

Hoje vou publicar um mini artigo que fiz para a pós-graduação ano passado sobre TDD. Se fosse escrever hoje sobre o mesmo assunto, com certeza falaria de muitas coisas diferentes, mas neste post vou colocar o artigo exatamente como entreguei na faculdade. Provavelmente voltarei a abordar esse tema por aqui, e ai sim colocarei mais informações. Por ser um artigo acadêmico, ele é bem superficial e não técnico. Segue o texto na íntegra.


O que significa TDD e qual é o seu benefício

Fazer software com qualidade não é uma tarefa fácil. Especificações mal definidas e que mudam sempre, tecnologias e conhecimento sempre em evolução e mais uma série de fatores torna a construção de software uma tarefa muitas vezes ingrata. Quando dizemos Software de qualidade, temos que ter em mente que qualidade não é medida apenas por experiência do Usuário ou grau de completude da especificação funcional. Qualidade de Software deve contemplar todos os aspectos do mesmo, como o código. Em código temos vários outros fatores que podemos citar, como legibilidade, complexidade, desempenho e o que mais interessa esse artigo, manutenção. Uma manutenção difícil em código pode acabar com o funcionamento de um sistema e causar perdas inestimáveis.

Teste é a principal ferramenta para alcançar a qualidade de Software. Ao longo de um desenvolvimento de Software, vários tipos de testes são executados, cada tipo é especifico para uma fase do desenvolvimento. Para o escopo do código, o tipo de testes apropriado é o Teste de Unidade ou Unitário, que visa validar cada pequeno trecho de código e conferir que ele está executando conforme esperado. Em um cenário onde a mudança é constante esse risco deve ser mitigado ao mínimo e Test-Driven Development (TDD) ou Desenvolvimento Orientado a Testes, é uma ferramenta que se encaixa perfeitamente nesse cenário. TDD é a prática de desenvolver Testes Unitários antes de desenvolver a unidade, para dessa forma direcionar o desenvolvimento da unidade.

“Código limpo que funciona é o objetivo do TDD. Código limpo que funciona é um objetivo que vale a pena por uma série de razões: Prediz a forma de desenvolver. Você sabe quando terminou, sem a preocupação de uma lista de erros. Permite seu time confiar em você. Melhora a vida do seu usuário. Possibilita aprender do seu código tudo que ele tem para te ensinar.” [1]

TDD prega que você deve codificar o teste do seu código antes mesmo de escrevê-lo, dessa forma, a primeira pergunta que você deve fazer ao iniciar o desenvolvimento não é “Como devo desenvolver este código”, mas sim “Como devo testar esse código”. Um dos benefícios dessa abordagem é que ao pensar nos testes, o desenvolver irá desejar códigos fáceis de testar, dessa forma com os testes prontos, ele será forçado a desenvolver um código limpo, desacoplado, do contrario será muito difícil realizar o teste.

Todo software de qualidade tem a maior parte do seu código testado, métrica essa chamada de Code Coverage. Com o TDD, você tem a certeza que 100% do seu código está testado, pois ele só foi escrito depois que você o testou. Comprovadamente, você gastará menos tempo com código testado ao alterá-lo do que com código sem testes. Código sem teste é legado para o próprio desenvolvedor que o desenvolve.

“Você provavelmente terminará o seu sistema com o mesmo número de linhas de testes que o número de código funcional quando implementar TDD. Para TDD fazer sentido economicamente, você precisa estar apto a desenvolver duas vezes mais linha de código por dia ou a metade de linhas para desenvolver a mesma funcionalidade” [1]. Quando uma pessoa está iniciando em TDD, é normal se questionar apenas com a realidade de ter que desenvolver mais rápido, e isso motiva muitos profissionais a desistirem dessa técnica. Porém, como afirmado anteriormente, o código desenvolvido com TDD é mais limpo, simples e enxuto, logo, no maior número de vezes, o seu código é que vai cair pela metade no número de linhas, e não sua produtividade terá que aumentar.

Ainda que seu código não diminua, em muitos casos você não terá que aumentar a capacidade de velocidade de desenvolvimento, pois se você desenvolver cem linhas de código com testes perderá X tempo para realizar manutenção no mesmo, por outro lado, se você fizer o mesmo número de linhas de código sem testes, o tempo que perderá para fazer manutenção em algum código é imprevisível, mas será sempre maior que X.

Podemos saber que está na hora de começar a desenvolver testes para nosso código no momento que o nível de Stress para realizar uma manutenção nele for alto. Ou seja, quando o desenvolvedor ao deparar com um código necessitado de manutenção o Stress do time aumenta? Então você não sabe o que está falhando. Esse sentimento é comum em projetos sem testes, com TDD o nível de Stress de uma equipe tende a zero, pois o erro é rapidamente explicitado por testes, e você sabe exatamente o momento que a manutenção está completada, pois os novos testes criados antes das alterações estarão passando. Para implementar TDD em uma equipe, comece com indicadores que mostre o nível de Stress ou Medo da sua Equipe. Mostre a evolução desse índice conforme o TDD for sendo adotado.

Todas as práticas utilizadas em testes devem ser adotadas em TDD, como testes isolados, simples, etc; mas com TDD alguns outros passos também devem ser considerados, tais como fazer pequenos passos (baby steps), não ter pressa de fazer um teste passar, etc. Essas são boas práticas que facilitam a vida de pessoas iniciantes no Desenvolvimento Orientado a Testes, mas que devem ser seguidas por todos os desenvolvedores experientes. “Escaladores conservadores têm uma regra de que pelo menos três dos quatro membros do corpo devem estar presos à montanha ao mesmo tempo. Movimentos rápidos onde são tirados dois membros ao mesmo tempo são muito arriscados.” [1]. Essa mesma regra deve ser seguida por desenvolvedores, não ter pressa de pular os pequenos passos ao desenvolver os testes, dessa forma, terá certeza que a maior parte do código está sendo testado, e seu Nível de Medo será praticamente zero ao entregar o software.

Apesar de TDD ser muito aplicado quando o time segue Metodologias Ágeis de desenvolvimento, essa técnica pode e até mesmo deve ser adotada por qualquer tipo de time, como aqueles que utilizam RUP, por exemplo. Com testes, você pode confiar em refatoração, Builds automáticos, e com TDD você pode confiar, além de tudo isso, que seu time entrega software não só que roda, mas que faz o que é certo, pois você codificou o que ele precisava fazer de acordo com um teste previamente escrito, e não testou algo que já estava escrito sem garantia que ele fizesse o que deveria.

Com TDD, concluí-se que é possível fazer “código limpo que funciona” e que é possível ser agradável criar software de qualidade sem altos níveis de Stress.

[1] Beck, Kent; Test-Driven Development by Example. Addison Wesley,2003


É isso. Até o próximo post.

31. January 2013 09:21 by Frederico B. Emídio | Comments (38) | Permalink

Introdução a Asp.Net WebAPI    

No período de inatividade do Blog, muitas coisas aconteceram no Asp.Net, inclusive uma nova versão foi lançada com muitas melhorias. O Diagrama que mostrei neste post já está desatualizado e vou tentar correr atrás do tempo perdido para falar de alguns temas por aqui.

Introdução

O Asp.Net já está bastante consolidado com uma arquitetura sólida e extensível, permitindo o desenvolvimento seguindo diversos padrões e paradigmas. Como mencionado no post citado, sobre o Asp.Net existem varias outras camadas que possibilitam diferentes abordagens para desenvolver diferentes tipos de projetos, sendo que todas elas são válidas de acordo com o objetivo da aplicação. De forma rápida podemos citar algumas destas formas de desenvolvimento que o Asp.Net fornece: Web Forms, Asp.Net MVC, Dynamic Data, Web Pages e agora também o Asp.Net WebAPI. É exatamente sobre essa última forma que falaremos ao longo desse post . Farei aqui apenas de uma introdução teórica, e nos próximos posts mostrarei alguns exemplos.

É comum, quando ouvimos falar de tecnologias Web, pensarmos em sites ou sistemas que rodem em Browser, porém, Web é muito mais que isso. Arquiteturas de Nuvem, como o Azure da Microsoft ou o AWS da Amazon são bons exemplos de cenários onde hospedamos serviços na Web, com o objetivo de serem consumidos de qualquer origem, e não apenas de sites. Esse tipo de arquitetura orientada a serviço (SOA na sigla em inglês) além de fornecer um grande ganho em escalabilidade, sazonalidade, especialização, entre outros benefícios, pode ser consumido por muitos tipos de aplicações, como Windows, Cobol, Console ou qualquer outra tecnologia que não rode em Browser e necessite de dados centralizados e serviços dedicados.

Um bom exemplo de aplicação que consome serviços na Web em grande escala, e que é muito popular nos dias de hoje, são os aplicativos para SmartPhone. A grande maioria desses aplicativos está sempre conectada, consumindo serviços e exibindo-os para o usuário como se eles fossem locais.

Imagine agora você ter uma ótima ferramenta para prover serviços para toda essa gama de aplicativos, seguindo sempre uma mesma arquitetura e o mesmo padrão. Foi pensando nisso que a Microsoft criou o Asp.Net WebAPI, que nos dá todas as ferramentas e facilidades necessárias para conseguirmos alcançar todos esses objetivos

Como o próprio nome diz, WebAPI é forma que a equipe do Asp.Net nos dá para disponibilizar uma API (Application programming interface) hospedada na Web, ou seja, abrir uma porta na sua aplicação para que outras aplicações e serviços possam interagir com ela por meio de serviços HTTP. Para quem é desenvolvedor Windows esse conceito é muito comum, pois muitas vezes dependemos de APIs do sistema operacional para desenvolver alguma funcionalidade mais avançada.

Entendendo Asp.Net WebAPI

O Asp.Net WebAPI, diferente de outras tecnologias Web da Microsoft, como o WebForms, Asp.Net MVC ou WebPage, não é construído diretamente sobre a plataforma Asp.Net, ele é, na realidade, uma abstração feita sobre o Asp.Net MVC 4. Isso quer dizer que além de você fazer uso de toda a tecnologia compartilhada pela base do Asp.Net, você terá em mãos também toda a tecnologia especializada e já consolidada do Asp.Net MVC. Na prática, um projeto Asp.Net WebAPI é um projeto Asp.Net MVC com itens/arquivos a mais. Ele contém toda a configuração padrão de um projeto MVC normal. Isso é muito proveito, uma vez que você poderá fazer um site e no mesmo projeto disponibilizar serviços padronizados.

Encontramos no Asp.Net WebAPI termos muito comuns de aplicativos Asp.Net MVC simples. Por exemplo, nossos métodos são expostos através de Controllers e são alcançados através de Rotas, as diferenças estão basicamente em classes bases, que são específicas para cada camada.

Os Controllers do WebAPI, por exemplo, herdam de ApiController ao invés de simplesmente Controller, que é o caso dos controllers de uma aplicação padrão MVC. Por sua vez, os controllers do WebAPI não retornarão Views como ActionResult, pois não é esperado que serviços retornem telas ou relatórios, mas apenas dados. As Rotas também têm diferenças: sua configuração se dá através do método routes.MapHttpRoute ao invés do routes.MapRoute, dessa forma permitindo que o mesmo aplicativo/site tenha rotas configuradas para o WebAPI e para Controllers MVC padrões.

É interessante saber também que com o WebAPI você não precisa ter uma infraestrutura Web disponível para sua aplicação, ou seja, você não precisa do IIS para disponibilizar seus serviços REST em um protocolo HTTP. Com poucas linhas de código você consegue expor serviços em um Console Application, ou outro tipo de aplicação .Net, fazendo o que é chamado de Self-Host WebAPI. Apenas baixando um pacote no NuGet você já tem tudo que precisa para criar serviços sem IIS, apenas com o .Net instalado.

O que são serviços REST?

Você deve ter reparado que comentei algumas vezes de serviço REST até agora no post. Isso porque o WebAPI foi criado principalmente para geração de serviços REST. Apesar de ter citado o termo algumas vezes, ainda não deixei claro o que seria esse tipo de serviço. Tentarei agora fazer uma breve explicação desse conceito de serviço, já muito utilizado por diversos desenvolvedores, mas que é sempre bom ser esclarecido.

Já ficou evidente que o principal ganho ao desenvolver projetos com Asp.Net WebAPI é a facilidade em desenvolver serviços REST (Representational State Transfer). Apesar desse não ser o único tipo de serviço web possível com WebAPI, nitidamente essa é o tipo de serviço recomendado pela Microsoft ao utilizar WebAPI, como veremos a seguir. Para desenvolver serviços RPC (Remote Procedure Call), por exemplo, os WebServices ASMX ou WCF já são bem aptos e fáceis, enquanto que, por outro lado, desenvolver serviços REST em ASMX ou WCF não é tão simples como com WebAPI.

Para quem não sabe, a grande diferença entre serviços REST e RPC é que enquanto em REST os serviços expõem seus dados como recursos em URI e os clientes interagem com esses dados através de verbos HTTP, em RPC os serviços expõem diferentes métodos organizados de forma aleatória em uma ou mais URIs.

Por exemplo, um serviço REST teria sempre a mesma URI no formato MeuEndereco/MeuDado e de acordo com o verbo HTTP (POST, GET, PUT, DELETE), a aplicação poderia responder as requisições do Cliente sobre o “MeuDado”. Ou seja, o cliente pode obter um registro através do verbo GET ou salvar o mesmo registro com o verbo Put ou Post.

Por outro lado, no RPC, se fosse implementado em ASMX, seria algo assim MeuEndereco/MeuServico/ObterDado, MeuEndereco/MeuServico/SalvarDado, MeuEndereco/MeuServico/OutroMetodo, etc. Perceba que o final do endereço é um Método, e não um recurso, como é o caso do REST.

Serviços REST são muito úteis para expor serviços com a função CRUD sobre um dado, pois os principais verbos HTTP servem exatamente como os principais comandos SQL (SELECT, INSERT, UPDATE e DELETE). Mas é importante ter em mente que serviços REST não estão restritos a apenas operações CRUD.

Quando criamos um serviço REST, respeitamos todas as formas de comunicação estabelecidas pelo protocolo HTTP. Visitando esse post você pode ter uma noção de como funciona o HTTP. Além disso, você pode encontrar todos os códigos de Status pré-definidos em diversos sites da Web. No .Net existe o enum HttpStatusCode, no namespace System.Net o nome e o código de todos esses Status.

Também precisamos ter em mente ao modelarmos serviços REST que eles devem ser planejados para não ter estados, como Sessions, e devido a isso, cada Requisição deve ter toda a informação necessária para que o servidor possa processar a mensagem. E para ser padrão, ele deve retornar dados compreendidos na maioria das linguagens, ou seja, ele deve retornar geralmente XML ou JSON. É muito comum, quando criamos serviços seguindo o padrão REST, chamá-los de RESTFul.

Em quais situações utilizar o WebAPI?

Não é difícil responder a pergunta “Em quais situações utilizar o WebAPI”. Por ser um framework de serviço, qualquer oportunidade de utilizar uma camada de serviço pode ser exposta via WebAPI. Eu já citei o uso em SmartPhones, que talvez seja o mais comum, mas podemos encontrar muitos outros lugares. Sites como o Twitter, por exemplo, disponibilizam uma API inteira para nossos softwares interagirem com seus serviços. Sites de notícias pode disponibilizar APIs que vão além da funcionalidade exposta por um RSS, por exemplo, possibilitando sistemas de terceiros ranquearem e comentarem notícias. Aplicativos de SmartTV também têm se popularizado e também são consumidores de APIs. A lista seria enorme se colocássemos todas as possibilidades.

O importante é saber o que o WebAPI oferece e dessa forma poderíamos visualizar oportunidades de aplicação de serviços feitos em WebAPI em ainda mais lugares.

Por exemplo, é possível expor serviços OData via WebAPI de forma tão simples quanto expor serviços RESTFul simples. Para alcançarmos tal objetivo, precisamos apenas retornar um interface IQueryable em nossos métodos, que a aplicação cliente teria condições de pesquisar, ordenar e filtrar os registros de forma automática.

Obs: OData (Open Data Protocol) é um protocolo Web para pesquisar e atualizar dados através de tecnologias conhecidas na Web. É uma forma, por exemplo, de expor tabelas um Banco de Dado direto no protocolo HTTP e permitir que as consulta sejam feitas diretamente via HTTP.

É isso pessoal, hoje apenas introduzi o Web API, nos próximos posts mostrarei alguns exemplos.

Até logo!

15. January 2013 04:13 by Frederico B. Emídio | Comments (73) | Permalink

Testando propriedades e métodos privados com PrivateObject.    

Olá pessoal! Estou retomando o Blog em 2013 e agora pretende realmente manter a regularidade.

Para iniciar essa nova fase vou falar de um assunto importante já abordado aqui de forma rápida, e que esse ano talvez eu dê mais foco: Testes. Hoje vou falar de uma característica bem específica dos testes, que é a dificuldade de testar propriedades e métodos privados.

De forma geral, é dito que propriedades e métodos privados e não precisam ser testados porque em teoria o método público que acessa o método privado uma vez testado, também testará o método privado. Infelizmente na prática muitas vezes precisamos testar esses métodos privados, talvez porque a lógica deles seja complexa, ou porque o design da classe não seja bom, ou mesmo porque a classe não é nossa, e não podemos alterá-la, mas precisamos testá-la.

O que devemos ter em mente é que nossos códigos precisam ser testados, e devemos fazer de tudo para que isso seja possível, seja com Mocks, Stubs ou até testando métodos privados.

Vamos ao código!

No exemplo abaixo apenas vou mostrar como testar, ignore a necessidade do método ser privado ou não, ou se eu podia fazer um código diferente, o objetivo aqui é mostrar a possibilidade do teste quando ele se faz necessário.

Minha classe de exemplo que deve ser testada:

   1:  public class ClasseTestada
   2:  {
   3:      private int SomaPrivado(int a, int b)
   4:      {
   5:          return a + b;
   6:      }
   7:  }

E o meu teste:

   1:  [TestMethod]
   2:  public void Testar_a_Soma_de_dois_numeros()
   3:  {
   4:   
   5:      var a = 2;
   6:      var b = 3;
   7:      var expected = 5;
   8:   
   9:      var teste = new ClasseTestada();
  10:      var pv = new PrivateObject(teste);
  11:   
  12:      var actual = (int)pv.Invoke("SomaPrivado", a, b);
  13:   
  14:      Assert.AreEqual(expected, actual);
  15:  }
  16:   

E o resultado

Resultado do Teste

Vejam que o teste passou como o esperado. O segredo é bem simples. Apenas utilizo a classe PrivateObject, essa classe basicamente utiliza reflection para invocar métodos e propriedades. Uma vez que passo minha instância a ser testada no construtor da PrivateObject, posso utilizar seus métodos para chamar os membros da classe a ser testada. No caso do exemplo acima estou utilizando o método Invoke para invocar um método privado.

Essa classe pertence ao Namespace Microsoft.VisualStudio.TestTools.UnitTesting, que fica automaticamente disponível ao criar um projeto de testes. Ela tem uma série de outros métodos que auxiliam no trabalho de acesso a membros privados. Abaixo tem toda a lista de métodos:

clip_image003

É isso. Bem simples e curto para começar o ano e retomar as postagens, mas em breve tem mais.

Abraços e feliz ano novo!

10. January 2013 01:13 by Frederico B. Emídio | Comments (4) | Permalink

Novos posts em Breve    

Olá pessoal!

Devido a certa correria, esse ano não consegui me dedicar muito ao Blog, mas em breve retomarei os textos com uma frequência ainda melhor que anteriormente.

Assinem o RSS e fiquem atentos para novos temas.

Abs,

Frederico

31. October 2012 03:24 by Frederico B. Emídio | Comments (108) | Permalink

Executando serviço em Background na infraestrutura do Asp.Net    

Olá pessoal!

Hoje eu vou abordar um tema não muito ortodoxo: As possíveis formas de “hospedar” um serviço na infraestrutura do Asp.Net.

Entenda como serviço nesse contexto uma tarefa que roda de tempos em tempos para realizar uma tarefa qualquer, que não dependa de uma ação do usuário, como o disparo de e-mails de newsletter, ou o que for. Não entenda serviço aqui como um WebService.

Digo que não é ortodoxo porque nesse caso, você deveria hospedar seu serviço em um WindowsService ou Console Application que pudesse ser iniciado pelo scheduler do Windows, pois essas são as formas normais de criar serviços que rodam por tempo indeterminado e que executam tarefas de tempos em tempos.

Mas então, porque criar um “serviço” na estrutura do Asp.Net? A resposta é simples: para as opções citadas acima, você precisa ter um servidor dedicado ou um Azure, o que muitas vezes não temos condições de ter, porque o projeto simplesmente não paga uma infraestrutura dessas. Muitas vezes hospedamos nossos sites em servidores baratos que não nos fornece nada mais que o IIS para hospedar nosso site. (Eu acredito que essa seja a realidade da grande maioria dos desenvolvedores).

Ao longo deste post, vou mostrar algumas formas de fazer esses “serviços” funcionarem, e expor os prós e os contras de cada um.

Vamos lá, mãos a obra!

Exemplo 1 – Criando nova Thread no Application Start

A primeira forma que vou demonstrar é bem simples. Vamos criar uma nova Thread na aplicação Asp.Net no momento que ela for iniciada, e essa Thread terá a lógica para manter o serviço sempre ativo e a própria lógica do processamento, o exemplo do código está abaixo:

   1: protected void Application_Start()
   2: {
   3:     AreaRegistration.RegisterAllAreas();
   4:     RegisterGlobalFilters(GlobalFilters.Filters);
   5:     RegisterRoutes(RouteTable.Routes);
   6:     ThreadPool.QueueUserWorkItem(Executar);
   7: }
   8:  
   9: public void Executar(object state)
  10: {
  11:     while (true)
  12:     {
  13:         //Lógica do Serviço
  14:         Thread.Sleep(20000);
  15:     }
  16: }

O código é bem simples. Na linha 6 eu crio uma nova Thread e  a lógica do “serviço” está no método Executar. Todo esse código está no Global.asax.

Prós:

  • Criação bem simples
  • Os usuários do site não são diretamente afetados pelo processo.

Contras:

  • Uma exceção não tratada em uma Thread que não está associada a um Request derrubará todo o processo.
  • Se você estiver em um WebFarm, você poderá criar a Thread em vários servidores, fazendo múltiplas instância do seu processo ser iniciado e mais de uma tarefa pode realizar o mesmo processamento.
  • O AppDomain do seu site pode cair por uma série de motivos e levar junto seu serviço, eventualmente corrompendo dados.

Exemplo 2 – Realizando o processamento em um Request

Essa segunda forma também é simples, porém eu a acho extremamente fraca e feia, mas não deixa de ser uma possibilidade. Ela verifica a necessidade de rodar um serviço em todos os Requests que chegam ao site, neste caso validado no momento de renderizar uma View sempre utilizada. Meu código está no meu arquivo de Layout do Asp.Net MVC (MasterPage se for WebForm):

   1: @functions{
   2:     public bool DeveExecutar(){
   3:         //Lógica para verificar se deve executar ou não, validando algo em cache ou banco de dados, por exemplo
   4:         return true;
   5:     }
   6: }
   7:  
   8: @if (DeveExecutar())
   9: {
  10:     //Lógica do serviço, pode ser em uma nova thread
  11: }

Acima estou mostrando um código em Razor onde eu preciso verificar se está na hora de executar o serviço, podendo fazer isso de diversas formas possíveis, como dito no comentário da linha 3. E também seguindo o comentário da linha 10, para não onerar demais o usuário com o processamento do serviço, você pode iniciar uma Thread nova para realizar sua tarefa.

Prós:

  • Para não falar que é nenhum, é ligeiramente fácil para desenvolver, especialmente em WebForm, que seria apenas codificar uma lógica semelhante à mostrada acima no Page Load da MasterPage.

Contras:

  • Todos os do Exemplo 1.
  • Por depender de Requests, se o seu site não tiver nenhum Request num grande período de tempo, o serviço não será executado.
  • Por interceptar um Request, o usuário poderá perceber que o site está lendo, pois o Response não está voltando tão rápido como deveria.

Exemplo 3 – Realizando o processamento em um Request – Modo 2

O exemplo 3 tem toda a lógica igual ao do Exemplo 3 e praticamente os mesmos Prós e Contras, a única coisa que muda seria a forma de desenvolver. Nesse exemplo, estou tratando o Evento BeginRequest do Global.asax.

   1: protected void Application_BeginRequest()
   2: {
   3:    if (DeveExecutar())
   4:    {
   5:        //Lógica do serviço, pode ser em uma nova thread
   6:    }
   7: }
   8:  
   9: public bool DeveExecutar()
  10: {
  11:    //Lógica para verificar se deve executar ou não, validando algo em cache ou banco de dados, por exemplo
  12:    return true;
  13: }

Esse modo de fazer tem ainda mais um contra:

  • O evento BeginRequest é chamado para qualquer Request, ou seja, para cada Recurso (imagem, js, etc) chamado, não só o Controller, esse método será chamado, o que pode crescer consideravelmente o tempo para carregar uma página.

Até agora vimos que há muito mais problemas do que soluções para criar esse tipo de serviços, vamos mais um pouco a fundo nos problemas, e assim depois poderemos procurar uma solução um pouco mais satisfatória que as definidas acima.

Motivos pelos quais o AppDomain pode cair

No último contra do Exemplo 1, eu comentei que o AppDomain pode cair e causar problemas para sua Thread, vamos ver alguns caso que isso pode acontecer.

O Asp.Net pode derrubar seu AppDomain pelos seguintes motivos, entre outros:

  • Quando você modifica o Web.Config do site o Asp.Net vai realizar o recycle do AppDomain.
  • O IIS vai por conta própria reciclar todo o processo a cada 29 horas, derrubando todos os AppDomain sob ele.
  • Em muitos servidores, é comum o IIS está configurado para derrubar o Application Pool depois de algum período de inatividade, ou seja, se depois de 20 minutos, por exemplo, sem receber um Request, o AppDomain pode ser derrubado.

Nesse caso, você pode estar se perguntando: Esses problemas não podem ocorrer também para Requests normais? O AppDomain não pode cair ou ser reciclado durante a execução de um Request?

Bom, quando o Asp.Net/IIS resolve derrubar/reciclar um AppDomain, ele procura descarregar todos os Requests pendentes e dar um tempo para eles terminarem seus trabalhos. Ele dá esse tempo para os códigos que ele sabe que estão rodando, e normalmente os código que ele “sabe” são os códigos de Requests padrões.

Basicamente, o problema apontado no Exemplo 1 com o AppDomain é que o Asp.Net/IIS não sabe que o seu código está rodando, por não ser um Request normal..

Informando ao Asp.Net que você tem um código especial rodando

Depois de ver tanto problema, vamos começar a estudar uma solução.

Existe uma forma fácil de informar ao Asp.Net sobre o seu código. No namespace  System.Web.Hosting  existe uma classe chamada HostingEnvironment que segundo o MSDN:

Provides application-management functions and application services to a managed application within its application domain

Ou seja, a classe te fornece funções e serviços para gerenciar uma aplicação dentro de um AppDomain.

Dessa classe, precisamos conhecer, para o nosso objetivo, apenas dois métodos: RegisterObject e UnregisterObject.

A função desses métodos é registrar e desregistrar um objeto a lista de “Códigos conhecidos” do Asp.Net.

Esses métodos recebem como parâmetro apenas um objeto que implementa a interface IRegisteredObject, que contém apenas um método, conforme snippet abaixo:

   1: public interface IRegisteredObject
   2: {
   3:     void Stop(bool immediate);
   4: }

Quando o Asp.Net vai derrubar o AppDomain, ele primeiro invoca o método Stop de todos os objetos registrados naquele AppDomain. Em geral ele chama o método duas vezes, primeiro com o parâmetro immediate igual a false, o que te dá um tempo para terminar o seu trabalho, tempo esse que é um total de 30 segundos para todos os objetos registrados terminarem o que estão fazendo, e uma segunda vez é chamado após esses 30 segundos passarem, e desta vez com o parâmetro igual a true, o que seguinifica que você precisa terminar seu trabalho agora, porque seu AppDomain está prestes a sumir do mapa.

O segredo é: Uma vez que o Asp.Net chama o método Stop do seu objeto, você deve impedir que esse método retorne até que seu trabalho tenha terminado. Quando o trabalho terminar, você deve desregistrar seu objeto.

Com essa explicação, vamos ao quarto exemplo:

Exemplo 4 – Criando o serviço em um objeto registrado no Asp.Net

Esse exemplo é melhor que todos os anteriores, pois continua sendo simples, e alguns dos contras são superados:

   1: public class Servico : IRegisteredObject
   2: {
   3:    private readonly object _lock = new object();
   4:    private bool _derrubando;
   5:    public Servico()
   6:    {
   7:        HostingEnvironment.RegisterObject(this);
   8:    }
   9:    public void Stop(bool immediate)
  10:    {
  11:        lock (_lock)
  12:        {
  13:            _derrubando = true;
  14:        }
  15:        HostingEnvironment.UnregisterObject(this);
  16:    }
  17:    public void Executar()
  18:    {
  19:        while (true)
  20:        {
  21:            lock (_lock)
  22:            {
  23:                if (_derrubando)
  24:                {
  25:                    return;
  26:                }
  27:                //Lógica do serviço
  28:            }
  29:            Thread.Sleep(60000);
  30:        }
  31:    }
  32: }

Veja que adicionei um Lock no método Stop e dessa forma impeço que esse método retorne caso esteja no meio de uma execução do método Executar.

No Global.asax eu chamo a minha classe do serviço:

   1: protected void Application_Start()
   2: {
   3:     AreaRegistration.RegisterAllAreas();
   4:     RegisterGlobalFilters(GlobalFilters.Filters);
   5:     RegisterRoutes(RouteTable.Routes);
   6:     ThreadPool.QueueUserWorkItem(state => new Servico());
   7: }

Veja que na linha 6 crio uma Thread nova para o meu objeto, mas agora ele estará registrado no Asp.Net e seu fim não será tão repentino como seria antes.

Agora meus Prós e Contras ficarão da seguinte forma:

Prós:

  • Os usuários do site não são diretamente afetados pelo processo.
  • Terei mais controle do tempo de vida do meu “serviço”

Contras:

  • Uma exceção não tratada em uma Thread que não está associada a um Request derrubará todo o processo.
  • Se você estiver em um WebFarm, você poderá criar a Thread em vários servidores, fazendo multiplas instância do seu processo ser iniciado e mais de uma tarefa pode realizar o mesmo processamento.
  • Para utilizar o RegisterObject é necessário que o site esteja rodando com FullTrust

Essa abordagem seria a mais próxima do ideal. Temos ainda o problema das Exceções, que você deve ter cuidado, e a questão do WebFarm, que você terá que fazer um controle mais manual, mas que foge do objetivo desse post, mas agora temos ao menos mais controle sobre o AppDomain.

Também temos um novo problema, que é a necessidade do FullTrust. Porém, até servidores baratos aceitam FullTrust e muitos frameworks que utilizamos no desenvolvimento também exige FullTrust, então acredito que esse é um problema secundário.

É importante falar ainda que o AppDomain pode cair por outros problemas criticos, e você não será avisado, o que pode ainda corromper seus dados no serviço. Por exemplo, o AppDomain por cair se alguém tropeçar no cabo de força ou se o Windows mostrar uma tela azul, ou por qualquer outro motivo desastroso. Porém, esses riscos você também pode enfrentar em Windows Services normais.

Exemplo 5 – Apenas uma abordagem alternativa ao Exemplo 4

Nesse exemplo venho apenas mostrar um alternativa aos Loops com While(true) que estou utilizando, mas a solução é ainda a do Exemplo 4. Nesse exemplo vou utilizar um Timer no  lugar do While(true). Acredito que essa abordagem seja um pouco mais elegante.

 

   1: using System;
   2: using System.Threading;
   3: namespace Exemplo5
   4: {
   5:     public static class MeuTimer
   6:     {
   7:         private static readonly Timer _timer = new Timer(OnTimerElapsed);
   8:         private static readonly Servico _servico = new Servico();
   9:         public static void Start()
  10:         {
  11:             _timer.Change(TimeSpan.Zero, TimeSpan.FromMilliseconds(60000));
  12:         }
  13:         private static void OnTimerElapsed(object sender)
  14:         {
  15:             _servico.Executar();
  16:         }
  17:     }
  18: }

Só preciso mudar uma linha no meu Global.asax:

   1: protected void Application_Start()
   2: {
   3:     AreaRegistration.RegisterAllAreas();
   4:     RegisterGlobalFilters(GlobalFilters.Filters);
   5:     RegisterRoutes(RouteTable.Routes);
   6:     MeuTimer.Start();
   7: }

Agora na linha 6 eu apenas chamo o método Start do meu Timer, e o meu serviço rodará de tempos em tempos de acordo com o meu Timer.

Conclusão

Vimos algumas alternativas para utilizar serviços na infraestrutura do Asp.Net, mesmo sabendo que essa deva ser a última opção, é bom saber quais alternativas temos. Nitidamente, eu prefiro utilizar a última abordagem, ela é a mais madura de todas.

É importante saber também que essas soluções funcionam tanto em WebForm como em MVC.

Por hoje é isso pessoal.

Segue o fonte dos exemplos:

Valeu!

Obs.: Esse post foi inspirado neste outro post

29. February 2012 23:32 by Frederico B. Emídio | Comments (2) | Permalink

Testando seu código JavaScript com QUnit    

Olá pessoal!

Depois de muito tempo sem escrever, volto falando de um dos meus temas preferidos: JavaScript!

Hoje vou falar de um tema complicado mesmo para linguagens compiladas como C# ou Java, para uma linguagem de script esse tema é ainda mais complicado. Como o título já mostra, vou falar de Testes em JavaScript.

Não pretendo aqui falar de todos os conceitos de Teste abordado para tantas linguagens e de seus inúmeros benefícios, mas apenas apresentar uma boa ferramenta que facilita muito a validação do seu código JavaScript. De qualquer forma, não posso deixar de falar que validar código não é o único benefício dos testes, na realidade, na minha opinião, um dos principais benefícios de criar código para testar seu código é melhorar o design do mesmo, deixando ele mais claro e organizado, além de facilitar futuras refatorações.

Organizar código JavaScript é uma tarefa difícil, e o teste facilita muito isso.

Então vamos lá, conhecendo o QUnit

QUnit é a uma ferramenta de teste criada pela equipe do JQuery, para testar sua biblioteca, que mais tarde foi exposta ao mundo para quem quiser utilizar. Só por ser utilizado pela equipe do JQuery já é um motivo para também querermos adotar essa suite de teste para nossos projetos, afinal, não é fácil encontrar erros na biblioteca do JQuery.

QUnit pode ser utilizada para testar código JavaScript puro ou mesmo o comportamento do DOM. Por exemplo, os plugins do JQuery são testados com QUnit, e não somente os códigos de lógica e classes.

Como qualquer ferramenta de teste, o QUnit é perfeito para teste de regressão, afinal, uma vez que um bug é encontrado, você pode criar um teste para validar a existência dele, e após corrigi-lo, verificar a correção, rodando novamente o teste toda vez que você trabalhar sobre o código.

Você não precisa instalar nada de especial para rodar o QUnit, é necessário apenas referenciar o script do Qunit, que pode ser encontrado aqui, e rodar seus testes no Browser.

Vamos ao código!

Utilizando o QUnit

Para utilizar o QUnit, você deve referenciar o script que realiza os testes e o CSS que dá o layout básico da página que mostra os resultados do teste, além de um HTML padrão para organizar os testes, da seguinte forma:

.

   1: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
   2: <html xmlns="http://www.w3.org/1999/xhtml">
   3: <head>
   4:     <title></title>  
   5:     <link rel="stylesheet" href="http://code.jquery.com/qunit/git/qunit.css" type="text/css" media="screen" />
   6:     <script type="text/javascript" src="http://code.jquery.com/qunit/git/qunit.js"></script>
   7:   
   8: </head>
   9: <body>
  10:     <h1 id="qunit-header">
  11:         QUnit example</h1>
  12:     <h2 id="qunit-banner">
  13:     </h2>
  14:     <div id="qunit-testrunner-toolbar">
  15:     </div>
  16:     <h2 id="qunit-userAgent">
  17:     </h2>
  18:     <ol id="qunit-tests">
  19:     </ol>
  20:     <div id="qunit-fixture">
  21:         test markup, will be hidden</div>
  22: </body>
  23: </html>

Explicando o código HTML acima: na tag HEAD adiciono o CSS e o Script necessários para funcionar os testes, e no Body as tags (h1, h2 e div) com id qunit-header, banner, testrunner-toolbar e qunit-userAgent são para criar um layout bonito e para auxiliar na execução dos testes, você não deve mexer nessas tags. A tag com id qunit-tests é onde será exibido os resultados dos seus testes, você também não deve adicionar conteúdo a essa tag. Por fim a tag com id qunit-fixture você deve utilizar no caso de testar algo com HTML, e não somente com código JavaScrit puro. É onde você adiciona HTML de teste. O conteúdo dessa tag não é exibido no browser.

O resultado do código acima é algo como a imagem abaixo:

image

Testando o código

Vamos fazer um código simples para testar. A classe abaixo será utilizada:

   1: var Calculadora = function () {
   2:     this.somar = function (a, b) {
   3:         return a + b;
   4:     }
   5:     this.subtrair = function (a, b) {
   6:         return a / b;
   7:     }
   8: };

Para utilizar o QUnit, precisamos conhecer alguns métodos básicos da biblioteca:

module (nome) – Para separar os testes em módulos

test (nome,testMethod) – Método que executa o teste, o primeiro parâmetro recebe o nome do método e o segundo é um método anonimo responsável por realizar o teste. Esse método tem um overload, podendo receber no segundo parâmetro, chamado expected, a quantidade de assets esperados dentro do método definido no parâmetro testMethod.

expect (total) – Define a quantidade de asserts esperados em um testMethod. É utilizado no caso de não ser utilizado o parâmetro expeted do método test.

Além do métodos acima, também têm os métodos responsáveis pelos Asserts dos testes, abaixo listo os principais:

ok(resultado, mensagem) – Realiza uma validação booleana do parâmetro resultado e exibe a mensagem como resultado.

equal(valor_real,valor_esperado, mensagem). Valida se o Valor Real é igual ao Valor Esperado.

notEqual(valor_real,valor_esperado, mensagem). Valida se o Valor Real NÃO é igual ao Valor Esperado.

Para testar meu código acima, vou fazer o seguinte código de teste:

   1: module("Testes de Somar");
   2: test("Testar Soma", 1, function () {
   3:    var calc = new Calculadora();
   4:    equal(calc.somar(1, 2), 3, "O resultado deve ser 3");
   5: });
   6:  
   7: test("Testar Soma Com Erro", 1, function () {
   8:    var calc = new Calculadora();
   9:    notEqual(calc.somar(2, 2), 3, "O resultado não pode ser 3");
  10: });

O resultado do código acima seria como a imagem abaixo:

image

Caso meu código tenha algum erro, o resultado seria como a imagem abaixo, mostrando inclusive o local onde ocorre o erro:

image

O código é bem simples, mas vou fazer uma pequena explicação.

No método test eu passo o nome do teste e a quantidade de assert que eu quero fazer no teste. Se eu definir um número de assert diferente do número que eu executo, o teste vai falhar (os possíveis métodos de assert do QUnit está acima). Por último parâmetro eu passo o método responsável por realizar os testes. O QUnit verifica os asserts dentro deste método, e se todos estiverem de acordo com o esperado, os testes passarão.

E se eu quiser testar um HTML? Bom vamos aos códigos abaixo:

Primeiro vou adicionar referência aos scripts do JQuery e JQuery.UI:

   1: <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
   1:  
   2: <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/jquery-ui.min.js">
</script>

Vou criar um JQuery Ui Widget simples, e testar se meu código está funcionando conforme o esperado:

   1: (function ($, undefined) {
   2:     $.widget('teste.bolder', {
   3:     _create: function () {
   4:  
   5:         $(this.element).append("<p class='bolder'/>");
   6:     }
   7: });
   8: } (jQuery));

O que meu código faz é apenas adicionar uma tag P com uma classe “bolder” ao controle selecionado pelo JQuery, ou seja, se eu fizer:

$(“div#teste”).bolder();

Um div vazio deve ficar assim:

<div><p class=”bolder”/></div>

Vamos ao teste. Primeiro devo alterar a tag com id qunit-fixture:

   1: <div id="qunit-fixture">
   2:     <div id="teste"></div>
   3: </div>

Após fazer isso, crio meu código de teste:

   1: test("Testando widget Bolder", 1, function () {
   2:    //Crio o controle executando o widget
   3:   $("#teste").bolder();
   4:   //faço um teste básico para ver  se fpo criado como o esperado.
   5:   equal($("p.bolder").length, 1, "Testando criação");
   6: });

É um teste bem simples, mas dá para ver como podemos fazer coisas mais complexas se quisermos. Lembre-se: A equipe do JQuery teste seu código dessa forma!

O resultado do meu teste está abaixo:

image

Bom, acredito que com isso você já consegue codificar seus métodos de testes de JavaScript. É só aplicar os mesmo conceitos que você já utiliza para testar seu código C#.

Para mais informações sobre o QUnit, visite o site do JQuery específico para essa ferramenta.

Abraços e até o próximo!

25. January 2012 00:03 by Frederico B. Emídio | Comments (7) | Permalink

Trabalhando com Escopo em JavaScript–Palavra chave ‘With’    

Olá pessoal!

Voltando a falar novamente de JavaScript, vou fazer um rápido post sobre uma palavra chave que dificilmente se vê por ai, a palavra with.

Todos nós já sabemos que o JavaScript tem uma forma um pouco diferente de trabalhar com escopos, e que chaves não têm o mesmo comportamento que têm em C#, por exemplo, não definindo escopo de forma alguma.

Veja o código abaixo:

   1: for (var i=0; i<3; ++i)
   2: {
   3:        var num = i;
   4:        setTimeout(function() { alert(num); }, 10);
   5: }
   6: alert(num);

Em C#, por exemplo, o num da linha 6, daria erro, por não estar definido, porém, o valor dele em JavaScript estará válido. No final da execução do código acima, o resultado será quatro alertas com o mesmo valor: 2. Isso porque no JavaScript, por padrão, uma variável tem contexto global, e os valores estão naturalmente definidos com a ordem de execução, e não com o momento ou local que elas aparecem no código.

Se eu fizer uma pequena alteração no código, atribuindo um valor fora do FOR, mesmo a variável dentro do método anônimo, dentro do setTimeout, que por sua vez está dentro do FOR será afetada:

   1: for (var i=0; i<3; ++i)
   2: {
   3:        var num = i;
   4:        setTimeout(function() { alert(num); }, 10);
   5: }
   6: var num = 9;
   7: alert(num);

Agora os quatro alertas que aparecem serão 9.

Dentro de funções, porém, isso não acontece. Por exemplo, o seguinte código daria um erro ao invocar o método metodoB, pois no contexto dele o metodoB não tem a variável num definida:

   1: function metodoA(){
   2:     var num = 2;
   3: }
   4:  
   5: function metodoB(){
   6:     alert(num);
   7: }
   8:  
   9: metodoA();
  10: metodoB();

Utilizando a palavra-chave With para definir escopo

Bacana, agora que já passamos por uma brevíssima introdução a escopo no JavaScript, vamos ver como utilizar a palavra chave With. Talvez você nunca tenha vista essa palavra chave, mas em alguns casos ela pode ser muito útil.

O que o With faz é definir que tudo que for criado dentro do seu bloco, terá um escopo novo, mesmo que seja dentro de um loop, por exemplo, se utilizarmos o primeiro código com um block with, ele ficaria assim:

   1: for (var i=0; i<3; ++i)
   2: {
   3:     with({num : i}){  
   4:        setTimeout(function() { alert(num); }, 10);
   5:    }
   6: }
   7: var num = 10;
   8: alert(num);

Agora os alertas seriam um diferente do outro, seria primeiro um 10, e depois um 0, 1 e 2. Dentro do próprio bloco poderíamos criar mais variáveis que elas não compartilhariam escopo.

Uma outra forma de utilizar o with é dizendo qual é o escopo dentro dele, como fazemos com o with da linguagem VB, por exemplo:

   1: var Pessoa = function(){
   2:     
   3:     this.Nome = "Frederico";
   4:     this.Idade = 25;
   5:  
   6:     this.Escrever = function(){
   7:  
   8:         with(this){
   9:             alert(Nome + " - " + Idade);
  10:         }
  11:     }
  12: }
  13:  
  14: var p = new Pessoa();
  15: p.Escrever();

Veja que legal: dentro do bloco with, estou definindo que o escopo da variável é o this, então não preciso utilizar this.Nome e this.Idade para acessar as variáveis da minha classe!

Agora imagina o quanto de código isso vai poupar quando você for fazer grandes interfaces com JavaScript, utilizando muito JQuery, Knockout, etc. Lembre que cada palavra no JavaScript pode mudar muito o tamanho do seu arquivo, o que faz uma bela diferença na Web.

Bom, era isso pessoal, espero que essa dica os ajude!

Abraços e até o próximo!

22. October 2011 09:32 by Frederico B. Emídio | Comments (2) | Permalink

Asp.Net MVC – Preenchando campos da View a partir do JavaScript (JQuery, KnockoutJS, etc)    

Olá Pessoal!

Atualmente, todo bom desenvolvedor se preocupa com a experiência do usuário. Não é a toa que bibliotecas JavaScript, como JQuery, tem se popularizado tanto. Nós queremos dar a melhor experiência para o usuário e o único jeito é abusar de JavaScript e Ajax.

Porém, quando começamos a fazer uma interface muito rica, normalmente fazemos todo o controle da View no JavaScript, e acabamos fazendo muitos request para preencher uma única página.

Por exemplo, se eu estiver utilizando KnockoutJS, e minha página for um pouco mais complexa, meu ViewModel começará a ficar extenso. Veja por exemplo esse tutorial do próprio site do KnockoutJS, que apesar de ser simples, já demanda um pouco mais de JavaScript. Num exemplo real, o JavaScript fica imenso.

E como recuperar os dados do servidor e fazer esse Model ser renderizado na página? Muitas vezes fazemos como era possível no WebForms:

  1. Renderizamos a Página
  2. Requisitamos os dados via Ajax
  3. Retornamos os dados.
  4. Preenchemos o ViewModel

O problema é que essa solução, além de fazer dois Request, no mínimo, faz a página “piscar” geralmente, afinal, ela aparece momentaneamente sem os dados, e depois os dados aparecem.

Por exemplo, imagine o simples modelo abaixo:

   1: <script src="../../Scripts/knockout-1.3.0beta.js" type="text/javascript"></script>
   1:  
   2: <div data-bind="with: Autor">
   3:     <p>
   4:         Nome : <span data-bind="text: Nome"></span>
   5:     </p>
   6:     <p>
   7:         E-mail: <span data-bind="text: Email"></span>
   8:     </p>
   9:     <h2>
  10:         Posts</h2>
  11:     <ul data-bind="foreach:Posts">
  12:         <li><span data-bind="text:Titulo"></span>
  13:     </li></ul>
  14: </div>
  15: <script type="text/javascript"> 
  16:  
  17:     var meuModelo = {
  18:         Autor: {
  19:             Nome: "Frederico",
  20:             Email: "teste@teste.com",
  21:             Posts: [
  22:                     { Titulo: "Post 1" },
  23:                     { Titulo: "Post 2" },
  24:                     { Titulo: "Post 3" },
  25:                     { Titulo: "Post 4" }
  26:                 ]
  27:         }
  28:     } 
  29:  
  30:     ko.applyBindings(meuModelo);    
</script>

Caso eu queira receber os dados do servidor, no formato JSON, a primeira idéia que viria na cabeça seria a seguinte:

Criar uma Action que retorna os dados do banco de dados:

   1: public JsonResult ObterDados()
   2: { 
   3:    //Buscando no Banco de dados
   4:    var model = new
   5:    {
   6:        Nome = "Frederico",
   7:        Email = "teste@teste.com",
   8:        Posts = new object[]{
   9:            new {Titulo = "Post 1"},
  10:            new {Titulo = "Post 2"},
  11:            new {Titulo = "Post 3"},
  12:            new {Titulo = "Post 4"}
  13:        }
  14:    }; 
  15:    return Json(model, JsonRequestBehavior.AllowGet);
  16: } 

E na minha View, deveria invocar essa Acion de forma assíncrona:

   1: <script type="text/javascript"> 
   2: //faço outro request
   3: $.getJSON("Home/ObterDados", function (dados) {
   4:     //Preencho meus campos.
   5:     ko.applyBindings({ Autor: dados });
   6: });
   7: </script> 

Bom, na minha opinião, a solução ideal vem a seguir:

Renderizando JSON com ajuda do Json Helper

Apesar dos exemplos estarem com Knockout, utilizar o JSONHelper pode ajudar em qualquer situação, afinal, praticamente todos os plugins JQuery fazem uso de JSON.

A solução é muito simples, você não precisa criar duas Action para renderizar a página (Uma Index que apenas retorna a View, e outra que retorna os dados do Model assincronamente). Na própria Index você retorna os dados, como se fosse preencher os campos normalmente, mas na View renderiza os dados como JSON. Veja:

Na Action padrão eu retorno os dados:

   1: public ActionResult Index()
   2: {
   3:    ViewBag.Message = "Welcome to ASP.NET MVC!"; 
   4:    //Buscando no Banco de dados
   5:    var model = new
   6:    {
   7:        Nome = "Frederico",
   8:        Email = "teste@teste.com",
   9:        Posts = new object[]{
  10:            new {Titulo = "Post 1"},
  11:            new {Titulo = "Post 2"},
  12:            new {Titulo = "Post 3"},
  13:            new {Titulo = "Post 4"}
  14:        }
  15:    }; 
  16:    return View(model);
  17: } 

E na View utilizo o helper @Json:

   1: <script type="text/javascript">
   2: var meuModelo = {
   3:     //Faço o retorno da Action ser transformado em JSON
   4:     Autor: @Html.Raw(@Json.Encode(Model))
   5:     //Aqui poderia ter meus outros métodos e propriedades do ViewModel
   6: }
   7: //Preencho meus campos.
   8: ko.applyBindings(meuModelo);
   9: </script> 
 

E o resultado é o mesmo, porém, com apenas um Request, e com o código ligeiramente mais limpo:

image

Veja como o HTML é gerado, o JSON está renderizado como o esperado:

image

Apesar de não ter utilizado nenhum controle Input (textbox), a solução funciona da mesma forma com esses controles.

Bom pessoal, por hoje era isso. Espero que possa te ajudar.

Abraços!

9. September 2011 12:00 by Frederico B. Emídio | Comments (5) | Permalink

Utilizando JQGrid com WebForms    

Olá pessoal!

Muitas pessoas têm dificuldades de utilizar plugins JQuery com WebForms. Principalmente porque desenvolvedor WebForm muitas vezes sabe usar apenas ServerControls, desconhecendo muitos conceitos básicos de Web em sí. Não é a toa que fiz uma série de post nesse blog mostrando conceitos de Web com WebForms.

Geralmente, quando uma pessoa vê exemplos de JQuery, eles estão em PHP, e a pessoa acredita que não funcionaria com WebForms, ou mesmo que saiba que seja possível, não sabe como adaptar para WebForm. Obs.: Geralmente esse problema não acontece com quem desenvolve com Asp.Net MVC.

Nos últimos post, mostrei alguns conceitos que podem ajudar, todos relacionados a PageMethods, como esse e esse. Se você não leu nenhum deles, ou está caindo diretamente nesse post, aconselho fortemente a ler pelo menos esse: Invocando PageMethods diretamente com JQuery.

Se você entender bem esse post que citei, nem precisará desse para utilizar o JQGrid ou qualquer outro plugin JQuery.

Então vamos lá!

Nesse post, vou utilizar as funcionalidades básicas do JQGrid, como Listar, Pesquisar e Editar/Adicionar.

O mais básico, e que todo mundo utiliza, é o Listar. Vou fazer o seguinte no meu HTML/JavaScript:

   1: <script type="text/javascript">
   2:     $().ready(function () {
   3:          $.ajaxSetup({
   4:                dataFilter: function (data, type) {
   5:                    if (type == "json" || type == undefined) {
   6:                        var msg = eval('(' + data + ')');
   7:                        if (msg.hasOwnProperty('d'))
   8:                            return JSON.stringify(msg.d);
   9:                        else
  10:                           return data;
  11:                   }
  12:                   else return data;
  13:               }
  14:            });
  15:  
  16:         $("#tbGrid").jqGrid({
  17:             mtype: "post",
  18:             url: '/Default.aspx/Listar',
  19:             datatype: "json",
  20:             ajaxGridOptions: { contentType: 'application/json; charset=utf-8' },
  21:             colNames: ['ID', 'Nome', 'Idade', 'E-mail'],
  22:             colModel: [
  23:                        { name: 'PessoaID', index: 'PessoaID', width: 55 },
  24:                        { name: 'Nome', index: 'Nome', width: 150 },
  25:                        { name: 'Idade', index: 'Idade', width: 100 },
  26:                        { name: 'Email', index: 'Email', width: 150 }
  27:                    ],
  28:             rowNum: 10,
  29:             rowList: [10, 20, 30],
  30:             pager: '#pager',
  31:             sortname: 'PessoaID',
  32:             viewrecords: true,
  33:             sortorder: "desc",
  34:             caption: "JQGrid com WebForms",
  35:             serializeGridData: function (dados) {
  36:                 return JSON.stringify(dados)
  37:             },
  38:             jsonReader: { repeatitems: false, id: "PessoaID" }
  39:  
  40:         });
  41:         $("#tbGrid").jqGrid('navGrid', '#pager', { edit: false, add: false, del: false });
  42:     });
  43: </script>
  44: <table id="tbGrid">
  45: </table>
  46: <div id="pager">
  47: </div>

Até ai não fiz nada de mais, adicionei configurações padrões na Grid. Adicionei quatro colunas que virão do meu PageMethod, que está na página Default.aspx com o nome Listar.

Como será que deve ficar meu PageMethod para aceitar a requisição dessa Grid? Se não sabe, ai vai uma dica: Utilizando o Fiddler ou o DevelopToolBar, você pode ver qual HTTP Request o Grid está fazendo. Verificando quais campos estão lá você saberá que são os mesmos parâmetros que deve ter no seu PageMethod.

No meu caso, o JQGrid mandou os seguintes campos:

{"_search":false,"nd":1314831637255,"rows":10,"page":1,"sidx":"PessoaID","sord":"desc"}

No meu caso, o que importa é :

  • rows – Representa a quantidade de linha por página.
  • page – Indica a página atual.
  • sidx – Indica o campo pelo qual a Grid está ordenada. Sidx significa Search Index
  • sord – Indica a ordem que o campo definido em sidx deve ser ordenado.

Nesse caso, se meu PageMethod tiver esses campos já será o suficiente. Abaixo segue a implementação dele:

   1: [WebMethod]
   2: public static Reader<Pessoa> Listar( string sidx, string sord, int page, int rows)
   3: {
   4:     var modelo = new Modelo();
   5:     var lista = modelo.Pessoas.ToList();
   6:     var reader = new Reader<Pessoa>(lista,page,rows);
   7:     return reader;
   8: }

Como ele faz a busca, muda de solução para solução. No meu caso eu estou realizando a pesquisa em um SQLServer CE com EntityFramework. Minha classe Reader é responsável por realizar uma “paginação” nos registros retornados e retornar os campos de acordo com o JSONReader da JQGrid. Todo o código fonte dele está abaixo, apenas por curiosidade, visto que você não precisa dele para fazer seu JQGrid funcionar, ele apenas ajuda:

   1: public class Reader<T>
   2: {
   3:    /// <summary>
   4:    /// Número de páginas retornadas no registro
   5:    /// </summary>
   6:    public int total { get; set; }
   7:    /// <summary>
   8:    /// Página atual que a Grid exibirá
   9:    /// </summary>
  10:    public int page { get; set; }
  11:    /// <summary>
  12:    /// Número total de registros na base.
  13:    /// </summary>
  14:    public int records { get; set; }
  15:    /// <summary>
  16:    /// Lista com cada registro.
  17:    /// </summary>
  18:    public List<T> rows { get; set; }
  19:  
  20:    /// <summary>
  21:    /// Construtor recebe a lista e faz a paginação virtual.
  22:    /// </summary>
  23:    /// <param name="lista">Lista com os registros</param>
  24:    /// <param name="paginaAtual">Página atual.</param>
  25:    /// <param name="totalPorPagina">Total de registros por páginas</param>
  26:    public Reader(List<T> lista,int paginaAtual, int totalPorPagina)
  27:    {
  28:  
  29:        var totalRegistros = lista.Count();
  30:        page = paginaAtual;
  31:        var ini = (page - 1) * totalPorPagina;
  32:        var fim = totalRegistros > totalPorPagina ? totalPorPagina : totalRegistros;
  33:        
  34:        if (ini > 0)
  35:        {
  36:            fim = totalPorPagina;
  37:            fim = fim - 10 >= 0 ? totalRegistros % totalPorPagina : fim;
  38:        }
  39:        var totalPags = totalRegistros / totalPorPagina;
  40:        if (totalRegistros % totalPorPagina > 0)
  41:        {
  42:            totalPags++;
  43:        }
  44:        total = totalPags;
  45:        page = page;
  46:        records = totalRegistros;
  47:        rows = lista.ToList().GetRange(ini, fim);
  48:    }
  49: }

Com os códigos acima minha Grid já está funcionando:

image

Se, por exemplo, eu mudo a quantidade de registros por página, a Grid vai passar os novos valores nos parâmetros do PageMethod, e minha pesquisa retornará a nova quantidade de registros por página:

{"_search":false,"nd":1314832264231,"rows":"20","page":1,"sidx":"PessoaID","sord":"desc"}

E se eu quiser utilizar os campos de pesquisa do JQGrid? Será que o PageMethod já está preparado? Vamos ver. Vou buscar pelo usuário de nome “Frederico” na lista da Grid, vamos ver o que a JQGrid envia para o servidor utilizar o Fiddler ou o IEDeveloperToolbar, ou qualquer ferramenta de sua vontade. Utilizando a “Lupa” da Grid e selecionando o campo na popup que aparece:

image

O que ele envia é o seguinte:

{"_search":true,"nd":1314832456208,"rows":"20","page":1,"sidx":"PessoaID","sord":"desc", "searchField":"Nome","searchString":"Frederico","searchOper":"eq","filters":""}

Veja que alguns campos foram adicionados e outros estão com novos valores, os campos que teremos que adicionar em nosso PageMethod são:

  • _seach – Indica se está sendo realizando uma pesquisa ou se é apenas a listagem do dados
  • searchField – Indica por qual campo está sendo pesquisado.
  • searchString – Indica qual é o valor pesquisado.
  • searchOper – Indica qual é o operado utilizado para a pesquisa.

Vou alterar meu PageMethod  para ficar da seguinte forma:

   1: [WebMethod]
   2: public static Reader<Pessoa> Listar(GridParams param)
   3: {
   4:     var modelo = new Modelo();
   5:     List<Pessoa> lista;
   6:  
   7:     if (param._search)
   8:     {
   9:         var operador = new Func<string,string,string>((op,valor) =>
  10:         {
  11:              int saida;
  12:              bool isNum = Int32.TryParse(valor, out saida);
  13:             switch(op){
  14:                 case "ne":
  15:                     return isNum? string.Format(" <>{0}", valor): string.Format(" <>\"{0}\"", valor);
  16:                 default:
  17:                     return isNum ? string.Format(" = {0}", valor) : string.Format(" = \"{0}\"", valor);
  18:                 }
  19:         });
  20:  
  21:         var sb = new StringBuilder();
  22:         sb.Append(param.searchField);
  23:         sb.Append(operador(param.searchOper,param.searchString));
  24:         lista = modelo.Pessoas.Where(sb.ToString()).ToList();
  25:     }
  26:     else{
  27:         lista = modelo.Pessoas.ToList();
  28:     }
  29:     
  30:     var reader = new Reader<Pessoa>(lista, param.page, param.rows);
  31:     return reader;
  32: }

Primeira mudança importante é que troquei todos os parâmetros por uma classe. A classe encapsula todos aqueles campos que a JQGrid envia para o servidor, independente do Request. Apesar de ser bem simples, segue o código:

   1: public class GridParams
   2: {
   3:     public string sidx;
   4:     public string sord;
   5:     public int page;
   6:     public int rows;
   7:     public bool _search;
   8:     public string searchField;
   9:     public string searchString;
  10:     public string searchOper;
  11: }

Criei essa classe para receber todos os parâmetros porque o Asp.Net não aceita WebServices/PageMethods com parâmetros opcionais ou com Overloads, então criei um método com apenas um parâmetro, que pode ter todos os campos definidos ou não. Visto que a JQGrid utiliza o mesmo método para listar e pesquisar, essa é a única solução.

O resto é apenas a lógica do método, que você pode mudar com a sua no lugar, a minha é só para exemplo. O segredo está em validar o parâmetro _search, se ele for true quer dizer que o usuário está realizando uma pesquisa, e não apenas carregando a lista. Para simular, criei apenas lógica necessária para o usuário usar um Campo como critério igual ou diferente do valor que for fornecido.

Com isso minha lógica já estará funcionando.

Como mudei o parâmetro do PageMethod, preciso apenas alterar um evento na configuração da Grid, para encapsular o nome do parâmetro:

   1: serializeGridData: function (dados) {
   2:    return JSON.stringify({param: dados})
   3: },

Esse evento é invocado pela Grid logo antes da requisição ser enviada para o servidor, então eu coloco os parâmetros dentro do campo param. E pronto! sua Grid está funcionando perfeitamente com Asp.Net WebForm.

Para finalizar, vamos fazer a Grid conseguir criar e alterar registros

Adicionando  e Alterando registro com JQGrid

Antes de mais nada, devo habilitar algumas configurações no Pager da grid, fazendo que o botão de adicionar e editar apareça:

   1: $("#tbGrid").jqGrid('navGrid', '#pager', { edit: true, add: true, del: false });

E alterar o ColModel para dizer como será a edição de cada campo:

   1: colModel: [
   2:    { name: 'PessoaID', index: 'PessoaID', width: 55, editable: false, editoptions: { readonly: true, size: 10} },
   3:    { name: 'Nome', index: 'Nome', width: 150, editable: true, editoptions: { size: 50} },
   4:    { name: 'Idade', index: 'Idade', width: 100, editable: true, editoptions: { size: 2 }, editrules: {integer:true} },
   5:    { name: 'Email', index: 'Email', width: 150, editable: true, editoptions: { size: 50 }, editrules: { email: true} }
   6: ],

Depois adicionar a opção para definir o caminho do PageMethod responsável por salvar o registro:

   1: editurl: "/Default.aspx/Salvar"

Também devemos alterar algumas configurações da Grid para conseguir tratar o JSON, porque infelizmente o JQuery tem um problema ao converter os dados para JSON automaticamente, então nós mesmos temos que fazer isso. Para isso, vamos alterar algumas informações padrões da Grid:

   1: $.extend($.jgrid.edit,
   2: {
   3:     ajaxEditOptions: { contentType: "application/json;charset=utf-8" },
   4:     serializeEditData: function (data) {
   5:         return JSON.stringify(data);
   6:     }
   7: });

Feito isso, não precisamos mais fazer nada no JavaScript, apenas preparar o PageMethod. que você verá que é autoexplicativo:

   1: [WebMethod]
   2: public static void Salvar(string Nome, int Idade, string Email, string oper, string id)
   3: {
   4:     var modelo = new Modelo();
   5:     if (oper == "add")
   6:     {
   7:         var pessoa = new Pessoa
   8:         {
   9:             Nome = Nome,
  10:             Idade = Idade,
  11:             Email = Email
  12:         };
  13:         modelo.Pessoas.Add(pessoa);
  14:     }
  15:     else
  16:     {
  17:         var pessoa = modelo.Pessoas.Where("PessoaID = " + id).ToList()[0];
  18:         pessoa.Nome = Nome;
  19:         pessoa.Idade = Idade;
  20:         pessoa.Email = Email;
  21:     }
  22:     modelo.SaveChanges();
  23: }

E pronto!

Com isso você já pode testar a alteração e inclusão clicando nos botões que aparecem na barra da Grid:

Alterando registro com JQGrid X WebForms

Criando registro com JQGrid X WebForms

É isso pessoal, com o que foi passado nesse post você já consegue ir muito além. Você viu que tudo é possível com a estrutura que o Asp.Net fornece para o WebForm.

Espero que isso ajude. Muitas pessoas já me perguntaram sobre JQGrid com WebForm, isso deve ajudar muita gente.

Abraços e até o próximo!

 

::Atualização

Estou publicando o exemplo do código para esclarecer as dúvidas de alguns. Qualquer problema, comente ai embaixo.

Abs.

JQGridWebForm.zip (3,04 mb)

2. September 2011 00:39 by Frederico B. Emídio | Comments (17) | Permalink

Sobre mim

Minha Imagem

Meu nome é Frederico Batista Emídio, trabalho com desenvolvimento de sistemas profissionalmente a oito anos, porém, já faço sites pessoais a pelo menos dez anos.

Para saber mais clique aqui.

Páginas

Calendário

<<  April 2014  >>
MoTuWeThFrSaSu
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

Visualizar posts em um Calendário
Sigua @fredemidio

MCP Asp.NET