Mostrar mensagens com a etiqueta C#. Mostrar todas as mensagens
Mostrar mensagens com a etiqueta C#. Mostrar todas as mensagens

domingo, setembro 13, 2009

Deferred Load ASP.NET/JQuery


Hoje vou descrever uma técnica de como carregar o conteúdo  dinâmico após a pagina ser carregada, isso pode aumentar bastante a experiência com o usuário se você tem que exibir uma quantidade grande de dados, ou pegar algum conteúdo de um servidor externo. A técnica consiste em baixar todo o conteúdo estático, e após o documento estar pronto (ready), fazer uma requisição em AJAX para baixar o conteúdo dinâmico.






Temos dois exemplos um pegando o conteúdo de RSS e montando uma lista via javascript, no segundo caso que renderiza o HTML é um WebControl contendo um Repeater, então vamos para o código.

Antes de mais nada crie um novo projeto Web e monte o mesmo com a estrutura abaixo.




O  FeederLoader.cs representa o Proxy entre a fonte de dados e a requisição AJAX, nela usamos o WebClient para baixar um RSS e devolvemos para o navegador, e o código dela e este:


public class FeederLoader
{
    public FeederLoader()
    {
    }

    public string Load(string url)
    {
        WebClient client = new WebClient();
        byte[] buffer = client.DownloadData(url);

        return UTF8Encoding.UTF8.GetString(buffer);
    }

O método Load baixa o conteúdo da url usando o WebClient e devolve como uma String UTF-8.
Partimos agora para o Handler FeederJS.ashx


public void ProcessRequest (HttpContext context) {
        context.Response.ContentType = "text/xml";
        FeederLoader Loader = new FeederLoader();
        
       System.Threading.Thread.Sleep(1000); //só pra ficar cool

        string rss = Loader.Load("http://newbie-x11.blogspot.com/feeds/posts/default?alt=rss");
        context.Response.Write(rss);
        context.Response.End();
        
    }

No método ProcessRequest usamos o FeederLoad pra baixar o RSS deste blog :-] , e devolver para o XML navegador.
Agora a página Web, EmJavaScript.aspx


<style type="text/css">
        
        .Loading
        {
            background-image: url('images/loader.gif');
            background-repeat:no-repeat;
        }
        
    </style>

  <div id="master">
            <div id="static">
                Como as vezes tenho uma alta enorme de creatividade não vou escrever nada aqui.
            </div>
            <div id="dynamic">
            </div>
        </div>
        <div>

            <script type="text/javascript" src="js/jquery-1.3.2.min.js"></script>
            <script type="text/javascript">
            $(document).ready(function(){
            var ajaxOption = {
                        type: "GET",
                        url: "handlers/FeederJS.ashx",
                        dataType : "xml",
                        success : function(data){
                            var html = "<ul>"
                            $(data).find('item').each(function(){
                                var titulo = $(this).find('title').text();
                                html += "<li>"+titulo+"</li>";
                            });
                            
                            html += "</ul>";
                            
                            $("#dynamic").append(html);
                            $("#dynamic").attr("class","");
                        }
                    };
                    
                    $("#dynamic").attr("class","Loading");
                    
                    $.ajax(ajaxOption);
                    
            });
            
          
            </script>

        </div>

Acima antes de fazer a requisição em AJAX , mudamos o CSS do elemento que abriga o conteúdo dinâmico para a classe que contem a imagem carregando, na Requisição (success), fazemos o loop no xml e montamos uma lista, preenchemos o conteudo carregado no elemento, e removo a classe de CSS .Loading, e Badabum..

Agora se você não quer ficar montando conteudo via JavaScript demonstro abaixo com poucas modificações, fazemos a mesma coisa só que usando o um Repeater e um User Control, vamos lá crie o Controle, então adicione um novo Web User Control com o nome de Listageml.ascx, e o código dele segue abaixo.


<%@ Control Language="C#" AutoEventWireup="true" CodeFile="Listageml.ascx.cs" Inherits="controles_Listageml" %>
<div>
    <asp:Repeater ID="Repeater1" runat="server">
        <HeaderTemplate>
            <ul>
        </HeaderTemplate>
        <ItemTemplate>
            <li>
                <%# Eval("title") %>
            </li>
        </ItemTemplate>
        <FooterTemplate>
            </ul></FooterTemplate>
    </asp:Repeater>
</div>

O .cs:


protected void Page_Load(object sender, EventArgs e)
    {
        XmlTextReader reader = new XmlTextReader("http://g1.globo.com/Rss2/0,,AS0-5597,00.xml");

        DataSet ds = new DataSet();

        ds.ReadXml(reader);
        Repeater1.DataSource = ds.Tables["item"];
        Repeater1.DataBind();
    }

Creio que não seja necessário comentar o bloco acima, agora vamos ao Handler FeederASP.ashx, que tem modificações significativas:


