.Net Magazine edição 53

Se você se preocupa com qualidade para desenvolvimento de software, sugiro que você leia também meu outro artigo da edição deste mês na .Net Magazine (edição 53), que fala sobre testes unitários utilizando mocks e stubs.

Li a algum tempo em algum blog uma definição de legado. Pelo que eu me lembro:

1) Se você não documentou, é legado.
2) Se você não criou testes unitários, é legado.
3) Se outra pessoa não consegue ler o código, ou ele parece uma macarronada, de tão embarassado, é legado.

Não interessa se você escreveu o código hoje de manhã. Se você não criou testes unitários ele é automaticamente código legado. Então, para ajudar a diminuir a quantidade de código legado no mundo, eu quis escrever este artigo, e os próximos que virão (é uma pequena série, a terceira vai ser focada em testes com ASP.Net MVC). Ele vai te mostrar como escrever testes verdadeiramente unitários, vai diferenciar testes unitários de testes integrados, e vai te ensinar a criar uma aplicação mais testável, ou seja, que te permite testar, por exemplo, um componente de acesso a dados, sem a base de dados.

Garanto que, se você passar a testar seu código, a criar um teste unitário logo após criar uma função de negócio, a qualidade do que você desenvolve vai melhorar muito. E se você já testa, mas não conhece os objetos de mock, vai ganhar muito, porque a idéia do mocking como um todo é sensacional.

Me digam o que acharam. Vão passar a testar? Vale a pena?


Postado na(s) categoria(s) Artigos técnicos pelo giovanni bassi em 24 de julho de 2008 às 19:58 | Tags: , , , , ,

Ninguém avaliou. Dê sua nota!

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
.Net Magazine edição 53

Saiu a .Net 53 da .Net Magazine. Nesta edição estou falando de um tema muito legal: Lambas e Generics com C# 3.0.

Muita gente não conhece o poder adicionado à linguagem com as Lambdas. Já tínhamos os generics antes, o que dava bastante poder, e agora com as Lambdas, a linguagem ficou ainda mais elegante. Sugiro que, se você gosta de conhecer novas maneiras de programar, que dê uma olhada. Com as Lambdas, o C# ficou com uma carinha de linguagem funcional, meio parecido com o F#.

Dêem uma olhada nessa construção com Lambdas, impossível no C# 2.0:

Action<Action<string>, string> DeixaMaiusculaEProcessa = (acao, texto) => acao(texto.ToUpper());
DeixaMaiusculaEProcessa((texto) => Console.WriteLine(texto) , "algum texto maiusculo");

Eu gosto muito, passa uma idéia de ação, mesmo. Sem dúvida é uma adição boa.

Me contem o que acharam por aqui.


Postado na(s) categoria(s) Artigos técnicos pelo giovanni bassi em 24 de julho de 2008 às 19:41 | Tags: , , , , ,

5.0 ponto(s). Avaliado por 1 pessoas

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Acabo de assistir uma conversa com Juval Lowy, que é autor do livro “Programming WCF Services” (o livro de WCF do “peixinho”, mas informações aqui), gravada durante o TechEd americano deste ano, onde ele fala de Interface Based Design. Ele explica algo que muitos ainda não entram ainda em contato: desenvolvimento baseado em abstrações, e porque isso é bom. Ele bate forte no desenvolvimento baseado em classes concretas ou até em classes abstratas, e defende que o uso de interfaces para classes de negócio deve ser generalizado. O vídeo é em inglês, sem legendas. Recomendo assistir.

Isso é um conceito com o qual concordo, mas que acho que deve ser utilizado com cuidado, e não em todo lugar. Bem utilizado é poderosíssimo, e realmente, foram poucas as vezes que o vi utilizado, e das vezes que vi, não foram todos os usos bem direcionados. Um bom uso de interfaces facilita nos testes, desacopla o design, e deixa a aplicação como um todo mais flexível.

E você, tem usado Interfaces no seu dia a dia? O que acha disso?


