terça-feira, 27 de dezembro de 2011

Asp.net com EntityFramework: Gerenciando o ciclo de vida do ObjectContext

Olá, pessoal!

Uma dúvida relativamente comum pra quem está começando a usar o Entity Framework em projetos Asp.Net diz respeito ao gerenciamento do ciclo de vida dos objetos ObjectContext.

Qual é a forma correta de gerenciá-los? Em que momento devem ser criados ou destruídos? Por um lado, é muito custoso instanciar um novo contexto toda hora. Você não poderia, por exemplo, criar um a cada método que requeira o uso de um ObjectContext. Por outro, também não podemos simplesmente criar um ObjectContext e deixá-lo viver na memória por toda a sessão de um usuário, ou por todo o ciclo de vida da aplicação.

Uma abordagem amplamente aceita para balancear a quantidade de objetos criados e o tempo de vida de cada um deles é criar um ObjectContext para cada request do Asp.net. Assim, no início de cada request instanciamos um novo ObjectContext, e o destruímos no final da request.

Fazemos isso adicionando um HttpModule ao nosso projeto, e injetando a lógica de criação/destruição dos ObjectContexts nos eventos BeginRequest e EndRequest.

Abaixo, um exemplo dessa implementação. Lembre-se de referenciar o namespace System.Web.

public class ContextManagerModule : IHttpModule
{

    public void Init(HttpApplication application)
    {
        application.BeginRequest += new EventHandler(BeginRequestHandler);
        application.EndRequest += new EventHandler(EndRequestHandler);
    }

    public void BeginRequestHandler(object sender, EventArgs e)
    {
        HttpContext.Current.Items["__DATACONTEXT"] = new EFContext();
    }

    public void EndRequestHandler(object sender, EventArgs e)
    {
        ((EFContext)HttpContext.Current.Items["__DATACONTEXT"]).Dispose();
    }

    public void Dispose() { }
}

Nesse exemplo, EFContext é nossa classe que implementa o ObjectContext do EntityFramework. Substitua pelo nome do ObjectContext do seu projeto.

Agora, sempre que tiver que acessar o ObjectContext, pode fazê-lo assim:

var exemplo = ((EFContext)HttpContext.Current.Items["__DATACONTEXT"]);


Não declare esta variável dentro de um using, pois não queremos que o objeto seja destruído antes do final da request.

O handler que criamos para o evento BeginRequest garante que um ObjectContext ficará disponível para uso ao longo do ciclo de vida de cada nova request.

Já o handler que adicionamos no evento EndRequest da aplicação garantirá que o ObjectContext será destruído logo ao final da request.

sexta-feira, 23 de dezembro de 2011

Ajax Framework Failed to Load? Sys is not defined? WebForm_InitCallback is not defined? E agora?

Como começou

Tem um post fresquinho saindo do forno pra esse final de semana, que será uma introdução ao Ajax com Asp.Net.

Mas antes vou comentar um problema que me deu uma baita dor de cabeça essa semana. Vi mil soluções possíveis na internet e nenhuma funcionou pra mim.

Algumas páginas começaram apresentar os seguintes erros:
  • Ajax Framework Failed to Load
  • Sys is not defined
  • WebForm_InitCallback is not defined
Uma investigação rápida mostrou que o WebResources.axd estava retornando conteúdo em branco, em vez dos scripts que normalmente esperaríamos.

As fontes possíveis do problema, conforme discutidas em vários fóruns e blogs, apontavam principalmente para:
  • WebFarms;
  • Compressão de conteúdo;
  • Mapeamento incorreto ou inexistente da extensão .axd no IIS;
  • Presença de um HttpModule customizado que estivesse interceptando as requests e "desviando o tiro do alvo" para as url's com extensão .axd;
  • Data/Hora incorretas no servidor (pois é, isso também pode causar esse problema...);
  • Ausência da dll do AjaxToolkit na pasta \bin da aplicação
  • Instalação corrompida do .Net Framework, sendo necessário reparar a instalação

 Investiguei cada ponto e nenhum desses era aplicável ao projeto que eu tinha em mãos...

Como eu resolvi

Antes de tudo, "resolvi" significa "contornei". Meus .axd's continuam sem retornar nada...

O que eu fiz, nesse caso, foi alterar a propriedade "EnableCDN" para true em todos os meus objetos ScriptManager.

Com isso, em vez de fazer o download dos scripts a partir do meu próprio IIS (usando os WebResource.axd e ScriptResource.axd), meus objetos ScriptManagers vão procurá-los, antes, na CDN da Microsoft, e baixam os scripts a partir de lá.

quarta-feira, 14 de dezembro de 2011

jQuery DataTables Plugin

Olá, pessoal!

Pra “aquecer o motor” nesse primeiro post do blog, vamos falar um pouco sobre um plugin muito útil para jQuery, o DataTables.

