Outubro 2006 - Posts
Uma vez que me voltaram hoje a colocar a mesma questão, "tenho um problema, inserção dupla na bd ao fazer page refresh..." resolvi falar um pouco sobre este assunto.
Isto é um problema que acontece para quem está a programar "tudo à mão" no asp a inserção e remoção de dados através por exemplo de uma grid, assim que há um page refresh o comando é repetido. Ou seja ficamos com dados duplicados, ou registos apagados indevidamente.
A inovação da versão 2.0 (asp) veio facilitar este (entre outros) problema, uma vez que o controlo Grid associasse ao SqlDataSource, e juntamente aos comandos sql que lhe configuramos. Portanto se no nosso sqldatasource, definirmos, comandos de selecção, actualização e remoção de dados, assim que associarmos a Grid ao controlo automaticamente ficamos com os comandos para executar estas tarefas, de uma forma segura e sem problemas de page refresh.
Inclusivé ficam disponíveis para a Grid poder ter esses comandos (Delete, Select, Sort).
Mas para quem necessita de programar estes comandos à mão existem algumas soluções, como:
- Clearing the header
- Detecting Browser’s refresh
- Trapping at database level
Existe um bom artigo que descreve cada uma destas soluções, apresenta alternativas, tempos, comparações e com exemplos completos.
http://aspalliance.com/687
A minha recomendação vai para o SqlException, é das mais rápidas e o controlo está sobre a Base de dados, sendo na minha opinião a mais segura.
Este grande e desejado evento aproxima-se cada vez mais! Quem quiser ir, ainda vai a tempo. Já sonhamos com ele (lol), a expectativa é muito alta e sinto que ainda vou ser surpreendido e voltar com a pena de ter acabado...
Espero que seja das melhores experiências da minha vida! Além de esperar de apreender muito, conto com troca de experiências com pessoas de todo o mundo, pessoas que usam a mesma tecnologia, programadores, estudantes, etc, e claro espero também pela componente de diversão subjacente a um evento desta dimensão!
Contem com post's vindos directos de Barcelona, sobre ASP e companhias. Tentarei fazer um diário e partilhar convosco o melhor do que vi e do que senti.
Agora só resta esperar pelo dia mágico!

