Construindo Auditoria de objetos com XMLSerializer

Quando pensamos em trabalhar com auditoria de dados e/ou informações, logo pensamos em conversar com um DBA do banco de dados, construir várias triggers para logar as alterações realizadas nas tabelas mais importantes do sistema - essa é a alteranativa que temos utilizando o banco de dados. Mas o que temos de bom com isso? Bom, quando trabalhamos com rotinas, Jobs, Triggers e outras tarefas sendo executadas pelo SGBD deixamos nossa aplicação mais leve e tiramos a responsabilidade e logar a auditoria das alterações pela aplicação. Mas o que isso nos trás de ruim? Amarração ao banco de dados!


Por outro lado, podemos trabalhar com essa responsabilidade por parte do sistema que está sendo desenvolvido. Como? Normalmente, temos tabelas de auditoria para cada tabela que será auditada pelo sistema, por exemplo: Cliente - ClienteAuditoria, Produto - ProdutoAuditoria. O que isso traz de ruim para nossa aplicação? Para a aplicação nada, mas para o banco de dados, replicação de tabelas que não precisariam ter sido replicadas. Mas, é uma solução!


E quando temos uma arquitetura em camadas, com classes de domínio, e que tem vários links com outras entidades de domínio? Como podemos fazer a auditoria dos nossos objetos? replicando a tabela correspondente ao objeto? amarrando nosso sistema ao banco de dados, trabalhando com Jobs, Stored Procedures ou Triggers? A resposta é: Pode ser desta forma, mas não é a melhor escolha! Em se tratando de aplicações com arquitetura OO separada em camadas, podemos trabalhar, também com uma arquitetura de Auditoria, utilizando os nossos objetos de domínio.


O Framework .NET possui vários recursos para trabalharmos com I/O, um deles é a classe XmlSerializer. Por meio dessa classe, podemos fazer a serialização de objetos em formato XML. Mas o que é uma serialização? Serialização, é o processo de transformar uma informação binária em um fluxo de dados o qual podemos utilizar para persistir essas informações em qualquer tipo de repositório, por exemplo. Esse fluxo de informações, nada mais é do que toda a estrutuda do objeto em memória em uma estrutura a qual podemos utilizar, como uma string XML; é o que iremos utilizar no artigo de hoje.


Antes de começarmos a codificar nosso exemplo, preciso explicar alguns detalhes que serão necessários para implementarmos o demo desse artigo. Para que possamos serializar objetos, para um formato XML, precisamos configurar nossas classes com as seguintes classes de atributos: Serializable, XmlRootAttribute, e XmlInclude. Esses atributos serão explicados ao longo da implementação do demo. Então, para começarmos, vamos criar uma aplicação Windows Forms com o nome: wfappAuditoria. Coloque um botão em seu formulário, ao final do artigo iremos implementar o botão!


Com a nossa aplicação criada, temos que criar 2 pastas no nosso projeto, com os seguintes nomes: Auditoria e Entidade.Dominio. Dentro da pasta Entidade.Dominio, vamos criar as seguintes classes, como mostrarão os códigos abaixo:


using System;using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using System.IO;
namespace wfappAuditoria.Entidade.Dominio
{
public class ValueObject
{
public Int32 Id { get; set; }
public String SerializarParaXml()
{
XmlSerializer xmlSerializer = null;
StringWriter sw = null;

            try
{
sw = new StringWriter();

xmlSerializer = new XmlSerializer(this.GetType());
xmlSerializer.Serialize(sw, this);
}
catch (Exception ex)
{
throw ex;
}
finally
{
sw.Close();
}

return sw.ToString();
}
}
}
Como você pode analisar no código acima, estamos utilizar a classe XmlSerializer e StringWritero. O XmlSerializer será a classe responsável por serializar, ou seja, por transformar a estrutura do nosso objeto em memória, num fluxo que possamos utilizar para persistir, por exemplo. E quem será o nosso fluxo? StringWriter - Com essa classe, podemos armazenar toda a estrutuda do objeto em memória, numa string XML e retornar esse XML com todas as informações do Objeto serializado.

Abaixo será apresentada a estrutura das classes e existe um pequeno detalhe, no que diz respeito à herança e ao XmlInclude, que será explicado abaixo.