O DataTables fornece controles avançados de interação para tabelas em HTML. Tudo com umas poucas (pouquíssimas MESMO) linhas de Javascript.

Algumas coisas que o DataTables faz por você (e muito bem):
  • Paginação de resultados
  • Filtragem de dados na tabela
  • Ordenação
Além disso, o framework é altamente flexível, e cada detalhe é altamente customizável.

O DataTables também dá suporte ao ThemeRoller, do jQuery UI. Com isso, você pode aplicar às suas tabelas quaisquer temas do ThemeRoller - desde os “pré-fabricados” até os customizados, construídos por você. O que vai poupar um bocado do tempo que você passaria escrevendo CSS.

Mão na massa

Chega de conversa. Nada mais explicativo que algumas linhas de código pra mostrar o poder e a versatilidade do DataTables.

Antes de mais nada, você vai precisar baixar a versão mais recente do plugin no site oficial do DataTables: http://datatables.net/download/

Agora, crie uma página simples (digamos... HelloDataTables.html), com o seguinte código:

<html>
    <head>
        <title>Hello DataTables</title>
    </head>

    <body>
    <table id="tabela">
        <thead>

        <tr>
            <th>Nome</th>
            <th>Endereço</th>
            <th>Telefone</th>
            <th>E-mail</th>
        </tr>

    </thead>

    <tbody>
        <tr>
            <td>Fulano da Silva</td>
            <td>Rua do Fulano da Silva, 1234</td>
            <td>(11) 1111-111</td>
            <td>fulano@exemplo.com</td>
    </tr>
    <tr>
            <td>Beltrano Oliveira</td>
            <td>Rua do Beltrano, 4321</td>
            <td>(22) 2222-2222</td>
            <td>beltrano@exemplo.com</td>
    </tr>
    <tr>
            <td>Ciclano de Tal</td>
            <td>Rua do Ciclano, 100</td>
            <td>(33)1234-5678</td>
            <td>ciclano@exemplo.com</td>
        </tr>
    </tbody>
</table>
</body>
</html>

Sua página deve ficar parecida com isso:


Voilá! Temos uma tabela com 3 linhas... 4 colunas... e nenhuma graça! Vamos incrementá-la um pouco usando o DataTables.
Primeiro, no final da página (entre as tags </body> e </html>) vamos referenciar os scripts do jQuery, jQuery UI e o DataTables:

</body>
     <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" language="javascript" type="text/javascript"></script>

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

    <script src="jquery.dataTables.min.js" language="javascript" type="text/javascript"></script>

</html>

Certifique-se de copiar o arquivo jquery.dataTables.min.js para o mesmo diretório onde está o HelloDataTables.html. Este arquivo é parte do pacote que você baixou no site do DataTables. No arquivo compactado com o DataTables, ele se encontra no diretório media\js.
Logo após essas referências criaremos o seguinte script:

<script language="javascript" type="text/javascript">

    $(document).ready(function() {

        $('#tabela').dataTable();

    });

</script>
 
Abaixo um screenshot de como vai ficar nossa página:


Perceba que temos um campo de filtro, e podemos controlar também quantos resultados queremos em cada página. Podemos ordenar os resultados clicando nos cabeçalhos de cada coluna.
Temos paginação, ordenação dinâmica dos resultados e filtro, e escrevemos apenas uma linha de Javascript, invocando o método dataTable() para nossa tabela! 

Melhorando a aparência

Voilá outra vez! Agora temos como ordenar, filtrar e paginar nossa tabela. Que continua não tendo graça nenhuma. Então vamos partir pra outra funcionalidade do DataTables e aplicar um tema do ThemeRoller.
Vamos usar um tema pronto. Para isso, vá até http://jqueryui.com/themeroller/#themeGallery
Na lista de temas que aparece do lado esquerdo da página, clique no botão “Download” de qualquer um deles. Em nosso exemplo, vamos usar o tema “UI Lightness”, mas você pode seguir o tutorial com qualquer um deles.
Vamos incluir a seguinte linha dentro do head da HelloDataTable.html para referenciar a folha de estilos customizada com o nosso tema:

    <link href="jquery-ui-1.8.16.custom.css" rel="stylesheet" type="text/css" />

Este arquivo, jquery-ui-1.8.16.custom.css, está no arquivo compactado que você baixou com seu tema do ThemeRoller. Procure-o em \css\ui-lightness (troque "ui-lightess" pelo nome do seu tema, caso tenha baixado outro).
E vamos modificar a linha que cria nosso DataTable. Onde estava:

$('#tabela').dataTable();

Vai ficar:

$('#tabela').dataTable({
    "bJQueryUI": true
});

Definir o parâmetro “bJQueryUI” para “true” instrui o DataTables a utilizar os temas do ThemeRoller.
Começa a ficar apresentável, mas ainda está estranho...
Vamos fazer alguns ajustes em nossa página. Primeiro, vamos criar um arquivo chamado exemplo.css, e vamos estilizar um pouco nossa página. O conteúdo do arquivo será o seguinte:

