Fernando Cerqueira 

Consultor , Arquiteto , Especialista Microsoft .NET

Home | Blog

Abaixo uma leitura de nosso Blog que mantemos no site Linha de Código.

Next Step Place 1/5
Blog by Fernando Cerqueira
Debugando pelo codigo fonte do .NET COM VS2008 (3/10/2007 15:41:00)
Sim agora é possível , já podemos debugar e entender melhor o funcionamento do  Framework .NET!


Veja na integra  mais detalhes pelo blog do grande 
ScottGu
Sys is undefined : “Ajax X Hora do Sistema”. AJAX ou .NET Framework ? (23/9/2007 11:52:00)

Introdução:

Já faz um bom tempo que não posto nada e aqui uma explicação: Transferi-me para São Paulo e ate ajustar tudo esta sendo bem mais demorado do que previa...

Costumo sempre dar uma pesquisa pela web sobre AJAX para ver como a comunidade vem absorvendo esta funcionalidade que hoje é indispensável para qualquer aplicativo web.  

Um post  em  um blog me chamou atenção hoje.  Não pelo seu conteúdo, mas pela dúvida gerada dentro de um cenário que dificilmente acontece.  Como lá só tinha o problema que apontava para o AJAX , sem a causa e explicação dos motivos resolvi escrever este pequeno post para compartilhar com a comunidade alguns fundamentos e conhecimento sobre este cenário . Afinal não adianta nada trazer problemas sem soluções e a devida pesquisa , isso pode leva a outra conclusão.

Qual é o cenário que estamos  falando :

Você esta criando um aplicativo web  habilitado para usar Ajax. A aplicação criada esta correta, o arquivo de configuração esta perfeito e o servidor também este corretamente configurado com os assemblys do AJAX devidamente registrado no GAC.  Ao executar o aplicativo aparece a mensagem de  “Sys is undefined”.  Verificando a Data do servidor você percebe que a Data esta errada (com alguns anos para trás...). A solução imediata é simples : É só corrigir a Data, porem o que chamou atenção foi justamente a dúvida deixada :

“Mas por que diabos recusar uma data desatualizada, só porque ele não existia na época? Tudo bem que sabe-se lá porque alguém precisaria disso, mas e se eu precisar do meu servidor com a data em 2002 ?”

A dúvida é mais que justificada!  porem não existe nenhuma relação com a existência ou não do produto na época.

A idéia deste post é entender melhor o que esta acontecendo, como funciona o AJAX ASP. NET e alguns outros detalhes do Framework .NET . Ao final descobrir as respostas e dar um “workarround” para este cenário caso seja necessário.

Para se chegar à explicação iremos precisar usar uma ferramenta(que inclusive já citei neste blog) como fundamentais para qualquer desenvolvedor:

http://linhadecodigo.com.br/cs2/blogs/fcerqueira/archive/2007/02/22/588.aspx

“Refletor (Embora muitos achem que a intenção é descompilar e ver o código fonte, estas ferramentas tem uma tarefa mais nobre :  Facilitar o entendimento do .NET framework,
Ajudar a melhorar a performance e reduzir os cast, Uma grande fonte de aprendizado”

A mensagem de Erro para o usuário :

“Microsoft JScript runtime error: 'Sys.WebForms.PageRequestManager' is null or not an object”

Esta mensagem normalmente  ocorre quando não temos instalado (diga-se registradas) as dlls do AJAX. Mas esta mensagem (neste nosso caso) é uma conseqüência e não origem do erro uma vez que tudo esta registrado  de forma correta.

Entendendo melhor:

O AJAX é formado por um conjunto de bibliotecas que estão no lado do servidor e no lado do cliente.  Quando tornamos nossos aplicativos habilitados para Ajax as requisições são interceptadas pela biblioteca que esta no servidor que executa o devido tratamento, inclusive incluído na página que retorna para o cliente os script necessários   (Veja o exemplo abaixo) :

...

O  ScriptResource.axd  é um recurso introduzido no Framework 2.0 que permite trabalhar com os resouces anexados no Assembly . Sua estrutura  é desta forma :

WebResource.axd?d=recurso & t =Tempo.   "d" significa o recurso da Web solicitada que vem encripitado e o "t" é a data/hora para o assembly solicitado. O parâmetro “t” ajuda a identificar se houve alterações feitas o recurso.

Para quem desejar saber um pouco mais, abaixo um artigo no MSDN que trata deste assunto :

http://support.microsoft.com/kb/910442/pt-br

A mensagem de Erro de origem:

Agora conhecendo melhor esta premissa de como os script são gerados e enviados, podemos partir para a origem do problema, testando a execução dos script  que retorna a mensagem real de origem abaixo :

Specified argument was out of the range of valid values.
Parameter name: utcDate

...
Exception Details: System.ArgumentOutOfRangeException: Specified argument was out of the range of valid values.
Parameter name: utcDate

...
Stack Trace:

 [ArgumentOutOfRangeException: Specified argument was out of the range of valid values.

Parameter name: utcDate]

   System.Web.HttpCachePolicy.UtcSetLastModified(DateTime utcDate) +3299747

   System.Web.HttpCachePolicy.SetLastModified(DateTime date) +47

 mpletedSynchronously) +64