Postado na(s) categoria(s) Arquitetura , Indicação de conteúdo pelo giovanni bassi em 24 de julho de 2008 às 14:19 | Tags: ,

Ninguém avaliou. Dê sua nota!

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Mais uma olhada no recém lançado Preview 4 do ASP.Net MVC. Se você chegou aqui agora, vale a pena dar uma olhada nos outros posts. Nesse vou dar uma olhada no tratamento de erros, que é feito com um Filter Interceptor. Eu já havia visto o cache com Filter Interceptor, e a implementação do tratamento de erro é também muito simples, seguindo o mesmo modelo de atributos em funções. É mais simples, inclusive, do que era antes com Web Forms. E é bem configurável.

Fiz 2 ações no controlador “Quebrado”: ação Index, que é a padrão (não precisa especificar), não trata o erro, e ação “tratada”, que trata o erro.

Antes, quando você tinha um erro, você recebia a seguinte tela de erro, se tivesse os custom errors desligados (ação Index):

Erro Padrão do ASP.Net com ASP.Net MVC- CustomErrors Off

Se custom errors estiver ligado, a mensagem é a seguinte (ação Index):

Erro Padrão do ASP.Net com ASP.Net MVC- CustomErrors On

Até aí, essa é a resposta padrão do ASP.Net, todos conhecemos a velha tela amarelona (e não gostamos de vê-la, não é verdade?). O ASP.Net MVC traz agora uma novidade: pode-se tratar erros com um simples Interceptor Filter, que é aplicado com um atributo no método (ação do MVC) ou na classe (controlador).

Se a exibição dos erros erros estiver habilitada (custom errors off), e uma ação tiver um erro, a view de erros será exibida, conforme a figura abaixo (note que essa é a ação “tratada”):

Nova View de erros - CustomErrors Off

E se estiver desabilitada (custom errors on), será exibida uma mensagem mais simples, sem o detalhamento do problema (o que é prática comum de segurança) – exibindo ação “tratada”:

Nova View de erros - CustomErrors On

