março 2009 março 2009 Índice Editorial Delphi Zlib - Construíndo um compactador de arquivos no Delphi Aprimorando o desempenho de um Banco de Dados Delphi Prism com LINQ 04 .NET Dicas Delphi Trabalhando com dynamic Data Controls O ASP.NET ultimamente vem revolucionou o desenvolvimento de aplicações para web oferecendo uma plataforma robusta e altamente produtiva elevando o potencial dos desenvolvedores com excelente recursos como: GridView, DataControls, Validators , WebParts entre outros. 24 Ativar Auto Run do CD-ROM Criptografar Strings Ver se existe Midia no Drive Ver impressora conectada Localizar Arquivos no Windows Mostrar as fontes True Types instaladas no Windows Intermediário 28 Desafio The CLub Legenda Iniciante 05 10 13 15 DataSnap 2009 - ParteI Introdução Posso dizer que a partir do mês de Agosto do ano passado onde assumi a direção da revista The Club Megazine, todas nossas edições foram feitas com o objetivo de levar um conteúdo cheio de novidades ... Teste seus conhecimentos. Avançado março 2009 30 03 Bem-vindo Posso dizer que a partir do mês de Agosto do ano passado onde assumi a direção da revista The Club Megazine, todas nossas edições foram feitas com o objetivo de levar um conteúdo cheio de novidades e recursos aplicáveis no dia-a-dia do programador. Este mês nos do The Club ficamos muito satisfeitos com o resultado desta edição, pois acreditamos que nas paginas desta edição para qualquer que seja o leitor, com certeza novidades serão encontradas. Assim continuando a seqüência de artigos sobre a nova ferramenta da Embarcadero, o Delphi Prism, Luiz Alexandre com seu artigo “Delphi Prism com Linq (Language Integrate Query)”, nos mostras mais algumas funcionalidades da ferramenta num artigo bastante completo que apresenta conceitos do Linq, que podem ser aplicados inclusive em um aplicação C#. Sabemos da importância de velocidade e performance em nossas aplicações, desta forma o consultor Marco Antonio Armando em seu artigo “Aprimorando desempenho de banco de dados”, demonstra em dicas simples, mas de grande importância na aplicação, como escrever consultas SQL visando obter a melhor eficiência do banco de dados. Entre as muitas novidades apresentadas pela Embarcadero no lançamento do Delphi 2009, encontramos os novos avanços na tecnologia DataSnap que chamaram bastante a atenção, neste mês trago o artigo “DataSnap 2009 - Introduçao“ onde mostro como ficou simples e funcional criar um servidor de métodos DataSnap, mostrando o poder de uma tecnologia que embora lançadas a muito tempo ainda é uma grande caixa preta para muitos programadores, e que agora após um período de estagnação da tecnologia, foi remodelada pela Embarcadero sendo lançada nesta nova versão com novos recursos e independente da tecnologia COM. Uma necessidade comum nas aplicações é a compactação de arquivo seja para backup de dados ou envio de arquivo pela internet, assim o consulto Antonio Spitaleri escreveu o artigo “Zlib Construindo um compactador de Arquivos no Delphi” onde usando a Unit ZLib.pas nativa do Delphi demonstra como realizar este procedimento sem o uso de nenhum componente de terceiros. Ao longo dos anos surgiu no mercado necessidades como a criação de telas automáticas de cadastro, com o lançamento do Service Pack 1 do .NET 3.5, foi disponibilizando dentro do ASP.NET um novo conjunto de controles conhecido como Dynamic Data Controls que tem objetivo de montar as telas para as ações básicas. Assim este mês Fabiano Belmont aborda este assunto com seu novo artigo “Trabalhando com Dynamic Data Controls”. Av. Profº Celso Ferreira da Silva, 190 Jd. Europa - Avaré - SP - CEP 18.707-150 Informações: (14) 3732-1529 Suporte: (14) 3733-1588 Internet http://www.theclub.com.br Cadastro: [email protected] Suporte: [email protected] Informações: [email protected] Skype Cadastro: theclub_cadastro Skype Suporte: theclub_linha1 theclub_linha2 Copyright The Club Megazine 2008 Diretor Técnico Marcos César Silva Diagramação e Arte Vitor M. Rodrigues Revisão Marcos César Silva Colunistas Antonio Spitaleri Neto Fabiano Belmonte Luís Alexandre de Oliveira Marco Antonio Armando Marcos César Silva Impressão e acabamento: GRIL - Gráfica e Editora Rua São Paulo, nº 447 Cep: 18740-00 - Taquarituba-SP Tel. (14) 3762-1345 Reprodução A utilização, reprodução, apropriação, armazenamento em banco de dados, sob qualquer forma ou meio, de textos, fotos e outras criações intelectuais em cada publicação da revista “The Club Megazine” são terminantemente proibidos sem autorização escrita dos titulares dos direitos autorais. Que todos tenham uma boa leitura e apreciem os artigos sem moderação. Marcos César Silva - Editor Chefe [email protected] 04 março 2009 Delphi é marca registrada da Borland International, as demais marcas citadas são registradas pelos seus respectivos proprietários. Delphi Parte I Entre as muitas novidades apresentadas pela Embarcadero no lançamento do Delphi 2009, encontramos os novos avanços na tecnologia DataSnap que chamaram bastante a atenção. O DataSnap embora possa parecer uma tecnologia nova e cheia de mistérios nasceu juntamente com o lançamento do Delphi 3, inicialmente conhecida como tecnologia MIDAS ( “Multi-Tier Distributed Application Services”), teve seu nome alterado para Datasnap quando no lançamento do Delphi 6 passou a contar com suporte a SOAP/XML HTTP. Um dos principais avanços concebido nesta nova versão foi à desvinculação do Datasnap da tecnologia COM, independência esta esperada a logo prazo por muitos desenvolvedores que ira simplificar em muito o processo de desenvolvimento. Embora que vale ressaltar que foi mantida toda compatibilidade com versões anteriores. N este artigo ire demonstrar com criar um servidor de métodos e uma aplicação cliente que irá consumir os métodos disponíveis pelo servidor. Introdução Imagem 1 – Criando VCL Forms Applications Criação do Servidor Datasnap No RAD Studio Delphi 2009 para criar uma aplicação servidora Datasnap o primeiro passo é criar uma aplicação VCL Forms Applications comumente utilizada, para isto vá em New Projects selecione a opção VCL Forms Applications (Imagem 1). Será criado a unit principal que salvaremos com o nome de unServer.pas e o projeto que salvaremos como Server.dpr, iremos também alterar a propriedade Name do form criado frmServer. Veja a imagem 1. março 2009 05 Neste momento irei adicionar os componentes servidor Datasnap, para isto em Tool Palette selecione a opção ”DataSnap Server” e inseria no formulário os componente TDSServer, TDSTCPServerTransport e um TDSServerClass1 como pode ver na Imagem 2. Imagens 2 – Componentes Inseridos no formulário Uma vez inserido os componentes é necessário configurar a propriedade Server dos componentes DSTCPServerTransport 1 e DSServerClass11 para DSServer1. Uma dica importante neste momento é observar a propriedade Port que se encontrada no componente TDSTCPServerTransport, o numero da porta deverá ser único por aplicação servidora para evitar que mais de uma aplicação responda na mesma Porta. 06 março 2009 Imagem 3 – Criando o Server Module Criaremos agora o Server Module, que será nosso DataModule da aplicação DataSnap, ou melhor um TProviderDataModule que ira servir como um provedor de métodos e dados da aplicação Servidora. Para criar o Server Module vá em New Itens e selecione o item Server Module (Imagem 3), e salve-o como unServerModule; Veja a imagem 3. Apenas para finalidade de demonstração irei criar um método que irá retorna a versão do projeto, para isto no código fonte do TDSServerModule1, na seção public da classe declare a function PegaVersao e em implementation insira o código fonte da função como pode ver abaixo: type TDSServerModule1 = class(TDSServerModule) private { Private declarations } public function PegaVersao:string; end; var Agora voltaremos ao form frmServer e adicione a uses unServerModule com Alt+F11 (Imagem 4) e no componente DSServerClass1, adicione o evento OnGetClass e insira o código abaixo: procedure TfrmServer. DSServerClass1GetCl ass(DSServerClass: TDSServerClass; var PersistentClass: TPersistentClass); begin PersistentClass := TDSServerModule1 end; DSServerModule1: TDSServerModule1; implementation {$R *.dfm} { TDSServerModule1 } function TDSServerModule1. PegaVersao: string; begin Result := ‘Versão 1.0’; end; março 2009 07 e então será executado o método desejado da classe, que neste caso é o PegaVersao; implementation uses unClientClass; {$R *.dfm} procedure TfrmClient. Button1Click(Sender: TObject); var ServerModule: TDSServerModule1Client; Imagem 4 - Adicionando a uses unServerModule Imagem 5 – Propriedades do SQLConnection1 Compile o projeto com a opção Run Without Debugging Shift+Ctrl+ F9 assim será criado o executável Server.exe sem a opção de debug, não “prendendo” a sua execução com a IDE do Delphi, neste momento apenas minimize o executável do projeto servidor, pois o mesmo deverá estar em execução para responder ao projeto cliente que iremos criar. Agora Clique com o botão direito no mouse sobre o componente SQLConnection1 e selecione a opção Generate DataSnap client classes (Imagem 6), para que seja criado pelo DataSnap proxy generator a unit de classes cliente correspondente ao métodos disponíveis na aplicação servidora, salve-a como unClientClass.pas. Outra dica importante, certifique-se que a aplicação servidora esta “rodando”, caso contrário a classe não será gerada com sucesso. begin ServerModule := TDSServerModule1Client. Create(SQLConnection1. DBXConnection); try Label1.Caption := ServerModule.PegaVersao; finally ServerModule.Free; end; end; Criação do Cliente DataSnap O próximo passo antes de criar a aplicação cliente e salver o grupo de projetos em Project Manager, para isto clique com o botão direito do Mouse sobre o Arquivo de Grupo de projetos e selecione “Save Projet Group As” salve como DataSnapProjectGroup.groupproj, salvo o arquivo volte a clicar sobre o Grupo de Projetos e selecione Add New Project selecione a opção VCL Forms Applications para criarmos a aplicação cliente, salve a unit como unClient.pas renomeando a propriedade Name do formulário para frmClient, e salvando o projeto como Client.dproj. O próximo passo é adicionar os componentes de conexão, para isto em Tool Palette selecione o componente TSQLConnection e adicione no formulário frmClient, assim no SQLConnection1 em sua propriedade Driver selecione o item Datasnap, observe que esta propriedade no Object Inspector tem Subpropriedades , coloque na subpropriedade HostName o valor localhost e em port 211, e não esqueça de alterar a propriedade LoginPrompt para false como pode var na imagem 5 08 março 2009 Variação da Aplicação Cliente DataSnap Imagem 6 – Criando as classes cliente Para que possamos testar, adicione no form frmClient a uses unClientClass (Alt+F11) e coloque um componente Label para receber o retorno do método e um Button para disparar a execução, no evento OnClick do botão adicione o código que pode ver abaixo, para que desta forma quando for executado o clique no botão será criado a classe TDSServerModule1Client gerada na etapa anterior Para deixarmos mais evidente que a conexão está sendo feita através do driver DBXClient existindo uma integração direita com DataSnap e dbExpress, iremos utilizar a mesma função da aplicação Server, só que desta vez sem a declaração da classe TDSServerModule1Client como foi feito no processo anterior. Para isto adicione o componente TSqlServerMethod que se encontra em Tool Palette -> dbExpress no formulário frmClient, altere a sua propriedade SQLConnection para SQLConnection1 e em ServerMethodName observe que será listada todos os métodos da aplicação Server, selecione assim o item que nos interessa que é o TDSServerModule1.PegaVersao (Imagem 6). Adicione mais um componente TLabel e outro TButton, no evento Onclick deste novo botão coloque o código abaixo responsável por executar o método: procedure TfrmClient. Button2Click(Sender: TObject); acesso a banco de dados. begin SqlServerMethod1. ExecuteMethod; Label2.Caption := SqlServerMethod1. ParamByName (‘ReturnParameter’). AsString; end; Quando selecionamos o ServerMethodName no componente SqlServerMethod1, automaticamente a propriedade params deste componente foi preenchida, neste nosso caso existia apenas o parâmetro de retorno (Result) chamado por padrão como ReturnParameter, Caso nosso método tivesse parâmetros de entrada (Imput), estes parâmetros seriam também adicionados em Params, e deveriam ter uma valor para eles atribuídos antes da chamada do método ExecuteMethod do componente SqlServerMethod1 da seguinte forma: SqlServerMethod1. ParamByName(‘Parametro1’). AsInteger := 10; Conclusão Imagem 7 – Selecionado o método Vimos como ficou muito mais simples e prático a criação de aplicação Multicamadas, não dependendo de nenhuma outra tecnologia para seu funcionamento. Em nosso próximo encontro iremos ver como usar a tecnologia Datasnap para Download: www.theclub.com.br/revista/rev0309/datasnap2009.rar Referencia: http://blogs.embarcadero.com/ andreanolanusse/2008/10/16/exemplos-datasnap-dbexpress-e-outros-recursos-do-delphi2009/ Sobre o autor Marcos César Silva, Consultor de Sistemas na consultoria de sistemas DataSmart e Consultor Técnico do The Club, Bacharel em Ciência da Computação, MBA em Gestão Empresarial, Certificações MCAD (Microsoft Certified Application Developer) e MCSD.NET (Microsoft Certified Solution Developer .NET) março 2009 09 Entre as inúmeras units presentes no Delphi, sem dúvida uma que poucos conhecem é a zlib. Essa unit contém classes que tornam possível a criação de objetos que realizam a compactação de arquivos. Nesse artigo, estarei mostrando passo a passo com construir um compactador de arquivos bem simples utilizando Delphi e a zlib. Mãos a obra! Formulário:frmcompactador. Edits: edtarquivo,edtdestino. Buttons: btncompacta,btndescompacta,btnse lecionaarq,btndestino; Finalizando, insira ao lado das edits edtarquivo e edtdestino duas labels com as propriedades caption como arquivo e destino respectivamente. O formulário deverá ficar com a seguinte aparência: Veja a Figura 01. Preparando a Aplicação Inicie um novo projeto no Delphi, e inclua na unit do form a unit zlib. Insiram no formulário os seguintes componentes: Realizando a codificação Chegou a hora de inserirmos o código necessário para que o nosso compactador funcione.No 2 edits(palheta standard); 4 buttons(palheta standard); 2 labels(palheta standard); 1 opendialog(palheta dialogs); Altere as propriedades name dos componentes conforme segue: Figura 01. 10 março 2009 evento onclick do btnselecionaarq digite: procedure Tfrmcompactador. btnselecionaarqClick(Sender: TObject); begin OpenDialog1.Execute; edtarquivo. Text:=OpenDialog1.FileName; end; Nesse evento estamos abrindo a caixa de diálogo abrir do windows, selecionando um arquivo e colocando seu caminho na edtarquivo. No evento onclick do btndestino digite: procedure Tfrmcompactador. btndestinoClick(Sender: TObject); begin OpenDialog1.Execute; edtdestino.Text:=Extra ctFilePath(OpenDialog1. FileName); end; Aqui estamos selecionando um arquivo e extraindo através da função extractfilepath seu caminho já que é apenas o caminho que será utilizado.Esse caminho irá ser exibido na edtdestino. Agora a parte mais importante, que é a codificação que irá realizar a compactação do arquivo selecionado.É nessa parte que estaremos utilizando as classes da zlib que realizam a compactação. No evento onclick do btncompacta digite: procedure Tfrmcompactador. btncompactaClick(Sender: TObject); var FileIni, FileOut: TFileStream; Zip: TCompressionStream; nomearq:string; begin try try nomearq:=InputBox(‘C ompactar’,’Digite um nome para o arquivo’,’’); FileIni:=TFileStream. Create(edtarquivo. Text, fmOpenRead and fmShareExclusive); FileOut:=TFileStream. Create(nomearq+’. zip’, fmCreate or fmShareExclusive); Zip:=TCompressionStream. Create(clMax, FileOut); Zip.CopyFrom(FileIni, FileIni.Size); except Raise Exception. Create(‘Não foi possível compactar o arquivo’); abort; end; finally Zip.Free; FileOut.Free; FileIni.Free; end; end; Fileini recebe o arquivo a ser compactado no formato stream para ser processado pelo objeto zip, que realiza a compressão e coloca o resultado no objeto fileout, este último também do tipo tfilestream. março 2009 11 DeZip:=TDecompressionStream. Create(FileIni); repeat i:=DeZip.Read(Buf, SizeOf(Buf)); if i <> 0 then FileOut. Write(Buf, i); until i <= 0; except raise Exception. Create(‘Não foi possível descompactar o arquivo’); abort; end; finally DeZip.Free; FileOut.Free; FileIni.Free; end; end; Repare que essa rotina possui a variável buf, um array de 1024 bytes onde será escrito o resultado da descompressão. Repare que caso algum erro ocorra, o processo de compressão é interrompido e é enviada uma mensagem ao usuário de que a compressão não foi possível. A chamada à função inputbox no início do evento permite que o usuário escolha o nome desejado para o arquivo depois de compactado. Os arquivos compactados com a unit zlib possuem a extensão .zip que é uma das extensões de arquivos compactados mais utilizada atualmente. O objeto mais importante de nosso aplicativo é o zip, do tipo tcompressionstream.A classe tcompressionstream da unit zlib é que contém os códigos que permitem a compressão de arquivos. Além da classe Tcompressionstream, a zlib também possui a classe tdecompressionstream, que como o nome sugere, realiza a descompressão de arquivos. Apesar de não ser o foco do artigo, estarei mostrando um pequeno exemplo de código para descompactação que pode ser inserido no evento onclick do btndescompacta: 12 março 2009 procedure Tfrmcompactador. btndescompactaClick(Sender: TObject); var FileIni, FileOut: TFileStream; DeZip: TDecompressionStream; i: Integer; Buf: array[0..1023]of Byte; nomearq:string; begin try try nomearq:=InputBox (‘Descompactar’,’Digit e um nome e a extensão do arquivo,’’); FileIni:=TFileStream. Create(edtarquivo. Text, fmOpenRead and fmShareExclusive); FileOut:=TFileStream. Create(nomearq,fmCreate or fmShareExclusive); Na rotina de descompressão, a inputbox deverá ser preenchida com o nome e a extensão do arquivo. Ex:teste.txt.Diferentemente da compressão onde apenas o nome é necessário. Como pôde ser observado, com a unit zlib é possível construir um compactador de arquivos até com certa facilidade sem a necessidade de componentes de terceiros. Espero que tenham gostado e até a próxima. Sobre o autor Antonio Spitaleri Neto Consultor Técnico The club [email protected] APRIMORANDO O DESEMPENHO DE UM BANCO DE DADOS No dia a dia, uma preocupação para desenvolvedores de bancos de dados, sem sombra de dúvidas, é a performance final dos mesmos. Neste sentido, muitos programadores se confundem no que tange a diferença entre um ajuste de banco de dados versus ajuste de uma instrução SQL. Assim desmistificando, temos: • O ajuste de um banco de dados significa o processo de ajustar o banco de dados real, ou seja, fatores como: alocação de memória, uso de disco, CPU, etc. Não podemos nos esquecer também que o ajuste também compreende a estrutura do banco de dados, como tabelas e índices. • O ajuste de uma instrução SQL, é o processo ajustar uma instrução SQL, como consultas, inserções, etc., com o objetivo de tirar o máximo proveito possível em performance e confiabilidade. Ajuste de uma instrução SQL O ajuste de uma instrução SQL, visa construir instruções com excelência, visando obter resultados com eficácia. A formatação de uma instrução representa um papel crucial na performance final, neste contexto o ajuste da clausulas FROM E WHERE são vitais, pois são essas duas clausulas das quais dependerão a forma em que o servidor de bancos dados irá processá-la. Embora possa ser rotineiro em nosso cotidiano, a construção de instruções SQL´s, requer atenção, e alguns pré requisitos fundamentais, eis alguns deles: • • • • Levar em conta sempre a legibilidade; Posicionamento das tabelas na cláusula FROM; Posicionamento de condições restritivas na cláusula WHERE; Posicionamento de condições de JOIN na cláusula WHERE; LEGIBILIDADE Quando se pensa em ajuste de uma instrução SQL, o primeiro passo a se levar em conta é a legibilidade da mesma, a fim de que fique bem escrita, e facilite a vida de quem além do desenvolvedor que a escreveu, possa discerni-la de forma rápida e segura. Para isto relacionaremos abaixo algumas particularidades que tornam uma instrução de forma legível: • Inserir uma cláusula FROM em uma linha separada da cláusula SELECT, e cláusula WHERE separada da cláusula FROM: SELECT ID_CODIGO, NOME, CPF FROM CLIENTES WHERE NOME = ‘GILSON’ • O uso de tabulações ou espaços e recomendável quando um argumento de uma cláusula, excedem uma linha. março 2009 13 • Quando da necessidade de utilizar várias tabelas em uma instrução, o uso de aliases é indispensável. SELECT C.ID_CODIGO, C.NOME, V.ID_CODIGO, V.VALOR FROM CLIENTES C, VENDAS V • Inicie sempre uma nova linha na instrução, caso haja a necessidade de se trabalhar com várias colunas na instrução SELECT, isto também se aplica as cláusulas FROM e WHERE. SELECT C.ID_CODIGO, C.NOME, C.CPF, C.TELEFONE, V.ID_CODIGO, V.VALOR, FROM CLIENTES C, VENDAS V WHERE C.ID_CODIGO = V.ID_CODIGO Veremos abaixo, um exemplo clássico de como muitos desenvolvedores escrevem uma instrução SQL, onde não se leva em conta a legibilidade: SELECT C.ID_CODIGO, C.NOME, C.CPF, C.TELEFONE, C.CIDADE, P.ID_CODIGO, P.NUM_PEDIDO, P.VALOR, P.QTDE FROM CLIENTES C, PEDIDOS P WHERE C.ID_CODIGO = P.ID_ CODIGO AND C.CIDADE = ‘SAO PAULO’ AND P.VALOR > 100 Agora levando em conta os princípios da legibilidade, vamos reescrever a instrução acima: SELECT C.ID_CODIGO, C.NOME, C.CPF, C.TELEFONE, C.CIDADE, P.ID_CODIGO, P.NUM_PEDIDO, P.VALOR, P.QTDE 14 março 2009 FROM CLIENTES C, PEDIDOS P WHERE C.ID_CODIGO = P.ID_ CODIGO AND C.CIDADE = ‘SAO PAULO’ AND P.VALOR > 100 Podemos observar que as duas instruções retornaram o mesmo número de registros, e não terão nenhuma diferença na sua performance, mas para quem as lerem, fará uma grande diferença, principalmente se estivermos desenvolvendo um projeto de grande porte, que envolvam muitos desenvolvedores. ORDEM ADEQUADA DE CONDIÇÕES DE JUNÇÃO: Quando trabalhamos com mais de duas tabelas em uma instrução SQL, a tabela base dever locada no lado direito de uma junção numa cláusula WHERE, onde a união deverá ser feita sempre primeiramente com a menor tabela e posteriormente deverá ser efetuada com as maiores, vejamos melhor no exemplo abaixo: SELECT <COLUNAS> FROM TABELA1 (TABELA MENOR) TABELA2 TABELA3 (TABELA MAIOR) WHERETABELA1.<COLUNA> = TABELA3.<COLUNA> TABELA2.<COLUNA> = TABELA3.<COLUNA> BUSCANDO A CONDIÇÃO RESTRITIVA Buscar a condição mais restritiva é o foco principal, visando um ótimo desempenho em uma consulta SQL. Assim podemos definir uma condição restritiva, como a forma de utilizarmos em uma cláusula WHERE de uma instrução que retorne uma menor quantidade de registros. Para isto é importante saber como o otimizador do banco de dados opera para executar a instrução, pois alguns banco por exemplo lêem da parte inferior da cláusula WHERE para cima, assim devemos colocar a condição mais restritiva por último, pois é a primeira condição que é lida pelo otimizador do banco utilizado. Abaixo vejamos a sintaxe básica: FROM TABELA1 <TABELA MENOR> TABELA2 TABELA3 <TABELA MAIOR> WHERE TABELA1.<COLUNA> = TABELA3.<COLUNA> AND TABELA2.<COLUNA> = TABELA3.<COLUNA> AND CONDIÇÃO 1 <MENOS RESTRITIVA> AND CONDIÇÃO 2 <MAIS RESTRITIVA> Se por ventura, na implementação utilizada, na sua documentação, não mencionar como o otimizador funciona quanto a condições de restrição, para que possamos escrever instruções que interajam de maneira mais eficiente com o banco de dados, a melhor forma, seria executar as consultas cronometrando o seu tempo de execução, verificando se o otimizar lê a cláusula WHERE, de cima para baixo, ou de baixo para cima. Posteriormente, devemos organizá-las de forma que as condições restritivas respondam de maneira concernente ao otimizador. É muito comum, no suporte do THE CLUB, os sócios muitas vezes, apresentarem muita preocupação e dúvidas quanto a conexão a ser escolhida para desenvolvimento de uma aplicação, isto sem dúvida requer uma escolha correta, mas nunca não podemos de nos esquecer que, banco de dados bem construídos, e instruções SQL´s bem escritas nos pouparão de muitos problemas, e nos retornarão, um resultado final muito mais proveitoso, principalmente aos olhos de nossos clientes. Um abraço e até a próxima. Sobre o autor Marco Antonio Armando Consultor Técnico The club Todos os aplicativos, exceto os triviais, precisam processar dados. Historicamente, a maioria dos aplicativos fornece um lógica própria para realizar essas operações. Mas essa estratégia pode fazer o código em um aplicativo tornar-se excessivamente amarrado à estrutura dos dados que ele processa. E se houver mudanças no esquema de banco de dados, talvez você necessite realizar um número significativo de alterações no código que trata os dados. A proposta da LINQ é abstrair o mecanismo que um aplicativo utiliza para consultar dados a partir do próprio código do aplicativo. A LINQ fornece uma sintaxe e semântica muito semelhantes da SQL, com vantagens parecidas. Você pode mudar a estrutura dos dados em consulta sem precisar alterar o código que a realiza. O diagrama acima apresenta uma visão geral do LINQ , seus provedores e as fontes de dados acessíveis: LINQ TO OBJECTS A LINQ tem como objetivo simplificar consultas a informações em collections, arrays, assim como informações baseadas em base de dados, XML e objetos. Tem como objetivo simplificar consultas a informações em collections, arrays, assim como informações baseadas em base de dados, XML e objetos Atualmente o LINQ suporta Atualmente o LINQ suporta: Linq to objects Linq to SQL Linq to xml LINQ to Objects LINQ to SQL LINQ to XML Delphi Prism suporta LINQ através de : Sequences e Query Expressions Lambda Expressions Expressions Trees Extension Methods Anonymous Types Type Inference Vamos entender como funciona o LINQ QUERY EXPRESSIONS março 2009 15 Permite executar querys em coleção de objetos. Vamos contruir um exemplo prático. Vá no menu File --> New --> Project . No treeview a esquerda escolha Delphi Prism. Selecione o template Windows Aplication .(Imagem 1) Clique em ok. No form Principal adicione um button e um listbox. Seu layout ficará assim (Imagem 2): Clique duas vezes no botão do formulário. Adicione no evento do botão o seguinte trecho de código (trecho 1) method MainForm.btnok_ Click(sender: System. Object; e: System. EventArgs); begin var words : Array of string :=[‘ola’,’Delphi Prism’, ‘linq’,’The Club’,’Alexandre’]; Imagem 1 var shortwords:= From word in words where Word.Length<=5 select Word; for each word in shortwords do begin lstValor.Items. Add(word); end; end; Trecho1. Imagem 2 Na edição anterior, abordei algumas caracteristicas da linguagem Oxygene( baseado em Object Pascal), vale a pena dar uma olhada. Observe a nova sintaxe de declaração de variáveis. Declarei um array com 5 elementos. Na variável shortwords (Query Exprressions) fiz um select dentro do array para trazer as palavras que contenham até 5 caracteres. Observe que a variável word receberá os itens consultados. Um ponto importante é que essa variável não necessita de uma declaração explicita e a mesma será utilizada no escopo do select. O select word retorna a instância do tipo array. 16 março 2009 O laço de repetição for each traz o resultado do Query Expressions.Dê um F5 para debugar e observe o resultado na figura 3. LINQ TO SQL Imagem 3 Permite executar querys em banco de dados, mapeando tabelas como classes, conhecidas como Entity classes. Tem como objetivo mapear tabelas e seus atributos como classes aproveitando o conheci- mento em SQL Vamos efetuar o mapeamento objeto relacional, criar um contexto de dados (DataContext) e consultar um banco de dados SQL Server com LINQ To SQL via código . Mapeando classes para Tabelas O conceito de mapeamento objeto Relacional (ORM) não é uma novidade. Um ORM pode ser visto como um classe (entidade) que possui propriedades ou campos que mapeiam para as colunas de um banco de dados. O mapeamento para uma tabela possui dois elementos básicos: 1. TableAttribute - mapeia a classe para a tabela no banco de dados; 2. ColumnAttribute - mapeia cada propriedade para uma coluna do banco de dados; Para mapear a classe para uma tabela usamos o TableAttribute com argumentos nomeados para associar a classe com a tabela. Abra o Visual Studio 2008 e crie um novo projeto do tipo Windows Application com o nome ExemploLInq. Imagem 4 A seguir inclua uma referência ao LINQ To SQL clicando com o botão direito sobre o nome do projeto e selecionando Add Reference; A seguir na janela Add Reference selecione, na aba .NET, System.Data.Linq e clique no botão OK. Veja a imagem 4; Com isso já temos tudo pronto para iniciar o projeto. Vamos criar um banco de dados com o Studio Express. No object Explorer clique duas vezes em seucomputador\sqlexpress. Com o botão direito do mouse em DataBase, acesse new DataBase. Em databaseName coloque Categorias. Clique em OK e pronto,nosso banco já esta criado. Veja a imagem 5. Imagem 5 março 2009 17 Clique duas vezes em CATEGORIAS e clique com o botão direito do mouse em tables. Adicione a primeira tabela conforme imagem 6 Faça o mesmo procedimento para adicionar a table SUBCATEGORIA. Veja a imagem 7. Faça o mesmo procedimento para criar uma tabela de contatos Vamos criar o diagrama do banco de dados. Clique em DataBase Diagrams com o botão direito do mouse e adicione um novo. Nosso relacionamento ficará assim: Veja a imagem 8. Imagem 6. Nosso objetivo será efetuar o mapeamento objeto relacional com as tabelas Contato Categoria e Subcategoria . No menu Project | Add New Item selecione o template Class e informe o nome Categoria.PAS. Clique em add. Nesta classe iremos definir o mapeamento entre as tabelas do banco de dados. Abra a classe e inclua a seguinte declaração : System.Data.linq, System.Data.Linq.Mapping, Agora inclua o seguinte código(Listagem1) que irá criar o mapeamento para as tabelas Categorias e SubCategorias para os campos definidos: Imagem 7. namespace ExemploLInq; interface uses System.Collections. Generic, System.Linq, System.Data.linq, System.Data.Linq.Mapping, System.Text; type [Table(Name:= 18 março 2009 Imagem 8. ‘CATEGORIAS’)] Categorias = public class Public [Column(IsPrimaryKey:=true, Name:=’CATEGORIAID’)] Property CategoriaID:Integer; [Column(Name:=’NOME’)] Property CatNome:String; End; [Table(Name := ‘SUBCATEGORIAS’)] Subcategorias = class Public [Column(IsPrimaryKey:=true, Name:=’SUBCATEGORIAID’)] Property SubcategoriaID:Integer; [Column(Name:=’NOME’)] Property SubcatNome:String; [Column(Name:=’CATEGORIAID’)] property CategoriaID:Integer; [Association (OtherKey:=’CategoriaID’)] Property Categorias: EntitySet<Categorias>; System.Linq, System.Data.linq, System.Data.Linq. Mapping, System.Text; type [Table(Name:= ‘CONTATOS’)] Contatos = Class Public [Column(IsPrimaryKey:=true, Name:=’CONTATOID’)] Property contatoid:Integer; [Column(Name:=’Nome’)] Property ContatoNome:String; End; implementation end. Na classe principal Main.pas adicione um DataGridView que se encontra na seção Data do ToolBox. Adicione também dois buttons e um textbox que se encontra na seção Common Controls. O textbox terá o caminho do banco. Adicione em uses : End; implementation end. Se trabalharmos com join podemos representá-las através de Associações . ENTIDADES PODEM ESTAR RELACIONADOS ATRAVÉS DO ATRIBUTO DE ASSOCIAÇÃO (Joins) Observe que z classe que verifica a associação entre Subcategorias e Categorias de um produto. Vamos mapear a tabela contatos.Crie uma outra classe Contatos .Listagem 1 namespace ExemploLInq; interface uses System.Collections. Generic, System.Linq, System.Data.Linq, System.Data.Linq. Mapping, O formulário ficará assim: No evento Clique do botão Query com Joins adicione o script abaixo: Adicione em uses System.Data.Linq, method MainForm.Joins_ Click(sender: System.Object; e: System.EventArgs); var Path :String := System.IO.Path. GetFullPath(tbDataBase. text); // inicialização da variável para pegar o banco de dados db:DataContext := new DataContext(path); cats:Table<Categorias> := db.GetTable<Categorias>(); subcats:Table<Sub categorias>:=db. GetTable<Subcategorias>(); Begin Var query:= from cat in cats join subcat in subcats on cat. CategoriaID equals subcat. CategoriaID order by cat.CatNome select new class (cat.CatNome,subcat. SubcatNome); dataGridView1.DataSource := query; end; Temos quatro variáveis,sendo a primeira é armazenado o caminho do banco e a segunda armazenda um DataContext que faz a conexão com o banco . As varíaveis categoria e subcategoria são do tipo table e através do método getTable elas referenciam seu tipo genérico. Em query temos os alias categoria e subcategoria representando as variaveis categorias e subcategorias respectivamente. Em “on categoria.CategoriaID equals subcategoria.CategoriaID” é feito o join. Os registros são ordenados em “order by Categoria.Nome”. março 2009 19 O resultado é retornado numa nova classe ( select new class (categoria.Nome,subcategoria. Nome) Atribuimos o resultado “query” que é um collection em um DataGridView. Nota do consultor. A classe DataContext é uma classe LINQ to SQL que atua como uma ponte entre o banco de dados SQL Server e as classes das entidades LINQ To SQL mapeadas para o banco de dados. Ela contém a informação e os métodos para efetuar a conexão com o banco de dados e manipular os dados. Da mesma forma , no botão query simples adicione o código abaixo: Imagem 9. LINQ TO XML method MainForm.Simples_ Click(sender: System. Object; e: System. EventArgs); Var Path :String := System.IO.Path. GetFullPath(tbDataBase. text); // inicialização da variável para pegar o banco de dados db:DataContext := new DataContext(path); // Criação do Contexto begin Var contato:= from contato in db.GetTable<Contatos>() where contato.contatonome = ‘JOSE’ select contato; dataGridView1.DataSource := contato; // atribuo a collection ao datasource end; 20 março 2009 O LINQ to XML é um provedor de dados LINQ que é implementado no namespace System.Xml. LINQ a partir da versão 3.5 da plataforma .NET. Ele fornece um modelo de programação que permite ler, construir e escrever dados XML. Você pode usar LINQ To XML para realizar consultas LINQ sobre dados no formato XML que podem ser retornados do arquivo de sistemas, de uma URL HTTP remota, de um web service ou partir de qualquer XML em memória existente. Observe a hierarquia da classe linq to XML. Veja a Imagem 9. As principais classes do link to xml são: • XDocument Atua como um container para a árvore XML e deve ser usado somente se necessário. Você pode salvar o documento com um arquivo XML. • XElement Permite criar elementos , modificar o conteúdo dos elementos (incluir, remover, modificar elementos filhos), modifica os atributos dos elementos , salvar como um arquivo XML. • XAttributeUsado para criar um par nome/valor no interior de um elemento do arquivo XML. • XName Representa um nome completo XML consistindo de um objeto XNamespace e do nome local. • XComment Representa um comentário XML. • XDocumentType Representa um documento XML DTD - Document Type Definition. • XProcesingInstructionRepresenta o processamento de uma instrução de um documento XML. • XTest Representa um nó Texto. • XDeclaration Representa uma declaração XML. O LINQ TO XML gera arquivos xml diretamente a partir das classes. Isso implica em redução de linha de código em até 4 vezes. Outro ponto importante é que o XML até então não tinha boa integração com as linguagens, sendo necessário o uso de diversas API’s. Vamos criar um pequeno exemplo desse poderoso recurso utilizando um array de livros. Crie um novo projeto em files->new projet windowapplication Adicione dois textbox e dois buttons. O layout ficará assim: Veja a Imagem 10 Para criar documentos completos XML você deve primeiro instanciar o objeto XDocument e então incluir o elemento XElement, XAtributte ou outra entidade que o documento necessita. Clique no botão gerar XML e adicione o código abaixo Imagem 10. Var xml:System.Xml.Linq. XElement; begin xml:= new xElement(‘Clientes’, new xElement(‘Cliente’, new xAttribute(‘codigo’,’1’), New XElement(‘nome’, ‘Alexandre’), New XElement(‘email’, ‘[email protected]’)), New XElement(‘cliente’, New XAttribute(‘codigo’, ‘2’), O arquivo xml foi atribuido para o textbox1 . Foi utilizado o método toString para conversão de tipos. Um arquivo teste.xml foi gerado no diretório raiz. No botão consultar xml adicione o código abaixo. var xml := XElement. Load(‘c:\teste.xml’, LoadOptions.SetBaseUri or LoadOptions. SetLineInfo); begin var consulta := v in xml.Elements from where v.Attribute(‘codigo’). Value = ‘2’ select v; New XElement(‘nome’, ‘Antonio’), For each cons in consulta do textBox2.Text:= cons.ToString; New XElement(‘email’, ‘[email protected]’))); end; textBox1.Text:= xml.ToString(); xml.Save(‘c:\ teste.xml’); end; Na variável xml foi atribuido o conteúdo do arquivo teste.xml por intermédio da classe XElement. O LoadOption possui quatro opções . None : todas as linhas desnecessárias,linhas em branco e linhas de informações, do arquivo XML não serão carregadas. PreserveWhitespace: essa opção define que todas as linhas em branco do arquivo XML março 2009 21 Imagem 11. serão preservadas. SetBaseUri : essa opção define o preenchimento da propriedade BaseUri. SetLineInfo: essa opção habilita o preenchimento da das informações de linha, essa informações pode ser recuperadas através da interface IXmlLineInfo. Na variável consulta atribuímos os elementos do arquivos onde o atributo código seja igual a 2. Observe o resultado da consulta. Veja a imagem 11. Conclusão Com esses exemplos dá para se ter uma ídéia do poder do Linq para manipulação de objetos, consultas e de arquivos xml. A Linq promete, sobretudo ser uma nova forma de trabalho de dados em memória. Sobre o autor Luís Alexandre de Oliveira é Técnologo em Processamento de Dados ,graduado pela Faculdade de Técnologia de Sorocaba, Consultor técnico do The Club. Docente do curso técnico informática - Etec de Avaré e do curso Tecnologia em Redes de Computadores - Fatec Eduvale – Avaré 22 março 2009 Referências: http://www.microsoft.com/brasil/msdn/Tecnologias/arquitetura/LINQ.mspx http://msdn2.microsoft.com/en-us/netframework/aa904594.aspx http://msdn2.microsoft.com/en-us/vcsharp/aa336746.aspx http://weblogs.asp.net/scottgu/archive/2007/05/19/using-linq-to-sql-part-1.aspx http://blogs.embarcadero.com/pawelglowacki/2005/09/22/21244 http://blogs.teamb.com/craigstuntz/2007/03/28/33565/ http://msdn.microsoft.com/pt-br/library/system.xml.linq.aspx http://edn.embarcadero.com/br/article/39135 Microsoft Visual C# Passo a Passo - John Sharp março 2009 23 Trabalhando com Dynamic Data Controls O ASP.NET ultimamente vem revolucionou o desenvolvimento de aplicações para web oferecendo uma plataforma robusta e altamente produtiva elevando o potencial dos desenvolvedores com excelente recursos como: GridView, DataControls, Validators , WebParts entre outros. A o longo dos anos, outras necessidades foram surgindo no mercado como a criação de telas automáticas de cadastro conhecidas atualmente como Scaffold bastante populares com o Ruby on Rails. Já faz algum tempo que a Microsoft vem investindo em linguagens dinâmicas e agora, com o lançamento do Service Pack 1 do .NET 3.5, foi disponibilizando dentro do ASP.NET um novo conjunto de controles conhecido como Dynamic Data Controls que tem objetivo de montar as telas para as ações básicas de qualquer CRUD (Create, Read, Update, Delete). Logo após instalar o Service Pack 1 do Visual Studio 2008, vai aparecer, conforme apresentado na figura abaixo, mais um novo tipo de projeto web para o .NET 3.5 chamado de “Dynamic Data” Veja na imagem 1. Após criar seu novo projeto, verifique nas referências e vai observar que ele traz as os novos namespaces System.Web.DynamicData e System. Web.DynamicData.Design. A funcionalidade Dynamic Data Controls trabalha em conjunto com o LINQ TO SQL e Entities FrameWork, pois ele precisa de uma interface de acesso a dados que será utilizada para gerar as funcionalidade em tempo de execução. Imagem 2. Bem agora para continuarmos com o exemplo precisamos de uma base de dados e um diagrama LINQ que será utilizado pela aplicação. Vou utilizar um banco de dados .mdb so SQLExpress que já tenho pronto. Agora vou criar o Diagrama LINQ e adicionar as tabelas que desejo trabalhar em meu projeto, para isso basta clicar com o lado direto do mouse no projeto, selecionar a opção add new item, e selecionar o DataClasses.dbml do LINQ. Como na imagem abaixo. Veja a imagem 2. Veja a imagem 3. 24 março 2009 Imagem 1. Imagem 3. março 2009 25 Imagem 4. Feito isso, agora vamos arrastar as tabelas que iremos utilizar no projeto para o diagrama do LINQ. Como na imagem abaixo. Agora vem a parte mais simples do projeto que será a implementação propriamente dita, ou seja onde colocamos a mão na massa, mais fique tranqüilo que com esta ferramenta trabalharemos muito pouco pois a idéia e que tudo seja gerado em tempo de execução. Para isso temos que configurar o Dynamic Data para utilizar o LINQ to SQL e gerar automaticamente as telas de manipulação das tabelas do diagrama. O primeiro lugar que vamos alterar e no arquivo global.asax nele devemos localizar o método RegisterRoutes() e dentro do mesmo modifique a linha Model.RegisterContext adicionando o nome do diagrama gerado pelo LINQ que para o nosso exemplo que foi DataClassesDataContext alterando o valor de ScaffoldAllTables para true Veja a Imagem 5. OBS: Esta linha geralmente vem comentada dentro do arquivo global.asax devemos descomenta-la e fazer as alterações necessárias Imagem 5. Pronto acabamos nosso projeto! Você pode estar se perguntando, nossa mais não fiz “quase nada”... Para ver o resultado basta rodar sua aplicação, e você verá as paginas de lista, inserção, deleção e edição prontas para utilização. Como nas imagens abaixo. Aqui temos o menu principal com acesso a todas as paginas do sistema: Veja a imagem 6. 26 março 2009 Ao Clicar no link de Produtos olha que legal, teremos um grid com os produtos cadastrados, onde as colunas do Grid são as mesmas colunas da Tabela, e o mais interessante e o filtro que foi gerado automaticamente de acordo com as Chaves da tabela de Produtos, tem também a paginação do Grid e tudo isso sem escrever nenhuma linha de código, temos também links para edição e deleção do produto e isso acontece para todas as tabelas relacionadas no diagrama. Note fizemos esse projeto em 45 min. Veja a imagem 7. Imagem 6.. Imagem 7. Você pode estar se perguntando e o layout como fica, não gostei desse, esta fora do padrão da minha empresa ou do meu site, não tem problema ele e feito todo em cima de CSS, você poderá customizar tudo, pode também gerar paginas personalizadas editar somente o GRID de Produtos, a ferramenta e bem Flexível. Agora você precisa tomar cuidado para que sua customização não te leve a fazer tudo do zero ai vale mais apena fazer de outra maneira. Esta ferramenta trará maior benefício se tiver pouca customização. Espero ter ajudado. Bons Códigos... Sobre o autor Fabiano Belmonte Senior Architect da InfoMoney.com, especialista em aplicações e-Business com larga experiência em B2B (Submarino.Com e Saraiva.Com). Trabalha há 5 anos com a tecnologia .Net, aplicando conhecimentos nas diversas áreas: instituições financeiras (sistema SPB), e-Commerce, gerenciamento logístico entre outras. Trabalhando com Visual Studio desde suas primeiras versões, responsável pela implementação de uma Metodologia de trabalho e melhoras significativas no resultados e na qualidade do time de Desenvolvimento de muitas empresas por onde passou como (Saraiva.Com) e ferramentas como TFS (Team Foundation Server). Foi palestrante em eventos como Codificando. NET 2008 e outros eventos sobre Tecnologia .NET. março 2009 27 Dicas DELPHI Ativar Auto Run do CD ROM // Declare USES. Registry na seção procedure SetCDAutoRun(AAutoRun:Boolean); const DoAutoRun : array[Boolean] of Integer = (0,1); var Reg:TRegistry; begin try Reg := TRegistry.Create; Reg.RootKey := HKEY_LOCAL_ MACHINE; if Reg.KeyExists(‘System\ CurrentControlSet\Services\Class\ CDROM’) then begin if Reg.OpenKey(‘System\ CurrentControlSet\Services\Class\ CDROM’, FALSE) then begin Reg. WriteBinaryData’AutoRun’, DoAutoRun[AAutoRun], 1); end; end; finally Reg.Free; end; ShowMessage(‘Esta alteração só será ativada após a reinicialização do PC.’); end; Criptografar Strings function Criptografar( const Str1: string): String; var Mascara,Str2: String; PonM, PonS: Byte; begin 28 março 2009 Mascara := ‘#$%$’#13#12; Str2 := ‘; PonM := 1; for PonS:=1 to length(Str1) do begin AppendStr( Str2, Chr( Ord(Str1[PonS]) Xor Ord(Mascara[PonM]))); Inc( PonM); if PonM>Length(Mascara) then PonM:=1; end; Result := Str2; end; Ver se existe Midia no Drive function DiskInDrive(Drive: Char): Boolean; var ErrorMode: word; begin Drive: = UpCase(Drive); if not (Drive in [‘A’..’Z’]) then raise EConvertError.Create(‘Not a valid drive ID’); ErrorMode := SetErrorMode(SEM_ FailCriticalErrors); try if DiskSize(Ord(Drive) - $40) = -1 then DiskInDrive := False else DiskInDrive := True; finally SetErrorMode(ErrorMode); end; end; Ver Impressora Conectada Function ImpresConect(Porta:Word):B oolean; Const Portas :Byte = $02; Var Res :Byte; Begin Asm mov ah,Portas; mov dx,Porta; Int $17; mov Res,ah; end; Result := (Res and $80) = $80; end; Localizar Arquivos no Windows TForm1 = class(TForm) Label1: TLabel; ListBox1: TListBox; Button1: TButton; procedure ListBox1Click(Sender: TObject); procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation procedure TForm1. Button1Click(Sender: TObject); begin with TDDEClientConv.Create(Self) do begin ConnectMode := ddeManual; ServiceApplication := ‘explorer. exe’; SetLink( ‘Folders’, ‘AppProperties’); OpenLink; ExecuteMacro(‘[FindFolder(, C:\ Windows)]’, False); CloseLink; Free; end; end Mostrar as fontes True Types instaladas no Windows unit Unit1; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; {$R *.DFM} // Evento OnClick do componente ListBox procedure TForm1.ListBox1Click(Sender: TObject); begin // A linha abaixo atribui a propriedade Caption do // componente Label o nome da fonte selecionada no // componente ListBox Label1.Caption := ListBox1. Items[ListBox1.ItemIndex]; // A linha abaixo muda a fonte de letra de acordo // com a fonte selecionada no componente Listbox Label1.Font.Name := ListBox1.Items[ListBox1.ItemIndex]; end; // Evento OnClick do componente Button procedure TForm1.Button1Click(Sender: TObject); begin // Carrega as fontes instaladas no Windows para // o componente ListBox ListBox1.Items := Screen.Fonts; end; type março 2009 29 30 março 2009 março 2009 março 2009