...

 

Ok, até aqui quase nenhuma novidade. Apenas identificamos a origem do erro, mas ainda restam duas perguntas: Porque deu este erro?  Como posso contornar o problema mantendo a data do servidor (mesmo que desatualizada)?  Vamos então continuar para termos  as respostas J

Porque deu este erro ?

Para responder vamos usaremos dois recursos: O Refletor (para ver a lógica e codigo que o framework utiliza)  e o código fonte do AJAX.NET .

Para quem não sabe o código fonte do AJAX.NET  esta disponível para estudo e para debug em :
http://www.microsoft.com/downloads/details.aspx?FamilyID=ef2c1acc-051a-4fe6-ad72-f3bed8623b43&DisplayLang=en

O erro apresentado é no método  UtcSetLastModified da classe  System.Web.HttpCachePolicy. 
Usando o Refletor temos os seguintes códigos abaixo:

public
void SetLastModified(DateTime date)
{
    DateTime utcDate = DateTimeUtil.ConvertToUniversalTime(date);
this.UtcSetLastModified(utcDate);
}
private void UtcSetLastModified(DateTime utcDate)
{
   utcDate = new DateTime(utcDate.Ticks - (utcDate.Ticks % 0x989680L));

    if (utcDate > DateTime.UtcNow)
    {
       throw new ArgumentOutOfRangeException("utcDate");
    }

if (!this._isLastModifiedSet || (utcDate > this._utcLastModified))
   {
        this.Dirtied();
        this._utcLastModified = utcDate;
        this._isLastModifiedSet = true;
  }
}

O Texto em vermelho grifado é o trecho de código responsável pela mensagem de erro, ou seja: caso a data passada seja maior que a data corrente ocorre uma exception.

Mas que Data é essa que é passada como parâmetro?

Agora precisamos usar o segundo recurso de analise: O Código fonte do AJAX ASP.NET.

Como estamos tratando uma requisição de resource já explicada, se buscarmos pelo titulo dos arquivos iremos encontrar um arquivo de nome: ScriptResourceHandler.cs 
 e dentro dele temos o método abaixo :

private static DateTime GetLastWriteTime(Assembly assembly)
{
            string codeBase = GetCodeBaseWithAssert(assembly);
            Uri codeBaseUri = new Uri(codeBase);
            if (!codeBaseUri.IsFile) return DateTime.MinValue;
                string localPath = codeBaseUri.LocalPath;
            FileIOPermission p = new FileIOPermission(FileIOPermissionAccess.Read, localPath);
            p.Assert();
            return File.GetLastWriteTime(localPath);
}


Analisando o código o leitor poderá perceber que o parâmetro passado é a data do assembly que possui o resouce a ser incluído.

Explicando o motivo de erro apresentado:

De um lado temos um assembly que foi gerado com uma determinada Data, e de outro lado temos a Data corrente do servidor .

Como a Data do assembly é superior a data Corrente do servidor temos uma inconsistência que é verificada pelo Framework! Ou seja, o erro gerado não é do AJAX e sim do próprio  .NET framework  que executa a verificação  de data.

Respondendo a segunda pergunta:  Caso seja necessário ter um servidor com data desatualizada e ainda assim executar assemblys que foram gerados com data superior a data corrente ?

Como se trata de um cenário extremante atípico precisamos  fazer um
“workarround”  (contornar o problema) . A forma mais simples é modificar a data do assembly e para isso podemos usar váriso aplicativos para este fim, um deles  é o File Touch utility  http://www.jddesign.f2s.com/touchpro.htm.

Conclusão :

Objetivo deste artigo/post foi apenas detalhar algumas funcionalidades que muitas das vezes passa despercebida pela maioria dos desenvolvedores e reforçar alguns conceitos sobre o desenvolvimento na web.

Um dos pontos que sempre reforço nos trabalhos de mentoring  que exerço é a necessidade de se conhecer bem os fundamentos  da web e das tecnologias  envolvidas para podemos ter uma compreensão melhor dos cenários e problemas que sempre irão surgir e com isso podemos fazer uma analise mais clara das origens dos problemas evitando-se com isso se chegar a conclusões que podem ser equivocadas.  Claro que este cenário apresentado não é trivial e requer uma analise em maior profundidade para se tirara algumas conclusões.