Já se você quiser fazer um tratamento customizado do erro, você pode. Abaixo fiz uma view padrão para mostrar erros de overflow (mais uma vez, ação “tratada”:

View de erros Customizada

Como funciona? Muito simples. Vejam abaixo o código do controller:

    9 public class QuebradoController : Controller

   10 {

   11     public ActionResult Index()

   12     {

   13         return JogaErro();      

   14     }

   15 

   16     [HandleError(ExceptionType = typeof(OverflowException), View = "OverflowException")]

   17     [HandleError()]

   18     public ActionResult Tratada()

   19     {

   20         return JogaErro();

   21     }

   22 

   23 

   24     public ActionResult JogaErro()

   25     {

   26         if (Session["aleatorio"] == null)

   27             Session["aleatorio"] = 0;

   28         var aleatorio = (int)Session["aleatorio"];

   29         aleatorio++;

   30         if (aleatorio > 1)

   31             aleatorio = 0;

   32         Session["aleatorio"] = aleatorio;

   33 

   34         switch (aleatorio)

   35         {

   36             case 0:

   37                 throw new OverflowException("Essa é uma OverflowException.");

   38             default:

   39                 throw new ApplicationException("Essa é uma ApplicationException.");

   40         }

   41     }   

   42 }

A acão “tratada” tem 2 atributos chamados HandleError, que são os atributos que fazem o tratamento. O primeiro informa que trata exceções de overflow, e especifica a View, através de parâmetros, ficando a View de nome “OverflowException” responsável por esses erros. O segundo trata todas as outras exceções, e utiliza a View padrão, que é chamada Errors.aspx, e fica no diretório /Views/Shared. Vejam o Solution Explorer:

Solution explorer

Vejam o código da View:

    7 namespace MvcApplicationErrorHandler1.Views.Shared

    8 {

    9     public partial class Error : ViewPage<HandleErrorInfo>

   10     {

   11     }

   12 }

Ela utiliza como modelo um objeto HandleErrorInfo, que é o seguinte:

    1 namespace System.Web.Mvc

    2 {

    3     public class HandleErrorInfo

    4     {

    5         public HandleErrorInfo(Exception exception, string controller, string action);

    6 

    7         public string Action { get; }

    8         public string Controller { get; }

    9         public Exception Exception { get; }

   10     }

   11 }

Esse objeto tem propriedades para a ação, para o controlador, e para a exceção lançada. Minha customizada utiliza esses dados:

    1 <%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master"

    2 AutoEventWireup="true" CodeBehind="OverflowException.aspx.cs"

    3 Inherits="MvcApplicationErrorHandler1.Views.Quebrado.OverflowException" %>

    4 <asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">

    5     <h2>Você teve uma OverflowException</h2>

    6     <b>Dados:</b><br />

    7     <b>Action:</b>              <% =this.ViewData.Model.Action %><br />

    8     <b>Controller:</b>          <% =this.ViewData.Model.Controller %><br />

    9     <b>Mensagem da exceção:</b> <% =this.ViewData.Model.Exception.Message %><br />

   10 </asp:Content>

E o resultado é a imagem lá em cima.

Achei o resultado muito bom. Dá para tratar erros muito bem. Diferente do AJAX com MVC, já é uma função bem completa. Já imagino uma classe que herde de ViewPage Controller tipo uma ErrorHandlerViewPage ControlleBase, sendo colocada como classe de base, e podendo até já realizar o tratamento de erro. Para o log do erro, imagino que seria com a criação de outro filter interceptor. Vou pensar nisso depois coloco aqui. Alguma sugestão pessoal?


Postado na(s) categoria(s) ASP.Net MVC pelo giovanni bassi em 23 de julho de 2008 às 21:00 | Tags: , ,

Ninguém avaliou. Dê sua nota!

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Continuo testando o recém lançado Preview 4 do ASP.Net MVC. Essa versão veio com uma implementação inicial com AJAX, conforme explica a documentação:

“We continue to work on enhancing the AJAX features for ASP.NET MVC. The features described here are preliminary versions of features that we expect to enhance in future releases.”

ou...

“Continuamos a trabalhar para melhorar as funcionalidades de AJAX para o ASP.Net MVC. As funcionalidades descritas aqui são versões preliminares das funcionalidades as quais esperamos melhorar em versões futuras.”

Ainda bem que eles adiantaram o assunto, porque realmente as funcionalidades são muito iniciais mesmo. Boa parte do trabalho vem baseado na library de AJAX da Microsoft, que vem encapsulado no já bem conhecido arquivo MicrosoftAjax.js. Enquanto este arquivo tem mais de 6000 linhas de código, o do MVC ainda não passou de 200. Imagino que eles estejam tratando essa versão como um direcionador para as versões futuras. Quando falei que ia demorar um pouco até sair a versão final do ASP.Net MVC, já imaginava esse tipo de coisa.

Na verdade, até prefiro assim. Prefiro uma versão mais completa que demora um pouco mais a uma versão mais ou menos lançada antes.
Até porque, quando você lança antes mas sem boa parte das funcionalidades que queria que estivesse na v1, acaba por ouvir reclamações de todos os lados, são feitas comparações com outros produtos semelhantes, dizendo-se que são melhores, e por aí vai. No caso da Microsoft, ela tem o seu excelente histórico de Web Forms a bater, o que não é fácil, depois de tudo que já conseguimos fazer com altíssima produtividade. No caso do AJAX é a mesma coisa. Agora que o AJAX heaven paira sobre nós quem vai querer abandoná-lo? Pois é. Tudo isso me criou uma expectativa com esse release do CTP 4, não posso mentir.

Enfim, vamos ao que vi. Existe agora uma propriedade chamada AJAX na ViewPage, que devolve um objeto do tipo AjaxHelper. Esse é o objeto responsável por fazer o trabalho de contato com Ajax. Nesse momento, ele possui apenas ActionLinks, para criar links que trabalham com Ajax, como se fossem LinkButtons, e uma função para criar um formulário html Ajax. É isso. E o que esses métodos fazem básicamente é criar ligações com o Javascript do arquivo MicrosoftAjax.js, e o novo MicrosoftMvcAjax.debug.js, a library de Javascript do ASP.Net MVC.

Você deve adicionar manualmente a referência aos arquivos Javascript do Ajax. O ideal é fazer na Master Page para não ter que fazer de novo. Para testar, é só criar um controlador simples. Fiz um com a ação Index padrão que só chama a View a ser Renderizada. Fiz também duas funções do Ajax, que já explico. Dêem uma olhada como ficou:

public class AjaxTestController : Controller
{
    public ActionResult Index()
    {
        return View();
    }

    public string ObterHorario()
    {
        var drpLista = Request.Form["drpLista"];
        string ret = "Item selecionado: " + drpLista + "
          Hora: " + DateTime.Now.ToString("dd/MM/yyyy hh:mm:ss");
        return ret;
    }

    public string ObterHorarioSegundo()
    {
        var drpLista = Request.Form["drpLista"];
        string ret = "Item selecionado: " + drpLista + "
          Segundo atual: " + DateTime.Now.ToString("ss");
        return ret;
    }    
}

As funções que trabalharão com Ajax são as duas que retornam strings: ObterHorario e ObterHorarioSegundo. Notem que essas ações não retornam ActionResults, como é o padrão em uma ação. Elas são chamadas no código, e o que é feito é uma substituição do valor de um elemento html com o valor retornado. As duas funções utilizam o valor de um DropDownList que está na View (veja abaixo), e concatenam com valores da hora atual.

Vejam como ficou a aspx:

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" 
AutoEventWireup="true" CodeBehind="Index.aspx.cs" 
Inherits="MvcApplicationAjax1.Views.AjaxTest.Index" %>
<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
<% using(Ajax.Form("ObterHorario",
       new AjaxOptions() { UpdateTargetId="lblOut"}) )
    { %>
    <% =Html.DropDownList("drpLista",
                new SelectList(new[] { 
                new { id = 1, desc = "um" }, 
                new { id = 2, desc = "dois" } ,
                new { id = 3, desc = "três" }
            }, "id", "desc"))%> <br />
    <% =Html.SubmitButton("submit","Recupere a hora 2") %><br />
    
    <% =Ajax.ActionLink("Recupere o segundo atual",
        "ObterHorarioSegundo", 
            new AjaxOptions() { UpdateTargetId="lblOut"}) %>
    <br />
    
    <span id="lblOut"></span>
    
<% } %>
</asp:Content>

Nada de mais, criei:

  • Um form Ajax, que aponta para atualizar o span chamado “lblout” e para a função do controlador ObterHorario.
  • O DropDownList preenchido com uma lista de valores (vai gerar uns selects e uns options).
  • Um botão submit simples.
  • Um link que vai chamar a função ObterHorarioSegundo.
  • Um span para receber o valor.

Funciona direitinho. Ao clicar no botão ou no link o texto respectivo é exibido, através da substituição do conteúdo do span. Vejam como ficou a view (clique para ampliar):

Exibindo após clicar o botão com Ajax

Note a observação abaixo “Item selecionado: 1”, e a hora na linha debaixo. Isso quer dizer que funcionou.

Abaixo temos o resultado ao clicar no Link (clique para ampliar):

Exibindo após clicar o link com Ajax

Já não é possível ver o valor do DropDownList. Isso é até esperado, já que o link não deve ter submetido o form.

Outra coisa meio estranha é que, se você deixar o span assim: <span id="lblout" />, em vez de usar uma tag de abertura e outra para fechar, ele dá problema, e some com o link e com o botão… vai entender.

Achei interessante essa primeira implementação. É um CTP (Preview), é verdade, mas esperava um pouquinho mais. Provavelmente o time estava mais envolvido com a parte de autenticação e autorização, que vou analisar postar em seguida.

E vocês, o que estão achando?


Postado na(s) categoria(s) ASP.Net MVC pelo giovanni bassi em 21 de julho de 2008 às 21:07 | Tags: ,

Ninguém avaliou. Dê sua nota!

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Até agora não havia uma forma de configurar o ASP.Net Routing com o Web.config (ao menos que eu soubesse). Até agora, fazia-se assim:

Public Class GlobalApplication
    Inherits System.Web.HttpApplication

    Shared Sub RegisterRoutes(ByVal routes As RouteCollection)

        routes.IgnoreRoute("{resource}.axd/{*pathInfo}")

         MapRoute takes the following parameters, in order:
         (1) Route name
         (2) URL with parameters
         (3) Parameter defaults
        routes.MapRoute( _
            "Default", _
            "{controller}/{action}/{id}", _
            New With {.controller = "Home", .action = "Index", .id = ""} _
        )

    End Sub

    Sub Application_Start()
        RegisterRoutes(RouteTable.Routes)
    End Sub
End Class

Então fiz algo aqui para ajudar. Criei um assembly para permitir a configuração. No web.config fica assim:

  <gb.aspnetrouting>
    <routing>

      <routes>
        <!--<route routeName="name of the route" 
           url="the url" 
           routeHandler="some 
           route handler" />-->
        <route routeName="DefaultMvc" 
           url="{controller}/{action}/{id}" 
           routeHandler="MvcRouteHandler">
          <defaults>
            <default name="action" value="index" 
               type="System.String, mscorlib"/>
            <default name="id" value="" 
               type="System.String, mscorlib"/>
          </defaults>
          <constraints>
            <constraint name="basic" 
               value="[^\.]*" 
               type="System.String, mscorlib"/>
          </constraints>
          <dataTokens>
            <dataToken name="someData" 
            value="I'm a data token" type="System.String, mscorlib"/>
          </dataTokens>
        </route>
      </routes>

      <ignoredRoutes>
        <ignoredRoute url="{resource}.axd/{*pathInfo}" />
      </ignoredRoutes>

      <routeHandlers>
        <routeHandler name="MvcRouteHandler" 
           type="System.Web.Mvc.MvcRouteHandler, System.Web.Mvc"/>
      </routeHandlers>

    </routing>
  </gb.aspnetrouting>

E no Global.asax, fica assim:

Public Class GlobalApplication
    Inherits System.Web.HttpApplication

    Shared Sub RegisterRoutes(ByVal routes As RouteCollection)

        GB.RoutingConfig.RoutingConfiguration.Configure( _
           "~/web.config", routes)

    End Sub

    Sub Application_Start()
        RegisterRoutes(RouteTable.Routes)
    End Sub
End Class

 

No Global.asax existe apenas uma chamada ao método de configuração. Tirei a idéia do que já existe hoje no .Net Remoting, que possui chamada de configuração semelhante.

Já no web.config, você pode configurar as rotas, com Constraints, Defaults e DataTokens, o gerenciador de rota (que no MVC é sempre o MvcRouteHandler), e as rotas ignoradas. Até o time do MVC lançar o deles vou usar esse para ajudar. E parece que vai ser bem para frente, já que eles já disseram que vão focar em features, por enquanto.

Quem quiser baixar, pegue aqui a dll compilada, e aqui o código fonte. Fiquem à vontade para melhorar, e me dêem notícia se funcionou para vocês e se gostaram.

Já adianto que não é o assembly mais testado no mundo, então ele tem somente a seguinte garantia: "roda na minha máquina". Espero que na de vocês também rode, e deve rodar, mas não garanto que está livre de bugs, já que foi mais um exercício do que a construção de um módulo, ok?


Postado na(s) categoria(s) ASP.Net Routing pelo giovanni bassi em 20 de julho de 2008 às 22:50 | Tags: ,

Ninguém avaliou. Dê sua nota!

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Acabo de testar o novo OutputCache do ASP.Net MVC Preview 4. Notei que ele não tem uma forma de configurar uma dependência de Cache com SQL, nem mesmo via perfil de cache (Cache profile). De resto, funciona perfeitamente.

Para confirmar criei um projeto de testes e analisei um pouco mais a fundo. Dêem uma olhada como ficou meu Web.config abaixo.

Primeiro a seção de strings de conexão:

  <connectionStrings>
    <add name="Northwind2005ConnectionString" 
    connectionString="data source=gb2;Initial Catalog=Northwind2005;User ID=user;password=pwd" providerName="System.Data.SqlClient"/>
  </connectionStrings>

E agora seção de cache:

     <caching>
      <sqlCacheDependency enabled="true">
        <databases>
          <add name="Northwind2005" 
          connectionStringName="Northwind2005ConnectionString" 
          pollTime="10000"/>
        </databases>
      </sqlCacheDependency>
      <outputCacheSettings>
        <outputCacheProfiles>
          <add name="NorthwindCacheProfile" varyByParam="none" 
          sqlDependency="Northwind2005:Employees" 
          enabled="true" duration="600"/>
        </outputCacheProfiles>
      </outputCacheSettings>
    </caching>

Tudo configurado corretamente. Se você aplicar esse xml no seu web.config para trabalhar com web forms e aplicar um diretiva de @OutputCache na página vai funcionar.

No ASP.Net MVC funciona assim: você cria uma ação, e coloca o atributo de OutputCache. Para testar, criei um controlador de cache, e duas ações, a padrão, Index, e outra, para testar a interação com a dependência SQL, que usa a mesma View. Vejam como ficou:

namespace MvcApplication1.Controllers
{
    public class CacheTestController : Controller
    {
        [OutputCache(Duration=10)]
        public ActionResult Index()
        {
            ViewData["mensagem"] = "Última atualização às " 
            + DateTime.Now.ToString("dd/MM/yyyy hh:mm:ss");
            return View();
        }

        [OutputCache(CacheProfile = "NorthwindCacheProfile")]
        public ActionResult SQLCacheDep()
        {
            ViewData["mensagem"] = "(SQL Dep) Última atualização às " 
            + DateTime.Now.ToString("dd/MM/yyyy hh:mm:ss");
            return View("Index");
        }
    }
}

A primeira funciona ok. A segunda resulta em um cache que só expira quando a duração é alcançada, ou seja, em 10 minutos (600 segundos). Abri um SQL Profiler e constatei: nada acontecendo lá, nenhuma conexão vindo da aplicação. Se fosse Web Forms estaria rodando a procedure “AspNet_SqlCachePollingStoredProcedure” para ver se alguma coisa mudou.

Para terminar, confirmei no código fonte do ASP.Net MVC a classe OutputCacheAttribute. Ela tem uma função chamada ApplyProfile, onde ela aplica os dados do Cache Profile, vejam só:

public void ApplyProfile(OutputCacheProfile profile) {
    Duration = profile.Duration;
    Location = profile.Location;
    NoStore = profile.NoStore;
    VaryByContentEncoding = profile.VaryByContentEncoding;
    VaryByCustom = profile.VaryByCustom;
    VaryByHeader = profile.VaryByHeader;
    VaryByParam = profile.VaryByParam;
}

Percebem como ela não aplica a propriedade “SqlDependency” do profile? Taí a explicação. O profile está sendo aplicado só com a duração configurada para expirar (propriedade duration), mais nada.

Imagino que a implementação deve sair no Preview 5, vamos ver.


Postado na(s) categoria(s) ASP.Net MVC pelo giovanni bassi em 20 de julho de 2008 às 14:24 | Tags:

Ninguém avaliou. Dê sua nota!

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Estou dando uma olhada no ASP.Net MVC Preview 4, e estou agora entrando em contato com uma feature que existe desde o Preview 2: os ActionFilters.

Com eles você pode colocar ações que acontecem antes das ações de um controlador, e antes da execução do ActionResult retornado pela ação. Você pode pegar erros, criar cache, e até cancelar uma ação (entre várias outras coisas).

Observação: Se você não sabe o que são ActionResults, por enquanto pense assim: são ações encapsuladas (seguem o Design Pattern Command – mais informações aqui e aqui). Quando alguém chama uma ação de um controlador, ele retorna um ActionResult para ser executado. O ActionResult mais comum é o ViewResult, que retorna uma ação de renderizar uma View, ou mais claramente, uma página. Podem existir outros tipos de ActionResult, para baixar arquivos, por exemplo.

Pois bem, criei um logger, bem simples, só para exibir o que está se passando, e criei uma View para exibí-lo. Vejam como foi.

Criei um projeto com Preview 4, e criei um ActionFilterAttribute (uma classe que herde de ActionFilterAttribute). Esse ActionFilterAttribute permite que você crie os tais dos filtros de ação. No meu caso o filtro chamou-se GiggioLogAttribute:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace MvcApplication1.ActionFilters
{
    public class GiggioLogAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuted(ActionExecutedContext filterContext)
        {
            base.OnActionExecuted(filterContext);
            filterContext.Log("OnActionExecuted");
        }
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            base.OnActionExecuting(filterContext);
            filterContext.Log("OnActionExecuting");
        }
        public override void OnResultExecuted(ResultExecutedContext filterContext)
        {
            base.OnResultExecuted(filterContext);
            filterContext.Log("OnResultExecuted");
        }
        public override void OnResultExecuting(ResultExecutingContext filterContext)
        {
            base.OnResultExecuting(filterContext);
            filterContext.Log("OnResultExecuting");
        }
    }
    static class LoggerHelper
    {
        public static void Log(this ControllerContext filterContext, string where)
        {
            string routeDataValues = string.Empty;
            foreach (var item in filterContext.RouteData.Values)
            {
                routeDataValues += "\r\n" + item.Key + ": " + item.Value.ToString();
            }
            
            Models.ActionFilterEventLogger.GetLogs().Add(new Models.ActionFilterEventLog()
            {
                ControllerName = filterContext.Controller.ToString(),
                EventName = where,
                RouteDataValues = filterContext.RouteData.Values
            });
        }
    }
}