Muita gente já me perguntou como se pode fazer streaming de audio/video com aspx. Ao que tudo indica parece que está na moda...
Existe várias formas de o fazer, quer com servidores de streaming dedicados, LiveCommunications Server, etc. Vamos ver como podemos resolver o problema de uma forma muito simples, para isso basta os seguinte requisitos:
- Windows Server 2003
- .net Framework 2.0
- IIS (Internet Information Services)
- Windows Media Services (incluídos no Windows 2003 Server)
O primeiro passo é configurar o servidor, aliás perde-se mais tempo a configurar a máquina do que por a webapp com streaming. Para tal, tem que se adicionar o Windows Media Services: ir ao Control Panel, Add/Remove Software, Windows Components, e seleccionar o Windows Media Services. Depois de instalados, abrimos a sua consola que tem um aspecto muito simpático e nos facilita a tarefa.
Coisas a ter em conta:
Este servidor trabalha sobre protocolos de comunicação:
RTSP with TCP-based transport (RTSPT)
RTSP with UDP-based transport (RTSPU)
HTTP transport
E portanto para cada ambiente de rede, é necessário fazer configurações de Firewall e portos de comunicação, sendo:
Delivering a unicast stream
| Application Protocol |
Protocol |
Port |
Description |
RTSP |
TCP |
554 (In/Out) |
Used for accepting incoming RTSP client connections and for delivering data packets to clients that are streaming by using RTSPT. |
RTSP |
UDP |
5004 (Out) |
Used for delivering data packets to clients that are streaming by using RTSPU. |
RTSP |
UDP |
5005 (In/Out) |
Used for receiving packet loss information from clients and providing synchronization information to clients that are streaming by using RTSPU. |
MMS |
TCP |
1755 (In/Out) |
Used for accepting incoming MMS client connections and for delivering data packets to clients that are streaming by using MMST. |
MMS |
UDP |
1755 (In/Out) |
Used for receiving packet loss information from clients and providing synchronization information to clients that are streaming by using MMSU. |
MMS |
UDP |
1024-5000 (Out) |
Used for delivering data packets to clients that are streaming by using MMSU. Open only the necessary number of ports. |
HTTP |
TCP |
80 (In/Out) |
Used for accepting incoming HTTP client connections and for delivering data packets to clients that are streaming by using HTTP. | | | |
Também tem serviços de autenticação, ver nas configurações do publish point.
Este serviço adiciona um "servidor" virtual de media, e por isso funciona como o IIS, tem os directórios de ficheiros e "virtual paths". Tem opções muito boas para quem queira fazer disto "o prato do dia" como:
- Cache/Proxy Management, permite fazer plug-ins próprios para o nosso servidor de streaming, bem como alterar definições de cache das transmissões, etc.
- Publish Point (que nos interessa particularmente, para este post), este é semelhante aos directórios virtuais do IIS! Portanto a tarefa é fácil, basta criar um publish point e seguir o wizard indicando o(s) ficheiro(s) que queremos nesse publish point, tal como se fosse um site. No final temos um URL para os nossos ficheiros, por ex: mms://nosso.servidor.com/videos/exemplo_streaming.wma, tanto para vídeo como para música. Com este URL podemos abrir directamente no MediaPlayer ou noutro compatível.
Mas se o objectivo é correr em aspx, o que o servidor de media faz, é criar um ficheiro .wsx, é uma playlist referente ao ficheiros que temos nesse directório para depois funcionar no Player da mesma forma como as vulgares playlists de músicas. Agora do lado do aspx a coisa torna-se fácil, das duas uma, ou inserimos na nossa página o plugin do media player e no lugar da source do vídeo trabalhamos uma função nossa dinâmica, ou podemos criar o ficheiro contendo os nome e musicas que queremos transmitir.
Exemplos:
- Plugin inserido na página com função dinâmica da source:
public void get_video()
{
if (return_video_string=="")
Response.Write("");
else
Response.Write(@" <object classid=""clsid:6BF52A52-394A-11D3-B153-00C04F79FAA6"" id=""WindowsMediaPlayer1"">
<param name=""URL"" value=""" + return_video_string
+ @"""<param name=""rate"" value=""1"">
<param name=""balance"" value=""0"">
<param name=""currentPosition"" value=""0"">
<param name=""defaultFrame"" value>
<param name=""playCount"" value=""1"">
<param name=""autoStart"" value=""-1"">
<param name=""currentMarker"" value=""0"">
<param name=""invokeURLs"" value=""-1"">
<param name=""baseURL"" value>
<param name=""volume"" value=""50"">
<param name=""mute"" value=""0"">
<param name=""uiMode"" value=""full"">
<param name=""stretchToFit"" value=""0"">
<param name=""windowlessVideo"" value=""0"">
<param name=""enabled"" value=""-1"">
<param name=""enableContextMenu"" value=""-1"">
<param name=""fullScreen"" value=""0"">
<param name=""SAMIStyle"" value>
<param name=""SAMILang"" value>
<param name=""SAMIFilename"" value>
<param name=""captioningID"" value>
<param name=""enableErrorDialogs"" value=""0"">
</object> "
);
}
Desta forma inserimos o plugin na página com a string que queremos como source do ficheiro.
- Outra forma, usando o ficheiro .asx, .wsx de forma a mandar várias musicas ou vídeos (tipo playlist) podemos escrever um ficheiro do género:
<ASX VERSION="3.0" PREVIEWMODE="NO" BANNERBAR="auto" >
<ENTRY >
<TITLE>Sample Song</TITLE>
<REF HREF="http://sample.microsoft.com/sample1.mp3" />
</ENTRY>
</ASX>
O ficheiro é simples e para cada musica/vídeo é só adicionar uma nova entrada <ENTRY>, isto é muito semelhante ao XML mas não é, os elementos não são case sensitive e o mediaplayer não aceita determinadas tags. Mas mesmo assim podemos escrever uma função deste género para nos criar os ficheiros asx:
MemoryStream CreateASX(SortedList songs, string server)
{
const string PUBPOINT = "OurMusic";
XmlDocument asx = new XmlDocument();
XmlElement element = asx.CreateElement("ASX");
element.Attributes.Append(asx.CreateAttribute("VERSION"));
element.Attributes["VERSION"].Value = "3.0";
element.Attributes.Append(asx.CreateAttribute("PREVIEWMODE"));
element.Attributes["PREVIEWMODE"].Value = "NO";
element.Attributes.Append(asx.CreateAttribute("BANNERBAR"));
element.Attributes["BANNERBAR"].Value = "auto";
XmlElement title = asx.CreateElement("TITLE");
title.InnerText = "Shawn Wildermuth's Remote Media Player";
element.AppendChild(title);
foreach (DictionaryEntry entry in songs)
{
FileInfo file = entry.Value as FileInfo;
if (file != null)
{
XmlElement media = asx.CreateElement("ENTRY");
XmlElement href = asx.CreateElement("REF");
href.Attributes.Append(asx.CreateAttribute("HREF"));
href.Attributes["HREF"].Value =
string.Format("{0}{1}{2}",
server,
PUBPOINT,
file.FullName.Substring(ROOTDIR.Length).Replace("\\", "/"));
XmlElement mtitle = asx.CreateElement("TITLE");
mtitle.InnerText = file.Name;
media.AppendChild(mtitle);
media.AppendChild(href);
element.AppendChild(media);
}
}
asx.AppendChild(element);
MemoryStream stream = new MemoryStream();
StreamWriter writer = new StreamWriter(stream);
writer.Write(asx.OuterXml);
writer.Flush();
return stream;
}
Desta forma podemos inserir o streaming directamente na nossa webapp, e gozar destas potencialidades fabulosas.
Já podem fazer broadcast do que quiserem :P
Voltando ao Assunto principal, Sql Caching, e depois de já termos falado sobre conceitos básicos de page caching, vamos então ver técnicas de SqlDataSource Caching. Portanto o objectivo é numa página em que tenhamos uma (ou várias), DataGrid's ou tabela de dados, podemos fazer cache de maneira a não estar sempre a "pingar" o SqlServer, e claro poupando muito, principalmente velocidade! Portanto a este procedimento chamamos de SqlCaching, e pode ser feito de duas formas:
- Nova notification table na base de dados. Esta técnica baseia-se numa tabela auxiliar agregada à nossa base de dados que vai conter as alterações feitas à base de dados, (notificação) e através de um Triger invalida a cache. Desta forma existe apenas uma "Query Ping" (de x em x tempo), muito leve sobre o servidor e caso haja alteração de dados esta tabela invalida a cache e por sua vez recarrega com os dados novos. Compatível com SqlServer 2000 e 2005.
- "CommandNotification", esta técnica é nova e foi introduzida com o SqlServer 2005, o que acontece é que passa a ser o sqlserver a informar á nossa webapp que a cache é inválida e fornece os novos dados. Para tal usamos na linha do
<%@ OutputCache Duration="172800" VaryByParam="none" SqlDependency="CommandNotification"%>
mas alterando a propriedade do SqlDependency para CommandNotification. É uma forma muito leve, e a mais eficiente em gastos de processador e velocidade, contudo obriga a dar permissões sobre a base de dados que por vezes podem não ser as mais seguras. É necessário também para esta técnica activar o ServiceBroker, que por sua vez activa o command notification..
Já existe muita informação na web sobre estas técnicas, tuturials, vídeos, etc. Portanto o meu objectivo centra-se na comparação e prova da melhor solução, contudo tudo varia de caso para caso. Vamos seleccionar alguns casos (perto da realidade) e analisaremos qual a técnica a usar. Porque muitas das vezes, nem compensa usar a cache ou até mesmo cache de 1 segundo sobre a página pode revelar resultados incríveis.
Apesar de gostar muito da plataforma .net (muito do desenvolvimento para a web), sinto que também é necessário ter algum tempo livre, é essencial para a mente e para o corpo descontrair. Mesmo tendo sempre o tempo super ocupado com a universidade, projectos, etc, faço questão de nas férias quando posso de fazer um hobbie que me dá muito prazer, uma paixão que descobri há algum tempo e que desde aí a tento desenvolver cada vez mais.
Trabalhar com madeira, dar-lhe forma, vida, transpor algo que sinta num pedaço de madeira. Desenvolver uma capacidade mais cultural e artística através de uma técnica com centenas de anos, acho que é pelo facto de eu sentir que algo de mim ficou numa peça que tenha feito e sentir que as pessoas vêm e reconheçam o que ali está.
A minha última criação que quero partilhar convosco, foi uma espada em madeira (ver figuras), e a ideia nasceu porque andei a pintar a cozinha e a chaminé estava muito vazia... então ocorreu-me fazer algo para lá colocar, o resultado está à vista.

