terça-feira, 10 de julho de 2012

MVC4 - Mobile Display Mode

Oi de novo. Raro isso, duas postagens em um dia. Essa vai ser mais curta.

A segunda coisa que fui ver no MVC4 foi a parte de display mode. Agora você simplesmente cria uma view com o mesmo nome da original e .mobile antes da extensão e o MVC magicamente detecta se a requisição vem de um celular, e direciona para a view certa. Bom demais pra ser verdade?

Pois é. Criei a aplicação MVC4, criei minhas views, publiquei a aplicação no meu IIS para ver do celular. Tenho um android e meu browser é o firefox lá também. Não funcionou. Procurei a achei uma variável que retorna se é Mobile*. Trazia false.

Mas há uma solução para que você adicione seus próprios display modes. Vi que o user agent do meu browser no celular: "Mozilla 5.0 (Android; Mobile; rv:14.0) (...)". Coloquei no Global.asax, no Application_Start, como ensinado em vários sites:

    DisplayModeProvider.Instance.Modes.Insert(0, new DefaultDisplayMode("mobile")
    {
        ContextCondition = (context => context.Request.UserAgent.IndexOf
            ("mobile", StringComparison.OrdinalIgnoreCase) >= 0)
    });

Funcionou. Chamou a view que tinha antes da extensão ".mobile". Agora, o celular da minha irmã, só para fazer um teste final... Não funcionou. A palavra mobile não está no user agent. Ela tem um samsung que não é android. O browser é o que veio com o próprio SO. Baixei novamente o browser que veio no meu android: Dolphin. Este possuía a palavra mobile na descrição.

Tentando evitar problemas, uma das soluções que achei é testar, além de mobile, mais alguns nomes de aparecem em celulares. Encontrei uma pequena lista neste artigo: http://www.add-digital.com.br/blog/como-acessar-a-versao-mobile-do-seu-site-atraves-do-javascript/. Caso o artigo saia do ar, aqui está a lista:

nokia, iphone, blackberry, sony, lg, htc_tattoo, samsung, symbian, SymbianOS, palm, series60, windows ce, android, obigo, netfront, openwave, mobilexplorer, operamini

So, that's all for now, folks!



* na view - ViewContext.HttpContext.GetOverriddenBrowser().IsMobileDevice

segunda-feira, 9 de julho de 2012

MVC4 - Async

Bom dia... Faz tempo que não posto alguma coisa. Enfim.

Estou vendo MVC4 agora. No momento, mexendo com a parte de chamadas assíncronas.

A primeira coisa é que queria construir algum exemplo simples, para entender melhor. Os que achei envolviam serviços e eu não queria criar um serviço para depois testar o que eu precisava.

Meu primeiro problema foi que o .NET 4.0 não conhece as keywords novas, async e await. Instalei o 4.5. Mas o Visual Studio 2010 não conhece o 4.5. Tentei até mudar no arquivo csproj em um editor de texto, mas não funcionou. Tive que instalar o VS2012 RC (http://www.microsoft.com/visualstudio/11/en-us). Se quer um conselho, não resolva instalar às 21h da sexta-feira.

Vamos à programação. Descobri que eu tinha que criar um método que chamasse qualquer outra coisa assíncrona. Pelo que li, já no 4.0 foram implementados métodos assíncronos no .NET. Tentei com chamada para a internet, mas retornava muito rápido.

O que resolvi usar foi leitura de um arquivo grande (precisava ser grande para testar o tempo, se realmente diminuiria). Meu computador é um core i5, 2,3 GHz, 4gB de ram, 64bit. Dois arquivos de mais ou menos 130mB foram usados.

Usei a classe StreamReader para ler, o método ReadToEndAsync. Para o método ser assíncrono, deve ser colocada a keyword async antes do tipo do método. Os métodos assíncronos que forem chamados dentro dele devem ser chamados com a keyword await antes, se for necessário aguardar o retorno deles para continuar.

Para o retorno do método, se usa a classe Task. Sozinha se for um void, como generic se houver algum tipo de retorno (passando o tipo do retorno real para a Task).

Meu método de leitura:
        private async Task<String> readAsync(String filename)
        {
            var reader = new StreamReader(filename);
            return await reader.ReadToEndAsync();
        }

Chegou a me passar pela cabeça: se ele chama o método assíncrono e fica esperando o retorno, qual a diferença de fazer isso ou chamar o convencional mesmo? Eis a resposta:

        var first = readAsync(firstFile);
        await readAsync(secondFile);
        await first;

Chamamos o primeiro, esperamos a execução do segundo, então esperamos a do primeiro (provavelmente ele já terá terminado). O primeiro fica executando ao mesmo tempo que o segundo. Uma economia de mais de 25% do tempo. Testei com 3 arquivos, cada qual em torno de 80mB. Quase 45% de ganho.

Problemas que esbarrei, para tomar cuidado:
  • Tentei fazer com três arquivos, dos maiores. Recebi uma bela OutOfMemoryException. Precisa de um pouco de cuidado, não pedir muita coisa ao mesmo tempo;
  • Tentei abrir o StreamReader com using*, mas ele fechou o arquivo antes de ler, por ser assíncrono. Então, cuidado quando faz dispose de objetos, sendo assíncrono você nunca sabe quando vai realmente usá-los, a menos que use await.
Quando for fazer as Actions, a regra é a mesma para o método (que é assíncrono, como os métodos que ele chama). Isso é cascateado a partir do momento de você cria o método: todos que o chamarem terão que ser assíncronos para usar await, e dali para cima permanece essa regra.

Meu ponto de partida foi este artigo: http://www.asp.net/whitepapers/mvc4-release-notes#_Toc303253813. A parte do timeout que cita não consegui fazer funcionar. Ele até retorna um erro de timeout, mas não redireciona para a view (nem trocando o tipo do erro para o que aparece na tela amarela) e executa o processo inteiro antes de retornar o timeout. Não me parece muito útil desse jeito.



* using (var reader = new StreamReader(filename))