Notem como estou sobrescrevendo as 4 ações, de antes da ação do controlador (OnActionExecuting), depois (OnActionExecuted), e de antes da execução do resultado da View (OnResultExecuting) e depois (OnResultingExecuted). Pego o contexto de filtro e passo para um método de extensão chamado Log, onde criou um objeto ActionFilterEventLog e adiciono à uma coleção de logs que fica guardada em uma variável de aplicação escondida atrás da função ActionFilterEventLogger.GetLogs(). Com isso, tenho uma coleção em memória do que aconteceu. Para aplicar é fácil. Vejam como ficou com o atributo decorando a ação About do controller Home:

        [ActionFilters.GiggioLog()]
        public ActionResult About()
        {
            ViewData["Title"] = "About Page";

            return View();
        }

Ao chamar o about, nada muda, mas as ações do meu logger são executadas.

Já para exibir, criei uma view, e usei a mesma coleção como modelo. Vejam o controlador, chamado Logger.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace MvcApplication1.Controllers
{
    public class LoggerController : Controller
    {
        public ActionResult Index()
        {
            return View(Models.ActionFilterEventLogger.GetLogs());
        }
    }
}

Super simples, certo? Simplesmente pega a coleção de logs e repassa à View. No caso, a View não foi nomeada, então segue o mesmo nome da ação: Index.