Espero ter dado uma pequena parcela de ajuda para que os desenvolvedores se interessem em compreender melhor os fundamentos e não apenas os resultados.
Testando WebParts com o ASP.NET AJAX 1.0 (28/3/2007 9:20:00)
Ontem fiquei fazendo alguns testes com webparts e Ajax e resolvi criar este resumo e compartilhar com vocês :

Posso usar webparts com ajax ?

Minha resposta SIM.

Para usar webparts com ajax  não preciso fazer nada ?

Minha resposta e não, você precisa fazer sim! Infelizmente nem todas as funcionalidades de webparts  foram implementadas , tanto é que ela não faz parte da versão RTM e sim da versão “Future” . Veja as limitações (sem fazer nada) em

http://forums.asp.net/thread/1545256.aspx 
ou em
http://blogs.msdn.com/mharder/archive/2007/01/23/webparts-and-asp-net-ajax-1-0.aspx

Então só posso Ajax com a versão “FUTURE” ?

Minha resposta e não, PODE IR ALEM, VOCE PODE USAR WEBPARTS COM A VERSÃO ASP.NET AJAX  1.0!!!!!!!!!!!!!!!!!.

E posso usar Ajax com Webparts em produção?

Minha resposta e não, mas por quê?  Por que embora as funcionalidades de Drag and Drop passem a funcionar com as soluções que demonstro mais abaixo, ainda precisa ser amadurecida e melhorada, inclusive se pensando em cross-browser. 

O Objetivo aqui  é compartilhar o conhecimento e mostrar que é possível fazer funcionar webparts com Ajax , quem acompanhou a evolução lembra que durante os ctps  a compatibilidade entres os browses veio depois , como em qualquer amadurecimento de código.

Mas como é possível? Já li que isso é mito uma lenda não é fato....

Minha resposta é que leia ate o final e depois tire a conclusão do que é lenda e que fato e pode ser feito, e conclua você mesmo, afinal não estou aqui para ficar demostrando o que não funciona e sim para compartilhar conhecimento  e aprender junto com a comunidade , ou seja com vocês.


Mas esta solução é sua?

Não. Estava trabalhando em uma solução para este cenário, porem meu trabalho foi abreviado por encontrar uma solução bem mais adiantada e seguindo o mesmo raciocínio que  o meu.

Mas afinal qual é a Mágica?

Bem antes de mostrar como é a mágica, vamos entender o que não funciona:

Uma das principais funcionalidades de usar webparts é você poder mover as webparts entre as webzones. Uma das coisas desagradáveis era que quando fazia isso para cada webparts que você movia era executado um postback e página era toda carregada.

Com a chegada do AJAX  isso pareceu ter sido resolvido de uma forma  muito elegante não ocorrendo mais o postback, tornando a usabilidade bastante atraente. Infelizmente  esta funcionalidade com o Ajax foi retirada nas versões betas e também  na versão RTM 1.0, sendo deixada para ser implementada em versões futuras.
Quando colocamos as webparts e o wepartmananger dentro do updatepanel o drag and drop  não funciona corretamente.  

Mas porque não funciona? 

É aqui que começa a mágica
.  Se observar o comportamento quando esta dentro do updatepanel ira perceber que funciona na primeira vez e depois quando é feito o “Partial render” pelo scriptmananger não é possível mais executar o drag and drop.  Este efeito indesejado esta centrado em um único componente: 

O webpartmananger.

O webpartmananger  que é responsável por gerar e registrar os scripts com as funcionalidades. Este script é que são os responsáveis pela funcionalidade Drag and drop, os Verbos dos menus e outras funcionalidades das webparzones.

Desta forma então será o webpartmananger que teremos que modificar para que o webparts funcione corretamente.

Mas porque precisamos modificar ele (webpartmananger) ? 

Porque quando colocamos ele dentro do updatepanel durante o primeiro render ele carrega os script  e executa as funcionalidades, mas depois do primeiro “Partial render” o Webpartmananger não consegue mas registrar os scripts e “se perde” criando exatamente  o comportamento  já descrito de apenas executar somente 1 vez, isso se explica porque o responsável por isso é o scriptmananger quando trabalhamos com  AJAX.
Simples não é..... São estes conceitos que ajudam a você a resolver cenários onde existe  pouca documentação e recursos existentes.

