Programação em Asp.Net - parte III
Autor: Luís Abreu
Conteúdo: Introdução à programação em Asp.Net 2.0
Ferramentas: Visual Web Dev Express
No último artigo aprofundámos os principais aspectos relativos à utilização daquilo a que (eu) gosto de chamar postbacks "parciais". Por outras palavras, vimos como é que podemos efectuar um postback sem que isso envolva refrescar completamente a página. Hoje vamos continuar a falar de novidades e vamos abordar a nova estratégia de acesso a dados: data source controls.
Uma das bandeiras da nova plataforma é a redução do número de linhas de código. Se me perguntarem se isso é uma boa coisa, eu digo que sim, desde que não me retirem a hipótese de escrever todo o código se eu assim o quiser(deste principio não abro mão!!!). Na versão 1.x da framework era necessário escrever (poucas) linhas de código para conseguirmos (efectivamente) efectuarmos o binding entre uma fonte de dados e um controlo. A nova versão da framework tende a tornar este processo ainda mais rápido uma vez que já permite efectuar o binding sem que seja necessário escrever uma única linha de código.
Acesso a dados através de datasources
A melhor forma de explicar estes controlos é demonstrá-los através de um exemplo simples. Aqueles que já tiveram a oportunidade de experimentar o asp.net 2 já devem ter reparado que existem muitos novos controlos. Entre as novidades podemos encontrar, por exemplo, o SqlDataSourceControl (este controlo é um dos vários controlos do tipo data source). Bem, sem perdermos mais tempo, vamos lá ver como é que podemos utilizar este controlo. Assim, podemos:
- começar por criar um novo projecto (utilizando o Visual Web Developer);
- adicionar um novo formulário web (caso não exista nenhum no projecto);
- na vista de design, adicionar uma GridView (podem formatá-la de acordo com as vossas preferências);
- Adicionar o SqlDataSourceControl e configurá-lo de forma a obtermos dados provenientes da base de dados Northwind (para tal basta seguir os passos do wizard - no meu caso decidi obter todos os registos da tabela Categorias);
- Configurar a grid de forma a que a fonte de dados seja o controlo adicionado em 4 (a forma mais rápida de efectuar este passo consiste em utilizar a edição no próprio designer - definida através da seta apresentada no canto superior direito do controlo quando este está seleccionado);
- Efectuar o build e fazer um preview do formulário no browser.
Então, repararam na novidade? Não? Então, como castigo, têm de repetir os passos anteriores até se aperceberem da novidade existente neste processo. Se estão a pensar que a novidade está apenas relacionada com os novos controlos, então também têm
de repetir o exemplo anterior! Por outro lado, se pensarem que eu deveria ter escrito algumas linhas de código no ficheiro de codebehind (nomeadamente as linhas que efectuam o binding dos dados à grid), então já chegaram onde eu queria. Sim, como repararam não tivemos de escrever uma única linha de código. Aha!
Bem, antes de se converterem ao que eu chamo "programação de rato", convém percebermos como é que este tipo de controlos funciona. A vista de código permite-nos ver o que se está a passar:
<asp:SqlDataSource ID="SqlDataSource1" Runat="server" SelectCommand="SELECT [CategoryName], [Description] FROM [Categories]"
ConnectionString="<%$ ConnectionStrings:Northwind %>">
</asp:SqlDataSource>
Portanto, na prática temos uma nova instrução "html" que define o nosso controlo data source. Neste exemplo, que é extremamente simples, o controlo apenas define a expressão que lhe permite obter os dados a partir da base de dados. Essa expressão é definida na propriedade SelectCommand. Para além dessa propriedade, temos também uma outra propriedade importante: ConnectionString. Esta propriedade armazena a string que define a ligação à base de dados (no caso do código anterior, esta string está definida no ficheiro de configuração numa nova secção adicionada nesta nova versão da framework cujo principal objectivo é permitir o armazenamento de connection strings de uma forma mais segura do que acontecia na versão anterior).
A propriedade Id deste tipo de controlos é a chave do relacionamento entre os controlos de apresentação e os controlos fonte de dados. No exemplo anterior, a grid sabe que deve obter os seus dados devido ao facto de possuir uma propriedade designada de DataSourceId que foi definida com o mesmo valor do Id associado ao controlo SqlDataSource:
<asp:GridView ID="GridView1" Runat="server" BorderWidth="1px" BackColor="White" GridLines="Vertical" CellPadding="3"
BorderStyle="None" BorderColor="#999999" DataSourceID="SqlDataSource1" AutoGenerateColumns="False">
...
Portanto, o binding dos dados nunca foi tão fácil como é hoje! Agora que já sabemos o básico, está na hora de avançarmos e falarmos de outros aspectos que também são importantes.
Utilização de parâmetros
Muitas vezes queremos mostrar dados associados a uma determinada entidade. Portanto, na prática, temos de efectuar a filtragem dos registos antes de os apresentarmos ao utilizador. Geralmente optamos por filtrar o sql de forma a que este retorne apenas os dados necessários à situação. Para tal, é costume utilizarmos instruções de sql que recorrem a parâmetros. O SqlDataSource apresenta uma propriedade chamada de SelectParameters (do tipo ParameterCollection) que serve para indicarmos os vários parâmetros necessários à instrução de select. Existem sete tipos diferentes de parâmetros. Um dos tipos, designado de Parameter, serve de classe base para os restantes seis.Esta classe base serve para definir um conjunto de propriedades base:
- Name: utilizado para definir o nome do parâmetro (geralmente coloca-se aqui o nome do parâmetro como foi definido na instrução de sql);
- Direction: indica a direcção do parâmetro tendo em atenção a enumeração ParameterDirection;
- DefaultValue: permite-nos definir um valor por defeito do parâmetro;
- Type: serve para indicarmos o tipo de dados do parâmetro;
- ConvertEmptyStringToNull: valor que indica se uma string vazia deve ser convertida para null.
Assim, podemos utilizar a seguinte instrução para filtrarmos os registos obtidos tendo em atenção o campo CategoryId:
<asp:SqlDataSource ID="SqlDataSource1" Runat="server"
SelectCommand="SELECT [CategoryName], [Description] FROM [Categories] WHERE ([CategoryID] > @CategoryID)"
ConnectionString="<%$ ConnectionStrings:Northwind %>">
<SelectParameters>
<asp:Parameter Type="Int32" DefaultValue="3" Name="CategoryID"></asp:Parameter>
</SelectParameters>
</asp:SqlDataSource>
Se o valor do parâmetro não for modificado, então apenas serão mostrados todos os registos cujo Id seja superior a 3. Seguindo este racíocinio, é muito fácil escrevermos o código necessário a que o valor do parâmetro seja proveniente, por exemplo, da query string. O excerto seguinte (retirado do código que acompanha o artigo) demonstra os passos necessários:
void Page_Load()
{
if (this.Request.Params["ID"] != null)
{
SqlDataSource1.SelectParameters[0].DefaultValue = this.Request.Params["ID"];
}
}
Pois, pois...os mais desconfiados já estão a dizer: "mas então e a história do databinding sem escrita de linhas de código?"...bem, de facto eu escrevi umas linhas de código (yep, sou culpado disso!!!)...mas isto deveu-se apenas ao facto de eu ainda não ter apresentado os outros 6 tipos de parâmetros existentes(lembram-se de eu ter afirmado acima que haviam sete tipos de parâmetros???).
Tipos de parâmetros
Actualmente existem seis tipos de parâmetros (se não contarmos coma classe base Parameter) que são utilizados na grande maioria dos casos. Essas classes são:
- ControlParameter: parâmetro que obtém o seu valor a partir de um controlo existente no formulário web. O controlo que fornece o valor é especificado através do atributo ControlId e o atributo PropertyName indica a propriedade do controlo especificado que irá fornecer o valor do parâmetro;
- QueryStringParameter: permite obter um valor passado através da query string. o atributo QueryStringField serve para indicar o nome do par nome/valor passado na query string que irá alimentar o valor do parâmetro;
- FormParameter: permite obter o valor a partir de um controlo (que não necessita de ser servidor!) contido num formulário. O atributo importante é o atributo FormField que, como seria de esperar, serve para indicar o nome do campo a partir do qual deverá ser obtido o valor do parâmetro.
- SessionParameter: neste controlo é utilizado o atributo SessionField para indicarmos o valor da variável de sessão a partir da qual será obtido o valor do parâmetro;
- CookieParameter: como seria de esperar, também está assegurado o suporte a valores provenientes de dados armazenados nos cookies. A propriedade a reter chama-se CookieName e permite obter o nome da chave que contém o valor que deve ser associado ao parâmetro;
- ProfileParameter: a nossa lista termina com um controlo que permite obter o valor do parâmetro a partir de informação armazenada no perfil do utilizador (os perfis de utilizadores serão abordados num artigo futuro desta série).
Assim, o código anterior poderia ser reescrito sem termos de utilizar qualquer linha de código adicional, Para tal bastava termos definido o nosso parâmetro através de um QueryStringParameter (em vez do tradicional Parameter). O excerto seguinte demonstra este principio:
<SelectParameters>
<asp:QueryStringParameter Name="CategoryID" DefaultValue="3" QueryStringField="ID"
Type="Int32"></asp:QueryStringParameter>
</SelectParameters>
Aha! Afinal sempre é possível construir uma página deste tipo sem qualquer tipo de código!!! O sample que acompanha este artigo demonstra também a utilização dos parâmetros do tipo ControlParameter (visto que este será, provavelmente, o tipo de parâmetro mais utilizado).
Operações de alterações de dados
Para além de permitirem obter os dados provenientes de uma fonte de dados, estes controlos são também capazes de efectuarem as operações de inserção, modificação e eliminação de dados. Para tal, é necessário definir as propriedades InsertCommand, UpdateCommand e DeleteCommand deste tipo de controlos. No caso das operações de modificação de dados podemos definir o modo da operação. Existem dois valores possíveis (provenientes da enumeração DataSourceOperationMode):Optimistic e Pessimistic. Se optarmos pela segunda opção, a actualização irá ser feita com base num critério mais restrito de forma a evitar eventuais erros relacionados com modificações concurrenciais (por outras palavras, o sql gerado será o necessário a garantir que uma linha será actualizada apenas quando os dados existentes nessa linha forem os dados originais obtidos aquando do select inicial).
Também não será surpresa se afirmarmos que cada um dos comandos possui uma colecção de parâmetros associados. Estas colecções são expostas através das propriedades InsertParameters, DeleteParameters e UpdateParameters. Os tipos de parâmetros associados a cada uma destas colecções são os mesmos apresentados anteriormente. O sample que acompanha este artigo apresenta uma página que demonstra este tipo de operações.
Métodos
A classe SqlDataSource apresenta vários métodos importantes que lhe permitem efectuar as operações de acesso/alteração dos dados. Nos exemplos apresentados até aqui, nunca tivemos de escrever uma única linha de código para efectuar essas operações devido ao facto dos controlos utilizados serem capazes de iniciar essas operações de forma automática. Contudo, podem haver situações em que seja necessário evocar os métodos dos controlos directamente, pelo que aqui fica a descrição dos métodos:
- Select: método que permite obter os registos indicados através da instrução sql associada à propriedade SelectCommand da classe. Este método pode retornar objecto DataView ou IDataReader. O tipo retornado depende da propriedade DataSourceMode. Esta propriedade suporta (como seria de esperar após a descrição anterior) dois valores (que se auto-descrevem): DataSet e DataReader. Por defeito, esta propriedade contém o valor DataSet;
- Delete: como o próprio nome indica, este método é o responsável por efectuar a instrução de sql associada à propriedade DeleteCommand;
- Insert: permite a introdução de um novo registo de acordo com a instrução sql definida na propriedade InsertCommand;
- Update: serve para actualizar um determinado registo de acordo com a instrução contida na propriedade UpdateCommand.
A página Test2.aspx demonstra como podemos utilizar um controlo deste tipo para adicionarmos um novo registo à base de dados.
Propriedade DataSourceMode
Antes de avançarmos para os eventos expostos por este tipo de controlo, convém apresentarmos melhor esta propriedade do controlo SqlDataSource. Até aqui não nos tinhamos referido a esta propriedade. Por outras palavras, estavamos a utilizá-la com o seu valor por defeito (que, como vimos na secção anterior, é DataSet). Contudo, se for necessário, podemos utilizar um DataReader para obter os dados necessários. A página Test6.aspx demonstra como é fácil proceder a esse tipo de operações. Contudo, é importante referir que, ao utilizarmos um DataReader, não iremos poder efectuar operações de sorting na nossa grid.
SqlDataSourceView
Apesar de nós (programadores) trabalharmos com a classe SqlDataSource, existe uma outra classe extremamente importante que é utilizada no relacionamento existente entre o controlo (de apresentação) e o nosso SqlDataSource. Essa classe chama-se SqlDataSourceView. Na prática, sempre que recorremos a um método da classe SqlDataSource, estamos a aceder ao método correspondente da classe SqlDataSourceView relativa ao objecto do tipo SqlDataSourceView utilizado pelo SqlDataSource.
Eventos
Tal como acontece com muitas das classes existentes, a classe SqlDataSource apresenta alguns eventos importantes, que são apresentados em seguida:
- Selecting: evento que é disparado antes do método Select ser (realmente) evocado. Este método recebe uma instância do tipo SqlDataSourceSelectingEventArgs que permite saber se a instrução de sql devolve uma contagem ou um rowset (ExecutingSelectCount) e/ou cancelar a operação (propriedade Cancel);
- Selected: como o próprio nome indica, este evento é despoletado após a conclusão do método Select.
- Deleting: semelhante ao evento Selecting, mas referente à eliminação de elementos. Neste caso, o parâmetro passado ao método associado ao evento é do tipo SqlDataSourceCommandEventArgs. Como seria de esperar, podemos cancelar a operação através da propriedade Cancel;
- Deleted: despoletado após terminar a operação e eliminação;
- Inserting: evento despoletado antes da operação de inserção ser efectuada;
- Inserted: evento que ocorre após a operação de inserção ter sido completada;
- Updating: evento que é despoletado imediatamente antes de uma operação de actualização;
- Updated: evento que ocorre após a evocação do método Update.
Caching
Todos nós sabemos da importância de guardarmos os dados na cache de forma a aumentarmos a performance das nossas aplicações web. Felizmente para nós, a Microsoft concorda com este ponto de vista. Por isso, o controlo SqlDataSource permite a utilização da cache. Existem cinco atributos importantes que servem para configurar o tipo de acesso à cache:
- EnableCaching: indica se a data obtida será ou não mantida em cache; só podemos utilizar a cache se os dados forem fornecidos sobre a forma de DataSet (ou seja, utilizando DataSet como o valor da propriedade DataSourceMode);
- CacheDuration: indica o número de segundos em que os dados devem permanecer na cache (o valor deve ser especificado por um inteiro) ;
- CacheKeyDependency: serve para especificar uma dependência de um valor colocado em cache. Esta propriedade deverá conter o nome da chave que contém o valor a partir do qual será criada uma dependência;
- SqlCacheDependency: permite criar uma dependência com uma tabela contida numa base de dados;
- CacheExpirationPolicy: permite definir a política de cache utilizada. Se o valor desta propriedade for Absolute, então após obter os dados da fonte de dados, estes são mantidos na cache até serem inválidos (por exemplo, devido ao facto do tempo ter expirado); por outro lado, se o valor for Sliding, então o intervalo utilizado como delimitador do tempo de vida dos dados em cache é reiniciado. Neste caso, os dados em cache são invalidades apenas se não houver obtenção de dados a partir do controlo durante um tempo igual ou superior ao definido na propriedade CacheDuration.
A utilização da cache resulta da combinação das propriedades referidas anteriormente. Aliás, a utilização da cache foi uma das áreas que também sofreu alterações nesta nova versão, pelo que iremos reservar um artigo futuro para falarmos de forma mais aprofundada sobre este assunto.
SqlDataSource: acesso a qualquer base de dados
Ao contrário do que se poderia pensar, a classe SqlDataSource permite o acesso a vários tipos de base de dados. Para tal, apenas temos de indicar o provider que queremos utilizar. Para tal, basta modificarmos a propriedade ProviderName de forma a que esta passe a utilizar o provider ADO.Net que quisermos. Por defeito, o provider escolhido permite-nos trabalhar com Sql Server; contudo, também existem outros providers que acompanham a framework e que nos permitem ligar a outras base de dados. Estamos a falar dos providers de OleDb, ODBC e Oracle. A documentção contém mais informações relativas a este tópico (num futuro próximo, conto falar um pouco mais sobre os providers).
Os "outros" controlos do tipo data source
Como referi no inicio deste artigo, existem vários tipos de controlos data source. Para além do SqlDataSource, ainda existem outros tipos de controlos DataSource. Nas próximas secções vamos analizar os restantes controlos existentes.
AccessDataSource: acesso fácil a base de dados Access
Se necessitarmos de aceder a uma base de dados Access. então devemos utilizar o controlo AccessDataSource. Este controlo, que herda da classe SqlDataSource, facilita a ligação a uma base de dados Access através de uma nova propriedade designada de DataFile. Esta classe é geralmente utilizada da mesma forma que a classe SqlDataSource. A página Test9.aspx mostra como podemos obter dados a partir de um controlo deste tipo.
Controlo XmlDataSource
Até agora temos apenas falado de controlos data source que fornecem listas de valores (ou, se preferirem o termo, flat data). Por outras palavras, até agora obtivémos apenas um conjunto de registos que foram mostrados sequencialmente numa grid. Este tipo de sitações adequa-se à maioria dos casos que ocorrem nos nossos dias. Contudo, existem outras fontes de dados que cada vez são mais utilizadas. Assim, já se começa a utilizar ficheiros de xml como forma de armazenar alguns dados. Para além disso, com a proliferação dos Web Services, muitas vezes temos acesso a streams de XML que devem de ser mostradas.
Os dados provenientes deste tipo de ficheiros costumam estar organizados hierarquicamente (ao contrário do que acontece normalmente com os dados obtidos a partir das fontes de dados tradicionais - i.e., das base de dados). Logo, um controlo utilizado para obter dados a partir de uma fonte de dados deste tipo tem de se adaptar a esta situação. Foi devido a estas duas situações que a nova framework apresenta duas novas classes base que fornecem as principais características que são expostas por estes dois tipos principais de controlos. Assim, temos:
- DataSourceControl: esta é a classe base de todos os controlos que fornecem a chamada flat data. Serve de base às classes SqlDataSource, AccessDataSource e ObjectDataSource (mais informação sobre esta classe num artigo futuro);
- HierarchicalDataSourceControl: serve de base aos controlos que conseguem carregar dados de forma hierárquica. Aqui incluem-se os controlos XmlDataSource (que irá ser discutido em seguida) e SiteMapDataSource (que já foi abordado num artigo anterior).
Este controlo possui várias propriedades diferentes dos controlos anteriores. Assim, podemos relacionar o controlo com o XML que vai carregar através da propriedade DataFile. Como o próprio nome indica, esta propriedade permite indicar o nome do ficheiro que contém o XML que irá ser utilizado pelo controlo. Alternativamente, podemos declarar o XML inline através da propriedade Data.
Se for necessário, podemos definir um schema e associá-lo ao controlo. Temos duas opções para efectuar esta operação: utilizando a propriedade SchemaFile ou a propriedade Schema. A forma de utilização destas propriedades é semelhante às propriedades DataFile e Data. Assim, a propriedade SchemaFile permite definir o caminho até um ficheiro que contém o schema; por outro lado, também podemos definir o schema inline, utilizando para tal a propriedade Schema.
Este tipo de controlos geralmente é utilizado em conjunto com controlos que permitem apresentar os dados de forma hierárquica (como, por exemplo, o controlo TreeView). Contudo, também podemos apresentá-lo noutro tipo de controlos (como, por exemplo, o GridView). A página Test10.aspx demonstra estas duas situações. O binding entre a fonte de dados e os controlos tradicionais "flat data" só é possível devido à utilização das novas expressões de binding para acesso a XML (as novas expressões de binding serão alvo de um artigo futuro).
Muitas vezes é necessário transformar o XML recebido de forma a que o XML "final" esteja de acordo com um formato previamente definido. Felizmente para nós, o XmlDataSource suporta a transformação automática desde que seja fornecido o ficheiro XSL(T) ou o próprio XSL(T). No caso do ficheiro, a propriedade TransformFile deve ser utilizada. Se pretendermos definir as transformações inline, então devemos utilizar o elemento Transform. Esta aproximação é demonstrada na página Test11.aspx (com, o xsl pode não ser perfeito, mas a verdade é que não o utilizo a sério desde 2001 :) ).
Normalmente este tipo de controlo é utilizado apenas para mostrar dados, não sendo por isso muito utilizado para efectuar operações de leitura/escrita. Contudo, se estivermos dispostos a aceitar algumas limitações, também podemos utilizar este controlo para efectuar operações de escrita. Assim, para editar o XML proveniente deste tipo de fonte de dados, devemos:
- efectuar o carregamento dos dados a partir de um ficheiro de XML (por outras palavras, não podemos definir o XML inline através do elemento Data) ;
- Não podemos efectuar uma transformação XSL(T) (como fizemos no exemplo anterior).
Refira-se ainda que estas operações apenas são suportadas através da manipulação directa do objecto XmlDataDocument que pode ser obtido a partir do controlo XmlDataSource. Em seguida, é só efectivar as alterações através do método Save.
Conclusões finais
Por hoje chega! Com este artigo iniciámos o nosso estudo sobre os novos controlos de acesso a dados. Hoje falámos sobre três controlos: SqlDataSource, AccessDataSource e XmlDataSource. No próximo artigo iremos abordar os restantes controlos: DataSetSourceControl, ObjectDataSource e SiteMapDataSource. Até à próxima semana.
Por favor enviem-me as vossas opiniões/sugestões/críticas/correcções para progC@netmadeira.com.
Fiquem bem e boa programação! Até à próxima. O código que acompanha este artigo está disponível na secção dos downloads do site.
Leiam o meu blog em: http://members.netmadeira.com/luisabreu