Vejam a View agora. Primeiro o code behind, onde seto o tipo de modelo:

public partial class Index : ViewPage <List<Models.ActionFilterEventLog>>
{
}

E abaixo a aspx para exibir os dados. Basicamente o que eu estou fazendo é montando uma tabelinha com os dados salvos pelo logger, iterando pela coleção.

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" AutoEventWireup="true" CodeBehind="Index.aspx.cs" Inherits="MvcApplication1.Views.Logger.Index" %>
<%@ Import Namespace="MvcApplication1.Models" %>
<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
    <h2>Logs:</h2>
    <table>        
        <tr>            
            <td><strong>Event Name</strong></td>
            <td><strong>Controller Name</strong></td>
            <td><strong>Route Data</strong></td>
        </tr>
        <% foreach (ActionFilterEventLog log in this.ViewData.Model) %>
        <% { %>
        <tr>
            <td><% =log.EventName %></td>
            <td><% =log.ControllerName %></td>
            <td>
                <% foreach (var item in log.RouteDataValues) %>
                <%{ %>
                    <strong><% =item.Key %>:</strong>&nbsp;<% =item.Value.ToString() %>
                <%} %>
            </td>
        </tr>
        <% } %>
    </table>    
</asp:Content>

Ficou assim:

Logger

Eu gostei bastante da tecnologia. Fácil de usar, rapidíssimo para implementar. Entre a leitura e o projeto final não passei de 40 minutos. Excelente! Em que outras opções vocês acham que esse tipo de técnica ficaria legal?