A madeira usada foi um pedaço de pinho, o desenho é simples, as linhas q a descrevem também. É constituída por dois pedaços, a "lâmina" com a pega, e a protecção.
Foi envernizada com verniz de soalho para dar um aspecto mais rústico, e queimado ao sol.

Outra perspectiva.

Foi este o resultado final, a mim agrada-me especialmente.
Qual será a próxima obra...
Continando o estudo sobre o OutputCaching, hoje vamos ver o partial caching, ou cache parcial. Este tipo de cache é muito útil, uma vez que nos permite fazer cache parcial dos conteudos da pagina, partes de página e cache de controlos sobre a nossa aspx.
A ideia subjacente a este conceito, é bastante simples, o que podemos fazer com esta funcionalidade, é fazer apenas cache sobre conteudos ou controlos que necessitamos, sabendo que o resto da página necessita de ser alterado. Mas claro contamos sempre com técnicas para invalidar a cache de forma a actualizarmos os dados na nossa página. A figura abaixo representa a ideia.

Imaginando que temos uma página com uma Grid que lê dados de um XML, podemos manter a Grid a ler da cache e só quando fizermos alterações no XML, invalidamos a cache, e desta forma a Grid passa a ler do ficheiro! Pode parecer pouco, mas as diferenças de tempos é bastante. Agora imaginem num site com muito tráfego o quanto podemos poupar de processamento e tráfego de dados entre XML, SQL, e a cache.
Podemos aplicar esta técnica de várias maneiras diferentes, para isso fiz um exemplo, que contem:
- Cache sobre um controlo - apenas lhe é activada a cache com a instrução: <%@ OutputCache Duration="10" VaryByParam="none" %>
- Cache sobre um ficheiro XML - é criado: um dataset para ler do ficheiro, e uma cache que recebe esse dataset, e que tem como forma de invalidação da cache as alterações ao ficheiro. Existe também um botão que permite remover o conteudo da cache.
O exemplo contem o Trace, de forma a se verificar a origem dos dados e o tempo gasto pelas operações.
No episódio de hoje, vamos ver um exemplo do OutputCaching a funcionar, a ideia (explicada no post anterior) é percebermos a possibilidade de podermos usar esta directiva para fazermos cache total da página.
Esta pode ser muito útil se falarmos de milhares de hits em determinados serviços ou recursos que a nossa página utiliza do servidor, e com isso podemos poupar tanto o tempo de resposta ao cliente (velocidade) como também poupar processamento desnecessário.
Considerando o cenário, de uma página que nos mostra por ex. uma tabela de dados acerca dos empregados que estão na empresa no momento, (normalmente não entram e saem empregado todos os dias da empresa), e por mais poucos registos que sejam se imaginarmos que o mesmo servidor tem a intranet, o website da empresa e outros serviços (como sql server, exchange server, servidor de domínio, etc) podemos aqui nesta página já poupar algum processamento (pode ser pouco, mas grão a grão enche a galinha o papo). Então para isso só temos que usar a directiva Outputcash da seguinte forma:
<%@ OutputCache Duration="172800" VaryByParam="none" %>
em que:
Duration - representa em segundos o tempo pretendido da duração da cache.
VaryByParam - representa o tipo de parâmetro desejado para filtrar a cache. (mais à frente vermos melhor)
A figura mostra a ideia para o cache total:

Com esta directiva e continuando no nosso cenário da pagina que contem uma tabela com os empregados, vamos definir uma cache de 172800 segundos (aprox, 2 dias), o código da pagina:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<%@ OutputCache Duration="172800" VaryByParam="none" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Chaves | OutPutCasching </title>
</head>
<body>
<form id="form1" runat="server">
<div>
<%
Response.Write(System.DateTime.Now.ToString());
%>
<hr />
</div>
<asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ A vossa connection string! %>"
SelectCommand="SELECT * FROM dbo.Tabela_empregados" OnSelecting="SqlDataSource1_Selecting"></asp:SqlDataSource>
<asp:GridView ID="GridView1" runat="server" DataSourceID="SqlDataSource1" EnableViewState="False">
</asp:GridView>
</form>
</body>
</html>
Ajustando para a vossa connectionstring e considerando que têm uma base de dados com a tabela empregados, a página seria carregada apenas passado dois dias, poupando assim o processamento constante dos departamentos que consultam regularmente esta tabela.
Pode ser um bom exemplo, ou mau, dependendo do ponto de vista em que é usada este tipo de cache, mas mesmo que esta seja de muito pouco tempo pode em grandes afluências de tráfego poupar muito.
No próximo episódio veremos mais outro exemplo.
Bom, este vai ser o primeiro de muitos posts sobre este assunto SqlCashing, através de aplicações web (ASP.NET, a minha área lol). Infelizmente começo mal, isto porque desde as primeiras versões (alphas, betas, rc) do asp 2.0 ainda conseguia fazer umas coisas com o cashing, que é um processo com alguma importância quando o acesso a dados é caro (penalizado por n restrições, localização, largura de banda, etc.), desde que saiu a versão final parece que as coisas não andam nada bem...
Existem várias formas de cashing,
- Cashing total, como o próprio nome indica podemos fazer o cache total da página num determinado período de tempo.
- Cashing parcial, permite apenas ter em cache uma parte da nossa página, ou várias partes.
- Cashing por objectos, permite guardar os estado dos controlos (controlos que permitem cache)
Dentro destas formas temos várias maneiras e manipular o cashing:
Um dos mais populares é o OutputCash ou Cashing de respostas, é simples, evita o redesenho do código e de uma forma bastante simples obtemos a funcionalidade, o conteúdo a ser enviado ao cliente (resposta) é armazenado em memória e usado nos próximos pedidos. De fato, todo o conteúdo dinâmico pode ser armazenado em mecanismos que suportem o HTTP 1.1. Podemos, então definir onde vamos querer o caching. Definimos por atributo Location, na directiva de OutputCache, ou ainda, por meio do método HttpCachePolicy.SetCacheability.
A ideia subjacente a este caching é mostrada neste desenho:
Para poderem usar este tipo de cache, é só adicionar a directiva à página pretendida:
<%@ outputcache="" duration="120" varybyparam="parametro" %="">
Permite passar um parâmetro de forma a filtrar elementos da cache, por exemplo se tivéssemos dados combinados de diferentes tabelas, e apenas um grupo de dados se altere, o outro fica em cache.
O facto é que até agora só consegui este tipo de caching, ou seja durante um período de tempo é mantida a página sem fazer consultas ao servidor. O mesmo acontece se a página aceder a uma base de dados, não existe tráfego entre o SqlServer e a aplicação ASP.
Mas este tipo de cache, não interessa para quem tem páginas dinâmicas que recorrem a dados, porque imaginando que tem um timeout de x tempo, se alguém introduzir novos dados na base de dados este só serão apresentados quando a cache for invalidada, ou seja só depois do x tempo... o ideal seria após qualquer alteração de dados a cache ficar automaticamente invalidada... pois é mas é aí que as dores começam... vejamos no próximo post.