*

{
font-family: Sans-Serif;
font-size: 10pt;
}


body
{
margin: 20px 0 0 0;
padding: 0;
text-align: center;
}

#wrapper
{
width: 700px;
margin: 0px auto;
text-align: left;
padding: 5px;
}

#tabela
{
width: 700px;
}

E vamos incluir uma referência a nosso arquivo CSS no cabeçalho da página, logo abaixo da referência à folha de estilos do nosso tema.

<link href="exemplo.css" rel="stylesheet" type="text/css" />

Finalmente, vamos envolver todo o conteúdo da página com um <div>.

<div id="wrapper">
… conteúdo da página ...
</div>

Agora, nossa página tem a seguinte aparência:

Abaixo, todo o código-fonte da página até agora:

<html>
<head>
<title>Hello DataTables</title>
    
<link href="jquery-ui-1.8.16.custom.css" rel="stylesheet" type="text/css" />

<link href="exemplo.css" rel="stylesheet" type="text/css" />
  
</head>

<body>

    <div id="wrapper">

        <table id="tabela">
            <thead>

                <tr>
                    <th>Nome</th>
                    <th>Endereço</th>
                    <th>Telefone</th>
                    <th>E-mail</th>
                </tr>

            </thead>

            <tbody>
                <tr>
                     <td>Fulano da Silva</td>
                     <td>Rua do Fulano da Silva, 1234</td>
                     <td>(11) 1111-111</td>
                     <td>fulano@exemplo.com</td>
                </tr>
                <tr>
                     <td>Beltrano Oliveira</td>
                     <td>Rua do Beltrano, 4321</td>
                     <td>(22) 2222-2222</td>
                     <td>beltrano@exemplo.com</td>
                </tr>
                <tr>
                     <td>Ciclano de Tal</td>
                     <td>Rua do Ciclano, 100</td>
                     <td>(33)1234-5678</td>
                     <td>ciclano@exemplo.com</td>
                </tr>
            </tbody>
        </table>
    </div>
</body>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" language="javascript" type="text/javascript"></script>

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

<script src="jquery.dataTables.min.js" language="javascript" type="text/javascript"></script>

<script language="javascript" type="text/javascript">
     $(document).ready(function() {
          $('#tabela').dataTable({
          "bJQueryUI": true
     });
});
</script>
</html>

Algumas melhorias a mais...

Além do parâmetro “bJQueryUI” que usamos antes, o DataTables expõe uma infinidade de outros parâmetros que podemos utilizar para modificar a aparência e o comportamento de nossas tabelas.
Abaixo, vamos usar alguns deles para completar nosso exemplo.
Podemos controlar a forma como o DataTables paginará nossas tabelas. Alguns parâmetros importantes nessa configuração são:
  • bLengthChange: Define se o usuário pode alterar a quantidade de registros exibidos por página. Vamos usar “false” em nosso exemplo;
  • bfilter: Define se os resultados podem ser filtrados. Vamos deixar os filtros habilitados em nosso exemplo;
  • iDisplayLength: Define a quantidade de registros exibidos por página. Vamos usar “10” em nosso exemplo;
Você provavelmente notou um problema com nossa tabela... Todas as mensagens estão em inglês! O DataTables expõe várias propriedades para internacionalização, de modo que possamos customizar ou traduzir essas mensagens conforme a nossa necessidade.
Vamos a algumas dessas propriedades:
  • oLanguage.sSearch: Define o label do campo de busca (filtro) da tabela. Vamos usar “Pesquisar” em nosso exemplo.
  • oLanguage.sInfo : Mostra quantos resultados estão sendo exibidos na tabela.
  • oLanguage.sEmptyTable : Mensagem que será mostrada se a tabela não tiver registros.
  • oLanguage.sInfoFiltered: Mensagem que notifica que os resultados da tabela foram filtrados. Vamos deixar em branco em nosso exemplo.
Portanto, o construtor de nossa tabela ficará assim:
$('#tabela').dataTable({

    "bJQueryUI": true,

    "bLengthChange" : false,

    "iDisplayLength" : 10,

    "oLanguage": {

        "sInfo": "Mostrando registros de _START_ a _END_ de um total de _TOTAL",
        "sInfoEmpty": "Nenhum registro para exibir",

        "sSearch" : "Pesquisar",

        "sInfoFiltered" : ""

    }

});


Agora, nossa página deve ter a seguinte aparência:

Para saber mais...

Esta é apenas uma pequena amostra das capacidades do DataTables. Você pode fazer muito mais com este plugin.
A documentação do DataTables é bem completa, e vale a pena gastar algum tempo vendo as propriedades e eventos disponíveis, e testando alguns deles para incrementar nosso exemplo.
Até a próxima!