Quem quiser baixar o código fonte, pegue aqui. Vai rodar em qualquer máquina com ASP.Net 3.5 instalado. As dlls do MVC já vem juntas, então você deve conseguir rodar no Visual Studio 2008 sem o Preview 4 instalado.


Postado na(s) categoria(s) ASP.Net MVC pelo giovanni bassi em 19 de julho de 2008 às 18:10 | Tags:

Ninguém avaliou. Dê sua nota!

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Está disponível no Codeplex desde ontem, 16/7, o quarto preview do ASP.Net MVC. Com novidades de todos os tipos, como novos filter interceptors, output cache, endereçamento de erro, autorização, AJAX e mais um monte de novidades.

Como os caras da Microsoft não dormem, já tem comentário de boa parte do time que o está desenvolvendo, começando pelo ótimo Scott Guthrie (que escreveu ainda antes do lançamento), Scott Hanselman (esse não dorme mesmo), Phil Haack, e Rob Conery. O mais completo é o do ScottGu.

Eu ainda não pus a mão mas vou fazê-lo esta semana, mal posso esperar. Estava contando os dias para este lançamento. São as primeiras demonstrações sérias de amadurecimento do framework.

Já baixou o seu?


Postado na(s) categoria(s) ASP.Net MVC pelo Giovanni Bassi em 17 de julho de 2008 às 19:39 | Tags: , ,