public void ProcessRequest (HttpContext context) {
        
        Page page = new Page();
        UserControl ctl = (UserControl)page.LoadControl("~/controles/Listageml.ascx");
        StringWriter writer = new StringWriter();

        System.Threading.Thread.Sleep(1000); //só pra ficar cool
        
        page.Controls.Add(ctl);
        HttpContext.Current.Server.Execute(page, writer, false);

        context.Response.ContentType = "text/plain";
        context.Response.Write(writer.ToString());
        context.Response.End();
    }

Acima usamos estas linhas malucas para renderizar o HTML do controle, se tiver a necessidade de passar parâmetros para o controle pode usar os atributos, mas compensa dar um passeio pelo Google e ler sobe WebControls.
Passamos agora para o arquivo EmASP.aspx, a única mudança é no Java script:


<script type="text/javascript">
            $(document).ready(function(){
            var ajaxOption = {
                        type: "GET",
                        url: "handlers/FeederASP.ashx",
                        dataType : "text",
                        success : function(data){
                         
                            
                            $("#dynamic").append(data);
                            $("#dynamic").attr("class","");
                        }
                    };
                    
                    $("#dynamic").attr("class","Loading");
                    
                    $.ajax(ajaxOption);
                    
            });
</script>

Simplificou bastante, aqui só pegamos o conteúdo pronto e colocamos no elemento.
Pronto é isso, pode baixar o código fonte aqui.

segunda-feira, janeiro 26, 2009

Performace: DataSet x XPath

Aqui no trabalho me mandarão otimizar a forma como o XML é manipulado (Muitos arquivos, muitos mesmo), aém de diminuir o número de chamadas para esses arquivos também me surgiu de otimizar a gravação que era feita com DataSet fiz um Benchmark da alteração de um único registro usando DataSet x XPath, o ganho com XPath foi de 900ms (bem interessante não?)

só desculpa pela formatação do código eu o template do blogger e o FireFox não entramos em um acordo. logo vou corrigir.

   1:  //usando DataSet
   2:  public static bool alterarCampo(string path, int id, Parametro parametro)
   3:          {
   4:              bool resultado = false;
   5:              DataSet dataSet = new DataSet();
   6:  
   7:              try
   8:              {
   9:                  dataSet.ReadXml(path);
  10:  
  11:                  dataSet.Tables[0].DefaultView.RowFilter = "ID = " + id.ToString();
  12:                  dataSet.Tables[0].DefaultView[0][parametro.Campo] = parametro.Valor;
  13:                  dataSet.Tables[0].WriteXml(path);
  14:  
  15:                  resultado = true;
  16:              }
  17:              catch (Exception)
  18:              {
  19:  
  20:                  resultado = false;
  21:              }
  22:              
  23:  
  24:              dataSet = null;
  25:              return resultado;
  26:          }
  27:  
  28:  //Usando XPath
  29:  public static bool alterarCampo(string path, int id, Parametro parametro)
  30:          {
  31:              bool resultado = false;
  32:              try
  33:              {
  34:                  StreamReader stream = new StreamReader(File.Open(path, FileMode.Open));
  35:                  XmlDocument doc = new XmlDocument();
  36:  
  37:                  string xPathStr = String.Format("/CATALOGO/LIVRO[ID = '{0}']", iclipping);
  38:                  doc.LoadXml(stream.ReadToEnd());
  39:                  stream.Close();
  40:                  stream = null;
  41:  
  42:                  XPathNavigator navigator = doc.CreateNavigator().SelectSingleNode(xPathStr);
  43:                  XPathNavigator nCampo = navigator.SelectSingleNode(parametro.Campo);
  44:  
  45:                  //O elemento nao existe
  46:                  if (nCampo == null)
  47:                  {
  48:                      navigator.AppendChild(String.Format("<{0}>{1}</{0}>", parametro.Campo, parametro.Valor));
  49:                  }
  50:                  else //logo existe
  51:                  {
  52:                      nCampo.SetValue(parametro.Valor);
  53:                  }
  54:  
  55:                  FileStream fstream = File.Open(path, FileMode.Open);
  56:                  doc.Save(fstream);
  57:                  fstream.Close();
  58:                  resultado = true;
  59:              }
  60:              catch (Exception)
  61:              {
  62:                  resultado = false;
  63:  
  64:              }
  65:  
  66:              return resultado;
  67:          }
Quanto aos resultados foi esse p número que consegui (Visual Studio Test)


ModificarXML alterarCampo(string path, int iclipping, Parametro parametro) BOX71 22/01/09 11:26 22/01/09 11:26 00:00:00.3165278 Xpath

ModificarXML alterarCampo(string path, int iclipping, Parametro parametro) BOX71 22/01/09 11:50 22/01/09 11:50 00:00:01.2892166 DataSet


Maquina : Intel 2140 2x1.6GHz 2GB Ram