Vocês já leram  também pelo meu blog que os validators também  tem problemas quando usando dentro do updatepanel (principalmente dentro de templates de grids que estão dentro dos updatepanels) . A solução para os validtors foi mapear as classes responsáveis pelos controles para outra classe que resolver o problema.

A solução para webparts é feita da mesma forma! Justamente por este caminho que estava trabalhando quando me deparei com outra solução feita por outro desenvolvedor segundo exatamente esta linha!

Mas o que deve ser feito?

Primeiro criar  uma classe que herde do WebpartMananger e sobre-escrever o RenderClientScript (para se ter o controle do se se escreve na pagina). Com isso registramos os scripts pelo System.Web.UI.ScriptManager e não mais pelo System.Web.UI.ClientScriptManager

Esta simples mudança faz com que os script registrados sejam executados a cada refresh do updatepanel  que  é controlado pelo scriptmananger.

Estava bem próximo disso quando encontrei esta solução que estou descrevendo. 

Para completar a solução será necessário mapear as  classes do System.Web.UI.WebControls.WebParts.WebPartManager  para a classe que esta sendo criada da mesma forma que foi feita a solução para os validators.

<configuration>
   
       
           
                        tagType="System.Web.UI.WebControls.WebParts.WebPartManager"
            mappedTagType="Sample.Web.UI.WebParts.WebPartManager, Sample.Web.UI.WebParts"
            />
           

       




E o código ? 
Vamos a explicação de algumas partes dele :

Como já falamos a classe herda de System.Web.UI.WebControls.WebParts.WebPartManager e o trecho abaixo mostra como esta sendo registrado o script necessário junto ao ScriptManager e depois durante a statup para garatir o resfresh do updatepanel.

Public Class WebPartManager
    Inherits System.Web.UI.WebControls.WebParts.WebPartManager

    Protected Overrides Sub RegisterClientScript()
        If Me.CheckRenderClientScript Then

            System.Web.UI.ScriptManager.RegisterClientScriptResource(Me,
            GetType(System.Web.UI.WebControls.WebParts.WebPartManager),
             "WebParts.js")

            System.Web.UI.ScriptManager.RegisterStartupScript(Me,
            Me.GetType, Me.ID & "_Script", Me.Script, True)

        End If
    End Sub

Abaixo parte de o script  necessário ser executado durante o startup.

    Private ReadOnly Property Script() As String
        Get
            Dim colorConverter As New System.Web.UI.WebControls.WebColorConverter
Dim _clientScript As String = String.Format("__wpm = new
 WebPartManager();{0}" & _
            "__wpm.overlayContainerElement =
 document.getElementById('{2}___Drag');{0}" & _
            "__wpm.personalizationScopeShared = {1};{0}" & _
"var zoneElement;{0}var zoneObject;{0}",
ControlChars.CrLf, Me.Personalization.CanEnterSharedScope.ToString.ToLower, Me.ClientID)
            For Each z As WebPartZone In Me.Zones……
            Return _clientScript
        End Get
    End Property

Você deve estar se perguntando como é possível saber que código escrever  para se chegar a este resultado ... Aqui Tb não existe nenhuma lenda nem nenhum “super guru” ..rs.rs, quem acompanha meu blog em  http://linhadecodigo.com.br/cs2/blogs/fcerqueira deve se lembra de uma dica que dei da nova versão do :

“Refletor 5.0 - Uma ferramenta indispensável” (http://linhadecodigo.com.br/cs2/blogs/fcerqueira/comments/588.aspx

No blog disse

“Embora muitos achem que a intenção é descompilar e ver o código fonte, estas ferramentas tem uma tarefa mais nobre :  Faciliar o entendimento do .NET framework.  Ajudar a melhorar a performance e reduzir os cast Uma grande fonte de aprendizado” 

E ai esta a outra parte da mágica, aprender  mais sobre o funcionamento vendo como é realizado as rotinas.

Uma pergunta final que pode ser feita : Esta solução é crossbrowser ?

Infelizmente não é, por enquanto esta solução esta compatível apenas com o Internet Explorer , porem mostra  que com um pouco de conhecimento pode-se se chegar a uma solução. O  legal de compartilhar conhecimento é poder mostrar os avanços, os caminhos . Compartilhando estes caminhos e códigos outros desenvolvedores aprendem melhor o funcionamento e pode evoluir a solução até chegar a uma maturidade de código que permita o uso em usa plenitude

Onde posso pegar todo o código fonte ?

Esta em um thread no fórum  www.asp.net  : http://forums.asp.net/thread/1621227.aspx 

Quem inica a thead e o propio autor da classe, a ele que devemos dar o grande parabens . De minha parte estou apenas ajudando a divulgar e compartilhar com vocês.

A todos um grande abraço