Classe Pessoa:
using System;using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
namespace wfappAuditoria.Entidade.Dominio
{
[Serializable]
[XmlRoot("Pessoa")]
[XmlInclude(typeof(Pai)), XmlInclude(typeof(Filho))]
public class Pessoa : ValueObject
{
public DateTime DataNascimento { get; set; }
public String Nome { get; set; }
}
}
Classe Pai
using System;using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
namespace wfappAuditoria.Entidade.Dominio
{
[Serializable]
[XmlRoot("Pai")]
public class Pai : Pessoa
{
public Decimal Salario { get; set; }

private List<Pessoa> _filhos = null;
public List<Pessoa> Filhos
{
get
{
return _filhos;
}

set
{
_filhos = new List<Pessoa>();
}
}
}
}
Classe Filho
using System;using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
namespace wfappAuditoria.Entidade.Dominio
{
[Serializable]
[XmlRoot("Professor")]
public class Filho : Pessoa
{
public String Formacao { get; set; }
public Decimal Salario { get; set; }

public Pessoa Pai { get; set; }
}
}

Se você observou a classe Pessoa, temos uma replicação do atributo XmlInclude. Mas porque temos que fazer isso? Como estamos trabalhando com herança de classes, o XmlSerializer não sabe trabalhar com polimorfismo, então essa é a maneira de informarmos para a estrutura e engine do XmlSerializer, quais serão as possiveis classes que a classe pessoa pode assumir como fator polimórfico, que no nosso caso será: Pai e Filho. Contudo, se faz ter a configuração das classes filhas: Pai e Filho, informando que estas, também, precisarão ser serializáveis, por meio do atributo: Serializable.

Bom, essa foi a nossa estrutura de classes de domínio. Agora, vamos mostrar o código das classes responsáveis por disponibilizar o método de Auditoria. Segue o código abaixo:


using System;using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using wfappAuditoria.Entidade.Dominio;
namespace wfappAuditoria.Auditoria
{
public class Auditoria
{
public String IP { get; set; }
public DateTime DataAuditoria { get; set; }
public String Acao { get; set; }
public String ObjetoXML { get; set; }
}
}
using System;using System.Collections.Generic;
using System.Linq;
using System.Text;
using wfappAuditoria.Entidade.Dominio;
using System.Net;
namespace wfappAuditoria.Auditoria
{
public class BLLAuditoria
{
public void GerarAuditoria(ValueObject entidadeDominio, String acao)
{
Auditoria auditoria = null;

try
{
auditoria = new Auditoria();
auditoria.IP = this.ObtertIP();
auditoria.Acao = acao;
auditoria.DataAuditoria = DateTime.Now.Date;
auditoria.ObjetoXML = entidadeDominio.SerializarParaXml();
                // Aqui, você pode persistir o seu objeto de auditoria numa Tabela única de Auditoria. Guardando apenas o XML do Objeto Serializado.            }
catch (Exception ex)
{
throw ex;
}
}

private string ObtertIP()
{
try
{
string strHostName = "";
strHostName = System.Net.Dns.GetHostName();
IPHostEntry ipEntry = System.Net.Dns.GetHostEntry(strHostName);
IPAddress[] addr = ipEntry.AddressList;
return addr[addr.Length - 1].ToString();
}
catch (Exception ex)
{
throw ex;
}
}
}
}
Abaixo segue a implementação do nosso botão, para realizar a serialização:
        private void btnGerarAuditoria_Click(object sender, EventArgs e)        {
Filho filho = null;
Pai pai = null;
BLLAuditoria auditoria = null;

try
{
auditoria = new BLLAuditoria();

pai = new Pai();
pai.Id = 1;
pai.Nome = "Primeiro Pai";
pai.Filhos = new List<Pessoa>();
pai.Salario = 15000;

filho = new Filho();
filho.Id = 1;
filho.Nome = "Primeiro Filho";
filho.DataNascimento = new DateTime(1981, 11, 26);
pai.Filhos.Add(filho);

filho = new Filho();
filho.Id = 2;
filho.Nome = "Segundo Filho";
filho.DataNascimento = new DateTime(1981, 07, 15);
pai.Filhos.Add(filho);

auditoria.GerarAuditoria(pai, "Inclusão do Pai");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}

O trabalho de serializar um objeto da memória é bastante simples, quando sabemos que existe, dentro do Framework .NET uma classe que faz todo esse trabalho pesado para nós: XmlSerializer. Contudo, existe um conhecimento necessários para que nossas classes de domínio possam ser configuradas, possibilitando a engine de serialização, reconhecer os domínios como sendo serializáveis, atráves das classes de atributos: Serializable, XmlRoot. Quando estamos trabalhando com herança, existe um outro atributo que É OBRIGATÓRIO ser configurado na classe Base, que é o XmlInclude. Desta forma, você tem total condições de montar toda uma estrutura dentro de sua arquitetura para que seja possível, guardar um snapshot do objeto, e posteriormente, possamos ler esses valores XML, para termos um filme da vida daquele determinado objeto.


Para aprofundar-se mais no assunto, acesse: XML Serialization in the .NET Framework

Para pronfundar-se nesse tema, acesse o grupo: C#

Exibições: 373

Comentar

Você precisa ser um membro de DevBrasil para adicionar comentários!

Entrar em DevBrasil

Comentário de Aislan Miranda em 1 maio 2012 às 21:24

Pessoal, estou tentando resolver um problema de forma mais simples.
Eu pego os dados pelo DataSet e crio o xml.

SqlConnection Conn = new SqlConnection(Conexao.StringDeConexao);

SqlCommand cmd = new SqlCommand("SELECT_NO_BANCO", Conn);
SqlDataAdapter da = new SqlDataAdapter(cmd);
DataSet ds = new DataSet();

ds.DataSetName = "chart"; //este vai ser o root
da.Fill(ds, "categories");

//Server.MapPath - serve para pegar o caminho completo no sistema.
//Server.MapPath("~/contatos.xml") = c:\inetpub\wwwroot\site\contatos.xml
//Destino do arquivo criado = c:\inetpub\wwwroot\site\contatos.xml
string sCaminhodoArquivo = Server.MapPath("~/testeGrafico/condominios.xml");

//Criando o arquivo XML
StreamWriter xmlDoc = new StreamWriter(sCaminhodoArquivo);
//Escrevendo no documento
ds.WriteXml(xmlDoc);

É exibido seguinte resultado.

<chart>
<categories>
       <NOME>ATLANTICA VILLE</COND>

       <VALOR>100</VALOR>
</categories>
<categories>
       <NOME_COND>PRAIA DE CAMBURI</NOME_COND>
       <VALOR>100</VALOR>
</categories>
</chart>

 

meu objetivo é gerar assim: agrupar as informações em tags separadas.

<chart>
<nomes>
       <NOME>ATLANTICA VILLE</NOME>

       <NOME>PRAIA DE CAMBURI</NOME>

</nomes>
<valores>
       <VALOR>100</VALOR>
       <VALOR>100</VALOR>
</valores>
</chart>


NÃO ESTOU CONSEGUINDO FORMATAR DESTA FORMA, ALGUÉM SABE COMO POSSO FAZER ISSO? DE QUALQUER FORMA, IREI LER MELHOR SOBRE O ASSUNTO DESTA DISCUSSÃO.

Abraços!

 

Comentário de Guilherme Gomes do Braz em 17 novembro 2010 às 20:52
Ajudou sim José Roberto, obrigado pelo retorno e abraços.
Comentário de José Roberto Araújo em 15 novembro 2010 às 23:35
Guilherme, não existe a melhor forma de persistência no banco de dados! O que temos hoje, são vários frameworks de ORM (Object Relational Mapping). Agora qual deles é o melhor? Também não existe O Melhor, existe o que melhor lhe convier e o que mais você adaptar-se a ele. Por exemplo: Tem pessoas que usaram por muito tempo o NHibernate, e hoje estam utilizando o Entity Framework e vice-versa.

A Camada de persistência com o banco de dados, pode ser utilizada em conjunto com qualquer framework de ORM, que temos no mercado; basta que você estude e escolha o seu.

Espero que tenha te ajudado.
Qualquer dúvida, pode perguntar!
Comentário de Guilherme Gomes do Braz em 15 novembro 2010 às 11:07
Excelente Artigo, vc pode exemplificar como seria a melhor forma de persistência no banco?
Abraços e continue postando.

© 2018   Criado por Ramon Durães.   Ativado por

Badges  |  Relatar um incidente  |  Termos de serviço