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 StringWriter. 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