Ninguém avaliou. Dê sua nota!

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Acabei de ver no blog do Cezar Guimarães que o time do ASP.Net publicou o roadmap do ASP.Net AJAX. Li e achei muito interessante. Dêem uma olhada, está no Codeplex.

Algumas novidades: integração no MVC, interoperabilidade, melhoria no Intelisense e comentários, melhorias nas ferramentas e novas funcionalidades, animação, mobile, etc.

Já dá para ver que os componentes do ASP.Net Ajax, que já são bons, vão ficar ainda melhores. E o melhor, como são feitos pela Microsoft, vêm com uma ótima qualidade e vão ser padrão de mercado, o que é sempre muito bom.


Postado na(s) categoria(s) ASP.Net , AJAX pelo Giovanni Bassi em 17 de julho de 2008 às 16:15 | Tags: ,

Ninguém avaliou. Dê sua nota!

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Quem é Giovanni Bassi

Giovanni Bassi Sou uma pessoa apaixonada por tecnologia e especificamente por .Net. Gerencio uma fábrica de software, gosto muito de arquitetura e engenharia de software, publico artigos e edito a .Net Magazine. Dou umas palestras e cursos de vez em quando, e quando dá tempo eu respiro um pouco. Mais detalhes nesta página.

Selos

Web Days 2008

MCPD

MCSD

.Net Magazine

Calendário

«  julho 2008  »
seteququsedo
30123456
78910111213
14151617181920
21222324252627
28293031123
45678910
Ver detalhamento de posts no calendário

Postagens recentes