Pena que no Brasil ainda não temos essa cultura =)
Mas hoje é dia do programador, então, parabéns à todos que tem essa profissão!!! Ou tentam ter hahahahahaha.
Abraços!
Se eu não apertei Shift + Del...
Pena que no Brasil ainda não temos essa cultura =)
Mas hoje é dia do programador, então, parabéns à todos que tem essa profissão!!! Ou tentam ter hahahahahaha.
Abraços!
Uma postagem rápida para falar sobre SQL e aproveitar para dizer que tive 2 artigos publicados na SQL Magazine deste mês de Agosto (Edição 78). \o/ hehehehe!!!!
Então... Acabei de me deparar com um problema bobo hoje. Estava criando um procedure que tinha um status do tipo Bit.
Para chamar a procedure passava 0 ou 1... Maravilha!
Entretanto, no sistema no qual a procedure ia ser chamada, poderia listar os ativos, inativos ou os ativos (E) inativos.
Poxa que fácil, basta apenas passar no parâmetro do status o valor NULL.
Vamos ao exemplo:
CREATE PROCEDURE [dbo].[ListarNulo]
@Status BIT
AS
BEGIN
SELECT
[ParceiroCodigo]
,[CPF]
,[NumMatricula]
FROM [ListaBranca]
WHERE
([Status] = @Status OR @Status = NULL)
END
Poxa, que nada de diferente, pronto acabou! Éééé... não, infelizmente quando rodei a procedure passando NULL no parâmetro... Não me trouxe nada! Mas pq???
Bom, foram 10 minutos revisando a procedure (não era essa acima, era uma maiorzinha) e mas 10 minutos para descobrir e bater a mão na testa dizendo "nossa como eu sou burro".
A frase do que eu precisa me dizia a resposta. "Preciso verificar se o parâmetro (É) nulo." "É" nulo e não "Igual" a Nulo. Vamos alterar a procedure.
ALTER PROCEDURE [dbo].[ListarNulo]
@Status BIT
AS
BEGIN
SELECT
[ParceiroCodigo]
,[CPF]
,[NumMatricula]
FROM [ListaBranca]
WHERE
([Status] = @Status OR @Status IS NULL)
END
Podemos ver que tirei o igual e coloquei a cláusula IS, apenas para verificar se é nulo. Resolvido! IS um lixo mesmo... Abraços!
Encontro poucas informações sobre WCF atualmente na Internet. Na verdade pouco usei esta tecnologia, mas quando começei a pesquisar sobre a maior dúvida era: O que WCF tem de diferente dos Web Services.
Para um conteúdo completo, recomendo o blog do MVP Israel Aece. Ele fez alguns vídeos e explica muitas coisas do WCF, falando também as diferenças entre as tecnologias.
A partir do blog dele, tentei fazer uma tabela básica com o que existe em Web Services e o que existe no WCF. O Resultado é o lixo abaixo:
| Ação | Web Services | WCF |
|
Estrutura de Projeto |
Arquivos .asmx, trabalha com métodos de serviço. | Arquivos .srv, trabalha com contratos de serviços. |
| Contratos | Obrigatório o uso atributo [WebMethod] em cada método que estará disponível para o acesso. | Interfaces determinam os contratos, decoradas com [ServiceContract]. Pode ter tipos complexos vinculados aos serviços. |
|
Serialização/ Deserialização |
Serializa todos os membros públicos com XmlSerializer. | Utiliza o DataContractSerializer, parecido com XmlSerializer porém persiste membros private e protected e gera um XML mais simplificado, melhorando a interoperabilidade entre as plataformas. |
|
Protocolo/ Hosting |
Pode somente ser hospedado no IIS, utilizando o protocolo HTTP/HTTPS. | Arquitetura muito mais flexível e é independente do protocolo, ou seja, ele pode rodar em HTTP, HTTPS, TCP, MSMQ, etc. Pode usar o IIS como host ou ser um serviço instalado no Windows. |
| Extensibilidade | Interceptar as requisições através de SOAP Extensions. | Inúmeros pontos de extensibilidade tanto do lado do serviço quanto do cliente. |
| Segurança | Segurança baseada no transporte, ou seja, ele somente será seguro se você expor o serviço através de HTTPS. Pode utilizar SoapHeader com usuário e senha. Funciona bem, mas é vulnerável. | É seguro por padrão. Para utilizar a autenticação baseada em UserName/Password que o WCF fornece sob o protocolo HTTP, será necessário utilizar um certificado. |
| Configuração | Utiliza a Web.config de forma declarativa | Seção de configurações são extremamente ricas. Segurança, transações, know types, behaviors, bindings, endpoints, contratos, etc. É um pouco complexo. Grande parte do que temos no WCF não existe nativamente no ASMX. |
| Interoperabilidade | Interoperabilidade O ASMX não possui nativamente suporte a especificações que foram definidas por grandes "players" do mercado. Precisa da instalação de um Add-on para o Visual Studio .NET. | Padrões já foram implementados nativamente e são configuráveis. Consegue facilmente conversar com serviços construídos em ASMX em ambos os lados. |
| Cliente | Pode ser integrado com muitas aplicações. | Muito mais flexível que o ASMX. |
| Desempenho | http://msdn.microsoft.com/en-us/library/bb310550.aspx | |
|
Resultado Final |
Gera WSDL | Gera WSDL |
| Conclusão |
Muitas funcionalidades ainda são de forma idêntica entre o WS e o WCF. Quanto a questões de configuração, segurança, desempenho e Interoperabilidade o WCF ganha por ter todos os padrões de XML adicionados nativamente, englobar todas as tecnologias de computação distribuída da Microsoft e ser mais flexível a alterações e extensibilidade. Os Web Services são pouco mais fáceis de se trabalhar quanto a questão de criação de projeto e análise de erros. Entretanto a questão de desenvolvimento o WCF é bem mais avançado. Por fim, os dois geram WSDL para o cliente consumir os dados. |
|
Se colocarmos em uma balança as duas tecnologias. Realmente pouca coisa parece ter mudado.
Entretanto, pensando no seguinte fato: Se hoje minha empresa vai começar a criar algo orientado a serviços, deveriamos usar o WCF, pois é uma nova tecnologia que vai substituir o Web Services.
Agora, fazer a migração das tecnologias, talvez isso ainda (atualmente) não vale tanto a pena. Pelo mesmo motivo de "parecer" o mesmo da mesma coisa.
Bem simples essa resposta. Funciona como esta na imagem acima =).
Ok, não é tão simples. Mas ai está o ponto deste lixo de post. As matrizes, ou como chamamos no C# arrays, multidimensionais são, como muitos professores de faculdade dizem, uma caixa de Biz, ou melhor, linha e coluna igual na matemática.
E eles estão certos. É isso mesmo. Mas não é como o C# trata isso internamente.
Arrays são Reference Types, traduzindo, variáveis por referência (para quem não sabe, existem 2 tipos, os tipos por referência e os por valor, os Value Types). Os Reference Types guardão, ao invés do valor da variável, o endereço de memória no qual este valor está disponível.
Entendendo isso, vamos a Array. Imaginando a seguinte entrada do programa:
int[,] x = new int[2, 3]; x[1, 2] = 10;
Na primeira linha, criamos uma matriz “x” de inteiros com 2 linhas e 3 colunas. Na realidade, inserimos na memória stack (Pilha) um endereço, podemos ver essa ação na imagem acima (e todas as outras ações também =P). Podemos ver que “x” guarda o endereço de memória “adew:12ws”.
A partir dessa posição de memória podemos entender como o C# se comporta.
Como temos uma array de 2 por 3, vamos ter primeiro neste endereço (“adew:12ws”) outros dois endereços conforme solicitamos na “linha” [2, 3], e esses dois endereços terão as quantidades (três neste caso) de VALORES que solicitamos na "coluna" [2, 3].
Eu disse valores em maiúscula pois realmente na última instrução da array não teremos mais apontamento para memória e sim o valor real guardado. Esse valor é iniciado zero para todas as 6 possibilidades.
E como funciona a troca de valor, como a segunda linha de instrução diz x[1, 2] = 10? Na imagem podemos ver passo a passo como funciona.
A variável "x", guarda o endereço “adew:12ws” que em 0 guarda o endereço “apop:1234” e em 1 guarda o endereço “eeee:uuuu” (lembrando que as posições da array sempre começam com 0 e terminam em valor - 1). Vamos trocar o valor na linha 1 (“apop:1234” em “adew:12ws”) da coluna 2 (valor 0 em “apop:1234”) para 10 (risca o zero e coloca o 10). Pronto assim o C# trata as arrays multidimensionais.
E se tivéssemos mais uma dimensão?
int[,,] x = new int[2, 3, 2];
A mesma idéia iria se aplicar, onde "x" apontaria para um endereço de memória (como na pilha vista) com 2 endereços de memória [2, 3, 2]. Esses dois endereços apontariam para mais três endereços de memória [2, 3, 2] e cada um desses endereços de memória teriam 2 valores [2, 3, 2]. Como muita coisa na informática é feita uma árvore de dados.
Esse post foi um pedaço da aula de orientação a objetos avançada que fiz junto com minha empresa com o professor Agnaldo no centro de treinamento 50 minutos. Achei realmente muito interessante.
Abraços.
Temos listas genéricas no Framework do .Net desde a versão 2.0. A primeira vez que eu vi já adorei pelo fato de aumentar o desempenho devido a não necessidade de conversão de tipos. Outro ponto é a segurança, pelo mesmo motivo da tipagem, antigamente nos ArrayLists poderíamos sem querer colocar uma string dentro de uma lista de inteiros, e só descobrir isso em execução. Por fim, também temos um código enxuto.
Então, lista genérica não é novidade, encontramos vários exemplos na Internet de como criá-las. Também encontramos exemplos de como fazer filtros ou encontrar objetos dentro das listas, mas são um pouco mais complexos de se achar e quando encontrados não são muito explicativos. Por isso então, este post vai pro lixo.
Vamos criar uma Lista de Pessoas, básico para estudar esse tipo de assunto (Vou usar um ConsoleApplication):
public class Pessoa
{
private string Nome { get; set; }
private Int16 Idade { get; set; }
private char Sexo { get; set; }
public Pessoa(string nome, Int16 idade, char sexo)
{
Nome = nome;
Idade = idade;
Sexo = sexo;
}
public Pessoa()
{
Nome = string.Empty;
Idade = 0;
Sexo = char.MinValue;
}
}
class Program
{
static void Main(string[] args)
{
List<Pessoa> lstPessoas = new List<Pessoa>();
lstPessoas.Add(new Pessoa("Adriano Galesso", 23, 'M'));
lstPessoas.Add(new Pessoa("Manuela Bognar", 21, 'F'));
lstPessoas.Add(new Pessoa("Pedro Alberto Braga", 44, 'M'));
lstPessoas.Add(new Pessoa("Rosalinda da Silva", 75, 'F'));
Existe(lstPessoas);
Procura(lstPessoas);
Remove(lstPessoas);
Console.ReadLine();
}
}
Temos uma lista com 4 objetos Pessoa. Agora vamos começar a “brincar” com nossa lista.
Primeiramente vamos criar um método que verifica se existem alguns nomes em nossa Lista. Para isso vamos utilizar a função Exists da lista genérica.
private static void Existe(List<Pessoa> lstPessoas)
{
string[] nomePesquisa = new string[] { "Adriano", "Margarida", "Pedro", "Marcos", "Graciele" };
for (int i = 0; i < nomePesquisa.Length; i++)
{
Console.WriteLine(lstPessoas.Exists(
delegate(Pessoa pessoa)
{
return pessoa.Nome.IndexOf(nomePesquisa[i]) >= 0;
}) ?
nomePesquisa[i] + " Existe na Lista" :
nomePesquisa[i] + " Não Existe na Lista");
}
Console.WriteLine("\n");
}
Neste método temos uma array de strings com nomes para pesquisar na lista. Para cada string vamos mostrar se existe ou não o nome da lista (mesmo que parte do nome).
Passamos uma lista de pessoas para o método fazer a verificação. Para a verificação foi utilizada a função Exists. Esta função espera um System.Predicate tipado, ou seja, um delegate que contém a regra para a busca dentro da lista. Sendo mais especifico, devemos delegar uma função para fazer qualquer condição que gostaríamos.
Neste caso o delegate fará a verificação de cada objeto pessoa em nossa lista, vendo se parte do Nome contém as strings da nossa array de pesquisa. Se tiver escrevemos uma coisa e se não tiver escrevemos outra.
Basicamente o mais importante de toda a busca é o delegate. Em uma função delegate, deve-se retornar sempre um boleano. Neste caso usamos o delegate junto com o próprio método, mas podemos fazer separadamente com um método qualquer. Vamos ver isso mais para frente.
Agora vamos conhecer a função Find() e FindAll(). A diferença entre as duas é bem simples. O Find() busca na lista, crescente, um resultado válido, caso encontre, ele para de buscar e retorna um resultado. Em contraponto o FindAll() procura por todos valores da lista e retorna uma outra lista de resultados válidos.
Vamos estudar o método Procura.
private static void Procura(List<Pessoa> lstPessoas)
{
//Função Find retornando uma pessoa pelo Nome
Pessoa pessoa = lstPessoas.Find(PorNome);
Console.WriteLine(
pessoa != null ?
"Pessoa Encontrada (No Find): " + pessoa.Nome + ", " + pessoa.Idade + ", " + pessoa.Sexo :
"Nenhuma pessoa Encontrada");
//Função FindAll retornando uma lista de pessoas pelo Sexo
foreach (Pessoa pessoaLocal in lstPessoas.FindAll(PorSexo))
{
Console.WriteLine("Pessoa Encontrada (No FindAll): " + pessoaLocal.Nome + ", " + pessoaLocal.Idade + ", " + pessoaLocal.Sexo);
}
Console.WriteLine("\n");
}
Este método começa utilizando a função Find da lista genérica. Assim como o método anterior, passamos a lista carregada.
Criamos um objeto pessoa, pois já sabemos que o Find retorna um resultado único. Podemos notar que não é utilizado o delegate, entretanto o Find também espera um System.Predicate.
Isto porque, agora chamamos direto um método, o PorNome.
private static bool PorNome(Pessoa pessoa)
{
if (pessoa.Nome == "Adriano Galesso")
{
return true;
}
return false;
}
O método PorNome retorna um boleano (como dito anteriormente). E recebe uma pessoa, mas como isso se não passamos nada na chamada do método?
Como o predicate é tipado, automaticamente se sabe que objeto você está tentando procurar. Então no parâmetro do método é passado o objeto da lista (funciona como um foreach).
No método PorNome simplesmente fazemos uma validação qualquer e retornamos ao método Procura. Que verificará se o objeto pessoa não esta nulo e escrever que foi encontrado na lista.
Continuando no método Procura. Agora vamos utilizar a função FindAll() da lista genérica. Como já sabemos teremos uma lista de resultados, pois ele irá procurar em todos objetos da lista. O predicate que utilizaremos é o método PorSexo.
private static bool PorSexo(Pessoa pessoa)
{
if (pessoa.Sexo == 'F')
{
return true;
}
return false;
}
Nada de diferente neste método, simplesmente vamos procurar em nossa lista todas as mulheres.
Voltando novamente no método Procura, como temos uma lista de resultados vamos usar o foreach para escrever os resultados com sucesso de nossa lista.
Por fim, vamos ver o método Remove.
private static void Remove(List<Pessoa> lstPessoas)
{
lstPessoas.Remove(new Pessoa("Adriano Galesso", 23, 'M'));
lstPessoas.RemoveAll(Jovens);
foreach (Pessoa pessoa in lstPessoas)
{
Console.WriteLine("Velhos: " + pessoa.Nome + ", " + pessoa.Idade + ", " + pessoa.Sexo);
}
Console.WriteLine("\n");
}
Como anteriormente, passamos nossa lista de pessoas como parâmetro. Inicialmente usamos a função Remove da lista genérica. Que não tem nada de novo, simplesmente passamos um objeto que gostaríamos de remover e pronto, nada de predicate.
Depois disso vamos Remover todos os Jovens da Lista com o RemoveAll. Assim como o FindAll teremos uma lista de resultados. A única diferença é que uma vez removido da lista, não poderemos mais encontrar o objeto removido. (Por exemplo, como a lista é por referencia de memória, se chamássemos o método Remove antes dos outros, nossa lista estaria com menos objetos).
Utilizaremos como predicate o método Jovens.
private static bool Jovens(Pessoa pessoa)
{
if (pessoa.Idade <= 30)
{
return true;
}
return false;
}
Vamos remover da lista, todas as pessoas com menos de 30 anos. Voltando ao método Remove, escrevemos a lista com os novos resultados.
Por fim, o mais legal das “buscas” em listas genéricas são os predicates. Mas ainda fica uma dúvida no ar. E se eu quiser passar mais parâmetros para um método delegate?
Bom, tem como, mas não fique todo felizão como eu fiquei quando descobri. Primeiro saiba que isso só é possível a partir do Framework 3.5 (meu projeto era no 2.0 =/).
Primeiramente se estivermos usando o Framework 3.5, podemos utilizar os Lambda Expression. Lambda Expression é a evolução dos delegates com pequenas notações, ou seja, se for só para retornar um valor simples, como por exemplo:
Método:
Double MultiplicaPorCinco(int x)
{
return x * 5;
}
Lambda Expression:
x => x * 5;
Vamos aplicar no método Remove:
private static void Remove(List<Pessoa> lstPessoas)
{
//lstPessoas.Remove(new Pessoa("Adriano Galesso", 23, 'M'));
lstPessoas.RemoveAll(pessoa => pessoa.Idade <= 30);
foreach (Pessoa pessoa in lstPessoas)
{
Console.WriteLine("Velhos: " + pessoa.Nome + ", " + pessoa.Idade + ", " + pessoa.Sexo);
}
Console.WriteLine("\n");
}
Se olharmos acima, veremos que foi substituído, o método Jovem pelo Lambda Expression.
Legal, e os 2 parâmetros na função?
Bom, isso vai ficar para o próximo Post, quando o lixo estiver cheio de Func =P e mais Lambda Expression
Abraços... PS: Recorri a um script para ajustar os códigos, é bem mais fácil =)
Ultimamente tenho escrito alguns artigos para revistas e sites de tecnologia e a lixeira acaba ficando um pouco vazia.
Entretanto, sempre tem alguma coisa velha que é uma boa nova idéia. Digo velho porque recuperar QueryStrings e variáveis de Sessão em C# até minha tia vó já sabe fazer. Mas será que ela faz da melhor maneira?
Imaginado minha Query String “teste”. “Teste” sempre será a identificação de um elemento que eu gostaria de passar pelas páginas. E outra, eu sei que “teste” é um inteiro.
Como recuperar?
int meuTeste = Convert.ToInt32(Request.QueryString["teste"]);
Isso é realmente fácil (minha tia vó que postou). Agora vamos falar pra minha tia vó que infelizmente o mundo não é perfeito e podemos ter engraçadinhos que tentam trocar o valor da QueryString manualmente (eu sou um deles). Por Exemplo, colocar um “teste=a” na URL.
Neste ponto minha tia vó sai de cena, isso ela já não sabe. Vamos ser espertos e colocar um Try pra pegar esses engraçadinhos. Mas não podemos ter um Try sem Catch e também não podemos parar a execução da página só porque temos uma variável sem valor (só se a página inteira for em torno desse valor). Neste caso então, vamos verificar se existe o valor da variável na Sessão “teste”, quem sabe o valor não está lá dentro!
int meuTeste = 0;
try
{
meuTeste = Convert.ToInt32(Request.QueryString["teste"]);
}
catch
{
meuTeste = Convert.ToInt32(Session["teste"]);
}
Legal, mas e sem não existir QueryString e não existir a Sessão? Minha página às vezes tem essa QueryString e às vezes tem essa Sessão? Vamos aninhar os Trys ai nos Catches nós pegamos zero da variável bla bla bla bla...
Nada disso. Esquece todos os códigos postados até aqui, pois para resolver qualquer problema para recuperar QueryString ou Sessão de tipos nativos devemos usar o TryParse. Digo devemos porque não há o que fazer se o valor que tentamos atribuir à uma variável não for de um tipo valido para mesma, assim evitamos exceções e obtemos valores válidos em qualquer tipo de ambiente.
Como não gosto de ficar sozinho em uma opinião, tenho dois link que além de me defenderem, eles fazem testes de desempenho entre o parse e o TryParse
http://www.informit.com/guides/content.aspx?g=dotnet&seqNum=523
http://www.codinghorror.com/blog/2005/08/tryparse-and-the-exception-tax.html
Entretanto a questão deste post não é escrever sobre conversões de tipos e sim recuperar valores de QueryStrings ou Sessões. Então, como fazer isso de maneira limpa e prática.
Vamos voltar ao mesmo cenário onde a variável de QueryString “teste” pode existir ou não em uma página, assim como a Sessão “teste” pode existir ou não. Lembro também que não podemos esquecer de testar se o valor da QueryString é um inteiro e não vai me jogar uma exceção.
Chegamos as seguintes linhas de código:
int meuTeste = 0;
if (!int.TryParse(Request.QueryString["teste"], out meuTeste))
{
int.TryParse(Session["teste"], out meuTeste);
}
O código diz que, se não for convertido a QueryString “teste” para minha variável de saída, que é um inteiro, ele tentará converter a Sessão “teste”. Não vamos verificar se ele consegue converter a Sessão, pois se não o valor da variável vai ser 0 de qualquer maneira.
E porque não usar o Convert.ToInt32? Justamente porque vamos ter que ficar testando os valores pra ver se realmente é um inteiro. E fazer esse processo deixa o código cheio e “chato”.
Por fim, podemos fazer o TryParse além de inteiros, em Decimal, DateTime, Float ou Double.
Até a próxima!
Após a publicação do Blog do meu amigo Tadanori sobre usabilidade, mais precisamente voltado à e-Commerce, achei essa imagem na net que diz muito sobre a realidade de uma empresa e como cada membro de uma determinada equipe tem uma opnião sobre como deve ser a usabilidade de um site.
De acordo com Jakob Nielsen, o maior análista de usabilidade atualmente, dono do lindo site http://www.useit.com/, pesquisas de usabilidade ajudam muito na modo de como um site pode ou não fazer sucesso na Web. Ou seja, usabilidade terá mais espaço aqui na Lixeira futuramente.
Olá pessoal, desculpe a falta de lixo neste dias que se passaram. Depois de 2 anos eu tirei minhas férias! \o/
Bom, este ano, vou voltar com a parte de padrões de projeto e outros assuntos que vão bomabar a lixeira.
Depois de 3 meses de muita quebradeira em casa, agora tenho meu espaço de volta e talvez consiga postar mais facilmente hahahahahaha.
Hoje vou falar, mais uma vez, sobre XML... É não tem como escapar.
Vamos ao seguinte cenário, temos 3 tabelas em nosso banco de dados, são elas: Conta, Usuario, Endereço. Meu cliente tem um sistema interno que recebe os novos usuários de seu site, ele quer receber diariamente todos os usuário do site em formato XML. Porém não só os usuários novos, ele quer todos os dias TODOS os usuário (É isso existe).
Meu cliente (Bão de venda), tem pré-cadastrados 100.000 usuários, ele pretende ter cerca de 500 novos por dia =O. Se minha calculadora estiver certa 182.500 por ano, pouco até, mas já precisa-se pensar sobre a performance.
O XML deve ter o seguinte formato:
<Usuario>
<Nome>Teste</Nome>
... *
</Usuario>
* São os campos: Nome, Sobrenome, Login, E-mail, Sexo, DataCadastro, Tipo, CPF, CNPJ, Razão Social, RG, DataNascimento, Logadouro, Numero, Complemento, Referencia, Bairro, Cidade, Estado, Pais, Telefone1, Telefone2, Celular, Fax... E assim vai porque isso é só para exemplificar que é "campo pra caramba"!!!
Então, podemos fazer de 2 modos:
O 1º é fazer uma view e após uma procedure que retorna todos esses carinhas para o meu objeto (1 objeto? Sei lá acho que vão ser 3 objetos ^^, ou um DataSet talvez, não importa) no C#, lá eu varro a lista e vou escrevendo o XML de uma das N maneiras que o C# tem de escrever um XML. Pronto...
Ou podemos deixar 60% mais rápido.
Podemos até criar a view, porém é a procedure que vai retornar para o C# um XML formatado, o C# vai jogar em um arquivo e vai salvar. Joiá! Mas como?
Para exemplificar vamos trabalhar com Nome, Sobrenome e E-mail. Assim pulamos a parte da View e vamos direto para a procedure:
CREATE PROCEDURE GerarXMLUsuarios
AS
BEGIN
SELECT [UsuNome]
,[UsuSobrenome]
,[UsuEmail]
FROM [tbUsuarios]
END
Agora utilizaremos a cláusula FOR XML.
O FOR XML simplesmente organiza e retorna as informações em formato XML.
Temos as seguintes opções no FOR XML:
1 – FOR XML AUTO:
O modo AUTO retorna os resultados da consulta como elementos XML simples e aninhados, ou seja, cada tabela indicada depois da cláusula FROM é um elemento do XML, as colunas indicadas no SELECT são atributos. Utilizamos o AS para nomear os elementos:
SELECT [UsuNome] AS Nome
,[UsuSobrenome] AS Sobrenome
,[UsuEmail] AS Email
FROM [tbUsuarios] AS UsuariosGerais
FOR XML AUTO
Resultado:
<UsuariosGerais Nome="Adriano" Sobrenome="Teste" Email="adriano.galesso@teste.com.br" />
<UsuariosGerais Nome="renata" Sobrenome="Teste" Email="renata@teste.com.br" />
<UsuariosGerais Nome="Christiano" Sobrenome="Teste" Email="web@teste.com.br" />
<UsuariosGerais Nome="suporte" Sobrenome="Teste" Email="suporte@teste.com.br" />
Se alguma coluna do SELECT for de uma tabela já identificada ela é colocada como atributo (conforme visto acima), se for de uma tabela diferente será adicionada como elemento dentro do elemento pai, que é a 1º tabela especificada:
SELECT UsuariosGerais.[UsuNome] AS Nome
,UsuariosGeraisIntro.[UsuSobrenome] AS Sobrenome
,UsuariosGerais.[UsuEmail] AS Email
FROM [tbUsuarios] AS UsuariosGerais INNER JOIN
[tbUsuarios] AS UsuariosGeraisIntro
ON UsuariosGerais.UsuCodigo = UsuariosGeraisIntro.UsuCodigo
FOR XML AUTO
Resultado:
<UsuariosGerais Nome="Adriano" Email="adriano.galesso@teste.com.br">
<UsuariosGeraisIntro Sobrenome="Teste" />
</UsuariosGerais>
<UsuariosGerais Nome="renata" Email="renata@teste.com.br">
<UsuariosGeraisIntro Sobrenome=" Teste " />
</UsuariosGerais>
<UsuariosGerais Nome="Christiano" Email="web@teste.com.br">
<UsuariosGeraisIntro Sobrenome=" Teste " />
</UsuariosGerais>
<UsuariosGerais Nome="suporte" Email="suporte@teste.com.br">
<UsuariosGeraisIntro Sobrenome=" Teste " />
</UsuariosGerais>
Vejam que, como estou pegando o Sobrenome de uma “tabela diferente”, ele criou uma nova Tag dentro da principal e aplicou o atributo Sobrenome a ela.
Assim funciona o FOR XML AUTO.
2 – FOR XML RAW:
No modo RAW cada resultado da instrução SELECT vem em linha de acordo com o nome fornecido ao RAW (que se não for fornecido é raw mesmo):
SELECT [UsuNome] AS Nome
,[UsuSobrenome] AS Sobrenome
,[UsuEmail] AS Email
FROM [tbUsuarios] AS UsuariosGerais
FOR XML RAW
SELECT [UsuNome] AS Nome
,[UsuSobrenome] AS Sobrenome
,[UsuEmail] AS Email
FROM [tbUsuarios] AS UsuariosGerais
FOR XML RAW('Teste')
Resultado:
<row Nome="Adriano" Sobrenome="Teste" Email="adriano.galesso@teste.com.br" />
<row Nome="renata" Sobrenome="Teste" Email="renata@teste.com.br" />
<row Nome="Christiano" Sobrenome="Teste" Email="web@teste.com.br" />
<row Nome="suporte" Sobrenome="Teste" Email="suporte@teste.com.br" />
<Teste Nome="Adriano" Sobrenome="Teste" Email="adriano.galesso@teste.com.br" />
<Teste Nome="renata" Sobrenome="Teste" Email="renata@teste.com.br" />
<Teste Nome="Christiano" Sobrenome="Teste" Email="web@teste.com.br" />
<Teste Nome="suporte" Sobrenome="Teste" Email="suporte@teste.com.br" />
Nota-se que diferente do AUTO, podemos nomear nossas elementos de acordo com o que colocamos no RAW e tabelas diferentes não são criadas como novos elementos filhos.
Assim funciona o FOR XML RAW
3 – FOR XML PATH:
O modo PATH tem uma maneira simples de misturar nós de elementos e atributos, e para propriedades mais complexas o PATH pode ser utilizado para trazer resultados com mais facilidade. Os nomes ou alias de colunas são tratados como expressões XPath.
Vamos ao teste com nome de colunas:
SELECT [UsuNome] AS '@Nome'
,[UsuSobrenome] AS Sobrenome
,[UsuEmail] AS Email
FROM [tbUsuarios] AS Usuarios
FOR XML PATH
Resultado:
<row Nome="Adriano">
<Sobrenome>Teste</Sobrenome>
<Email>adriano.galesso@teste.com.br</Email>
</row>
<row Nome="renata">
<Sobrenome>barbosa</Sobrenome>
<Email>renata@teste.com.br</Email>
</row>
<row Nome="Christiano">
<Sobrenome>Teste</Sobrenome>
<Email>web@teste.com.br</Email>
</row>
<row Nome="suporte">
<Sobrenome>Teste</Sobrenome>
<Email>suporte@teste.com.br</Email>
</row>
Nota-se que onde colocamos o @ temos um Atributo, os que estão sem o @ são Elementos.
Outro teste, pra mim o mais legal, é o seguinte:
SELECT [UsuCodigo] AS '@ID'
,[UsuNome] AS 'Nome/Nome'
,[UsuSobrenome] AS 'Nome/Sobrenome'
,[UsuEmail] AS 'Email'
FROM [tbUsuarios] AS Usuarios
FOR XML PATH('Usuarios')
Resultado:
<Usuarios ID="216562">
<Nome>
<Nome>Adriano</Nome>
<Sobrenome>Teste</Sobrenome>
</Nome>
<Email>adriano.galesso@teste.com.br</Email>
</Usuarios>
<Usuarios ID="216562">
<Nome>
<Nome>renata</Nome>
<Sobrenome>Teste</Sobrenome>
</Nome>
<Email>renata@teste.com.br</Email>
</Usuarios>
<Usuarios ID="216563">
<Nome>
<Nome>Christiano</Nome>
<Sobrenome>Teste</Sobrenome>
</Nome>
<Email>web@teste.com.br</Email>
</Usuarios>
<Usuarios ID="216566">
<Nome>
<Nome>suporte</Nome>
<Sobrenome>Teste</Sobrenome>
</Nome>
<Email>suporte@teste.com.br</Email>
</Usuarios>
Nota-se que quando colocado “/” entre os nomes dos elementos, o elemento anterior a “/” é o pai e o posterior é o elemento filho.
Outro ponto é o @, ele só pode ser usado se for o 1º elemento, ou se o elemento anterior for @ também. No caso se colocássemos o @ no Email daria um erro.
Por fim, o PATH pode ser nomeado assim como o RAW.
Assim funciona o FOR XML PATH.
4 – FOR XML EXPLICIT:
O mais avançado das 4 opções, o EXPLICIT especifica a forma que terá a arvore XML, assim você consegue manipular e ter mais controle em qualquer nó que será criado, no nosso caso por exemplo, o Sobrenome pode ser um nó filho de Nome sem precisarmos manipular tabelas conforme fiz em exemplos anteriores.
Porém o EXPLICIT tem algumas obrigações:
- “A primeira coluna deve fornecer o número da marca, o tipo de inteiro, do elemento atual, e o nome da coluna deve ser TAG.”
- “A segunda coluna deve fornecer um número de marca do elemento pai e esse nome de coluna deve ser PARENT. Assim criando uma hierarquia.”
Também precisamos especificar os nomes das colunas em um formato geral:
Nome do Elemento ! Numero da TAG ! Nome do Atributo ! Diretiva
Onde:
Nome do Elemento: Identificador da Tag, exemplo Usuario = <Usuario>
Numero da TAG: Assim como Tag e o Parent, ajuda a determinar a aninhamento do XML.
Nome do Atributo: Fornece o nome do atributo a ser construído no Nome do Elemento especificado, se existir a diretiva, ai esse se torna um elemento filho.
Diretiva: É opcional e você pode usá-la para fornecer informações adicionais para construção do XML. Com o propósito de codificar valores e mapear os dados de cadeia de caracteres para XML.
Não vou me aprofundar muito no EXPLICIT, pois o mesmo tem muitas informações. Uma boa leitura está aqui para quem quiser ou precisar se aprofundar no assunto. Entretanto vamos ver alguns exemplos:
SELECT 1 AS TAG
,NULL AS PARENT
,[UsuNome] as [Usuario!1!Nome!Element]
,[UsuSobrenome] as [Usuario!1!Sobrenome!Element]
,[UsuEmail] as [Usuario!1!Email!Element]
FROM [tbUsuarios]
FOR XML EXPLICIT
Resultado:
<Usuario>
<Nome>Adriano</Nome>
<Sobrenome>Teste</Sobrenome>
<Email>adriano.galesso@teste.com.br</Email>
</ Usuario >
< Usuario >
<Nome>renata</Nome>
<Sobrenome>Teste</Sobrenome>
<Email>renata@teste.com.br</Email>
</ Usuario >
< Usuario >
<Nome>Christiano</Nome>
<Sobrenome>Teste</Sobrenome>
<Email>web@teste.com.br</Email>
</ Usuario >
< Usuario >
<Nome>suporte</Nome>
<Sobrenome>Teste</Sobrenome>
<Email>suporte@teste.com.br</Email>
</Usuario >
Nota-se que obriguei todas as TAG serem 1 e todas as PARENT serem NULL, e Order é o nome do Elemento, 1 é o número da Tag (que só temos 1 mesmo), depois temos os nomes dos atributos e por fim digo que esses atributos são Elementos ( se não tivesse especificado isso, eles viriam em linha como se fosse o RAW).
Assim até que é fácil, mas e se quisermos fazer o que eu disse acima? Colocar o Sobrenome como elemento filho de Nome?
Temos uma tabela (retirada do site do MSDN) ela mostra como trabalha o TAG e o PARENT.
Vendo assim parece simples, mas ficar manipulando essas duas colunas é bem complicado.
Então podemos usar o Campo chave para organizar meu XML:
SELECT 1 AS TAG
,NULL AS PARENT
,[UsuCodigo] * 100 AS [Usuario!1!UsuCodigo]
,NULL as [Nome!2!Nome!Element]
,NULL as [Nome!2!Sobrenome!Element]
,[UsuEmail] as [Usuario!1!Email!Element]
FROM [tbUsuarios]
UNION
SELECT 2 AS TAG
,1 AS PARENT
,[UsuCodigo] * 100 + 1
,[UsuNome]
,[UsuSobrenome]
,[UsuEmail]
FROM [tbUsuarios]
ORDER BY [Usuario!1!UsuCodigo]
FOR XML EXPLICIT
Resultado:
<Usuario UsuCodigo="21656000">
<Email>renata @teste.com.br</Email>
<Nome>
<Nome>renata</Nome>
<Sobrenome>Teste</Sobrenome>
</Nome>
</Usuario>
<Usuario UsuCodigo="21656200">
<Email>renata@teste.com.br</Email>
<Nome>
<Nome>renata</Nome>
<Sobrenome>Teste</Sobrenome>
</Nome>
</Usuario>
<Usuario UsuCodigo="21656300">
<Email>web@teste.com.br</Email>
<Nome>
<Nome>Christiano</Nome>
<Sobrenome>Teste</Sobrenome>
</Nome>
</Usuario>
Agora nota-se muita coisa =)
Bom vemos que apareceu o UsuCodigo, ele vai ajudar a ordenação dos resultados, e mostro o porquê:
Sem ele os resultados viriam ordenados do 1º SELECT e depois os do 2º SELECT:
Com ele temos como organizar
Por isso fazemos a utilização da coluna chave e nela fazemos uma determinada conta para os números próximos não se repetirem.
O outro SELECT está para trazer os resultados da TAG 2 e PARENT 1, porque queremos que o Nome seja um nó filho dentro do nó principal. Se precisássemos de outro nó, novamente teríamos que usar o SELECT.
Não termino o FOR XML EXPLICIT dizendo que é assim que funciona pois tem N maneiras e N atribuições que podemos fornecer ao mesmo. Mas garanto uma boa leitura no site da MSDN.
5 – OUTROS ARGUMENTOS DO FOR XML (AUTO, RAW e PATH apenas)
Podemos aplicar a diretiva ELEMENTS após o FOR XML, assim o XML será todo ajustado a base de elementos (os valores não serão mais atributos):
SELECT UsuariosGerais.[UsuNome] AS Nome
,UsuariosGeraisIntro.[UsuSobrenome] AS Sobrenome
,UsuariosGerais.[UsuEmail] AS Email
FROM [tbUsuarios] AS UsuariosGerais INNER JOIN
[tbUsuarios] AS UsuariosGeraisIntro
ON UsuariosGerais.UsuCodigo = UsuariosGeraisIntro.UsuCodigo
FOR XML AUTO, ELEMENTS
Resultado:
<UsuariosGerais>
<Nome>Adriano</Nome>
<Email>adriano.galesso@teste.com.br</Email>
<UsuariosGeraisIntro>
<Sobrenome>Teste</Sobrenome>
</UsuariosGeraisIntro>
</UsuariosGerais>
<UsuariosGerais>
<Nome>renata</Nome>
<Email>renata@teste.com.br</Email>
<UsuariosGeraisIntro>
<Sobrenome>barbosa</Sobrenome>
</UsuariosGeraisIntro>
</UsuariosGerais>
<UsuariosGerais>
<Nome>Christiano</Nome>
<Email>web@teste.com.br</Email>
<UsuariosGeraisIntro>
<Sobrenome>Teste</Sobrenome>
</UsuariosGeraisIntro>
</UsuariosGerais>
<UsuariosGerais>
<Nome>suporte</Nome>
<Email>suporte@teste.com.br</Email>
<UsuariosGeraisIntro>
<Sobrenome>Teste</Sobrenome>
</UsuariosGeraisIntro>
</UsuariosGerais>
Nota-se que está tudo como Tag e seus valores não estão mais como atributos, também nota-se o novo nó que se criou com a tabela “diferente”.
Outra diretiva que podemos aplicar é o ROOT. Com ela atribuímos um elemento pai ao elemento geral:
SELECT [UsuCodigo] AS '@ID'
,[UsuNome] AS 'Nome/Nome'
,[UsuSobrenome] AS 'Nome/Sobrenome'
,[UsuEmail] AS 'Email'
FROM [tbUsuarios] AS Usuarios
FOR XML PATH('Usuarios'), ROOT('Teste')
Resultado:
<Usuarios ID="216562">
<Nome>
<Nome>renata</Nome>
<Sobrenome>Teste</Sobrenome>
</Nome>
<Email>renata@teste.com.br</Email>
</Usuarios>
<Usuarios ID="216562">
<Nome>
<Nome>renata</Nome>
<Sobrenome>Teste</Sobrenome>
</Nome>
<Email>renata@teste.com.br</Email>
</Usuarios>
<Usuarios ID="216563">
<Nome>
<Nome>Christiano</Nome>
<Sobrenome>Teste</Sobrenome>
</Nome>
<Email>web@teste.com.br</Email>
</Usuarios>
<Usuarios ID="216566">
<Nome>
<Nome>suporte</Nome>
<Sobrenome>Teste</Sobrenome>
</Nome>
<Email>suporte@teste.com.br</Email>
</Usuarios>
Temos outras diretivas como o TYPE, ELEMENTXSINIL, BINARY BASE64, XMLSCHEMA... Mas essas são mais avançadas, fogem do nosso artigo principal.
Bom, agora sabemos como gerar XMLs pelo SQL e formata-los da maneira que precisamos.
Mas para onde vai esse resultado?
Você pode gravar em uma coluna de uma tabela qualquer do tipo XML no SQL, ou pode mandar pro C# fazer alguma coisa. E é isso que vamos fazer, já que é este o cenário.
Vamos imaginar o projeto criado, e lá na nossa classe de dados temos os NAMESPACES:
using System.Xml;
using System.Xml.XPath;
using System.Data.SqlClient;
Vamos ter os atributos:
#region " Atributos "
private SqlConnection oConn;
private SqlCommand oCmd;
private XmlReader xmlReader;
private XPathDocument xpathDoc;
#endregion
Um construtor:
#region " Construtor "
public MinhaClasse()
{
oConn = new SqlConnection(ConfigurationManager.AppSettings["MinhaConn"]);
oConn.Open();
oCmd = oConn.CreateCommand();
}
#endregion
E por fim, o meu método que retorna um XPathDocument:
#region " Métodos "
protected internal XPathDocument SalvarXML()
{
oCmd.CommandType = CommandType.StoredProcedure;
oCmd.CommandText = "GerarXMLUsuarios”;
try
{
xmlReader = oCmd.ExecuteXmlReader();
xpathDoc = new XPathDocument(xmlReader, XmlSpace.Preserve);
}
catch (Exception ex)
{
throw new Exception(ex.ToString());
}
finally
{
oConn.Close();
}
return xpathDoc;
}
#endregion
Nada de novo até esse método. Chamamos a procedure e damos um ExecuteXMLReader.
O XmlReader fornece uma acesso somente leitura e somente para frente de um fluxo de dados Xml
O XPathDocument fornece representação em memória de um documento, também somente leitura e somente para frente de um Xml.
Sabendo isso, temos um acesso ao XML gerado pela base, e uma representação em memória deste XML, preservando espaços.
Agora na nossa classe de negócios:
Temos os NAMESPACES:
using System.Xml.XPath;
using System.Xml;
Temos atributos:
private string diretorio = ConfigurationManager.AppSettings["Diretorio"];
private string URL = ConfigurationManager.AppSettings["URL"];
E temos um método que gera e salva um XML:
public string GerarXML()
{
string arquivo = "teste.xml";
string dirArq = diretorio + arquivo;
TesteDAO oTesteDao = new TesteDAO ();
XPathDocument oXPdoc = oTesteDao.ListarXML(tipoXML);
XPathNavigator oXPNav = oXPdoc.CreateNavigator();
XmlTextWriter Xwriter = new XmlTextWriter(dirArq, System.Text.Encoding.UTF8);
Xwriter.Formatting = Formatting.Indented;
Xwriter.Indentation = 3;
Xwriter.WriteStartDocument();
Xwriter.WriteNode(oXPNav, true);
Xwriter.WriteEndDocument();
Xwriter.Close();
return URL + arquivo;
}
Nada de novo até esse método. Criamos um XPathDocument, que recebe o retorno do meu método na classe de acesso a dados vista acima.
Criamos um XPathNavigator que é uma classe abstrata, onde temos um modelo de cursor para navegar (como o próprio nome diz) em nosso XML.
Como não temos nada para editar no XML então vamos salvar o mesmo com o XmlTextWriter, onde passamos o caminho que queremos salvar e a codificação (no construtor). Depois podemos fornecer o tipo da formatação, indentação, começo e documento XML, nós (que nesse caso é o nosso XML inteiro), o fim do documento XML e fechamos o arquivo. Vamos ser bonzinhos e vamos retornar o caminho do XML em formato Web.
Pronto!
Fiz este artigo baseado em um problema real, o problema era bem parecido com o colocado aqui, mas não era 1 XML e sim 4, claro, não tem tantos usuários quanto o que eu falei, mas é um calculo baseado em possibilidades.
Esses 4 XMLs, atualmente com 25 MB somados, são gerados diariamente.
A 1º tentativa foi usando um framework e escrevendo os XML pelo C#, o servidor travava graças aos inúmeros acessos ao banco que o componente (framework) fazia.
A 2º tentativa foi criar procedures especificas que já traziam o resultado perfeito e no C# jogadas em objetos específicos também, os objetos eram listados e assim os XMLs eram escritos, não tinha estress, mas ainda demorava cerca de 30 minutos para gerar por completo.
A 3º tentativa esta da forma que eu falei neste artigo, uso o FOR XML PATH, trago pro C# e escrevo o arquivo.
Por isso este artigo está aqui. Gero 25 MB de arquivos XML em 28 SEGUNDOS... Um lixo mesmo! hahahahahha
Até a próxima.