Paulo Roberto's profileSomos a última geraçãoPhotosBlogListsMore ![]() | Help |
|
October 22 Auditoria de dados, usando NHibernateO NHibernate possui alguns eventos padrão que nos permitem fazer alguma coisa quando um objetos é incluído, excluido ou carregado do banco de dados. São os chamados EventListeners, realmente não tem muita documentação sobre o assunto, então, fazer qualquer coisa sobre isso, demanda uma boa pesquisa. Comecei aqui a implementar algo, então, vou poupar vocês de algum trabalho. No meu caso, eu preciso registrar as mudanças de informações que uma determinada classe sofreu, e quem realizou a mudança. Isso deve ficar armazenado no banco de dados. Logo, defini a seguinte classe: 1: public class Historico 2: {3: private Int64 _id; 4: private string _classe; 5: private string _alteracoes; 6: 7: public virtual Int64 Id 8: {9: get { return _id; } 10: set{ _id = value;} 11: } 12: 13: public virtual string Classe 14: {15: get{ return _classe;} 16: set{ _classe = value;} 17: 18: } 19: 20: public virtual string Alteracoes 21: {22: get { return _alteracoes; } 23: set{ _alteracoes = value;} 24: } 25: 26: public Historico() 27: { 28: Id = 0;29: Classe = string.Empty; 30: Alteracoes = string.Empty; 31: } 32: 33: public Int64 IdObjeto { get; set; } 34: public string Acao { get; set; } 35: public DateTime Data { get; set; } 36: public string Usuario { get; set; } 37: }
Nada complicado. Definimos aí uma propriedade Classe que irá armazenar o nome da classe que sofre alteração, uma propriedade Historico, que irá conter as alterações feitas, IdObjeto que guarda o Id do objeto que sofre alteração; Acao, que guarda o tipo de acontecido, Data que representa quando foi alterado e Usuario, que guarda o nome do usuario que fez a alteração. Como essa classe deve ser persistida, é preciso também fazer seu mapeamento. Não vou entrar em detalhes como fazer isso… Agora chegou a hora de implementarmos os eventos. Para registrar o que foi alterado, é preciso implementar a interface IPostUpdateEventListener. Ela disponibiliza o método OnPostUpdate. Esse evento é disparado depois que o objeto foi atualizado. A seguir temos a classe de auditoria. 1: public class LogAlteracao : IPostUpdateEventListener 2: {3: private const string _noValueString = " "; 4: 5: private static string getStringValueFromStateArray(object[] stateArray, int position) 6: {7: var value = stateArray[position]; 8: 9: return value == null || value.ToString() == string.Empty 10: ? _noValueString11: : value.ToString(); 12: } 13: 14: public void OnPostUpdate(PostUpdateEvent @event) 15: {16: if (@event.Entity is Historico) 17: {18: return; 19: } 20: 21: var entityFullName = @event.Entity.GetType().FullName; 22: 23: if (@event.OldState == null) 24: {25: var session = @event.Session.GetSession(EntityMode.Poco); 26: var sb = new StringBuilder(); 27: 28: for (int i = 0; i <= @event.State.Count() - 1; i++) 29: {30: var newValue = getStringValueFromStateArray(@event.State, i); 31: sb.AppendLine("Propriedadade: " + @event.Persister.PropertyNames[i]); 32: sb.AppendLine(" Novo valor..: " + newValue); 33: sb.AppendLine("-------------------------------"); 34: } 35: 36: var historico = new Historico(); 37: historico.Classe = @event.Entity.GetType().Name; 38: historico.IdObjeto = (Int64)@event.Id; 39: historico.Acao = "Atualização"; 40: historico.Alteracoes = sb.ToString(); 41: historico.Data = DateTime.Now; 42: session.Save(historico); 43: }44: else 45: {46: var dirtyFieldIndexes = @event.Persister.FindDirty(@event.State, @event.OldState, @event.Entity, 47: @event.Session); 48: 49: var session = @event.Session.GetSession(EntityMode.Poco); 50: var sb = new StringBuilder(); 51: 52: foreach (var dirtyFieldIndex in dirtyFieldIndexes) 53: { 54: 55: //Aqui tem que testar se for component. 56: //Se for, tem que pegar as propriedades manualmente... por reflection 57: if (@event.Persister.PropertyTypes[dirtyFieldIndex] is ComponentType) 58: {59: // Recupera o estado atual e o anterior da propriedade. 60: var obj = @event.State[dirtyFieldIndex]; 61: var objAnterior = @event.OldState[dirtyFieldIndex]; 62: 63: var propertyInfos = obj.GetType().GetProperties();64: foreach (var info in propertyInfos) 65: {66: // Se a propriedade não pode ser lida, não faz nada. 67: if (!info.CanRead) 68: continue; 69: 70: try 71: {72: // Recupera os valor novo e o antigo da propriedade. 73: var novoValor = info.GetValue(obj, null); 74: var valorAntigo = info.GetValue(objAnterior, null); 75: 76: // Verifica se são mesmo diferentes. 77: if (valorAntigo.ToString().Equals(novoValor.ToString())) 78: continue; 79: 80: sb.AppendLine("Propriedadade: " + info.Name); 81: sb.AppendLine(" Valor antigo: " + valorAntigo); 82: sb.AppendLine(" Novo valor: " + novoValor); 83: sb.AppendLine("-------------------------------"); 84: }85: catch 86: {87: continue; 88: } 89: } 90: }91: else 92: {93: var oldValue = getStringValueFromStateArray(@event.OldState, dirtyFieldIndex); 94: var newValue = getStringValueFromStateArray(@event.State, dirtyFieldIndex); 95: 96: if (oldValue == newValue) 97: {98: continue; 99: } 100: 101: sb.AppendLine("Propriedadade: " + @event.Persister.PropertyNames[dirtyFieldIndex]); 102: sb.AppendLine(" Valor antigo: " + oldValue); 103: sb.AppendLine(" Novo valor..: " + newValue); 104: sb.AppendLine("-------------------------------"); 105: } 106: } 107: 108: var historico = new Historico(); 109: historico.Classe = @event.Entity.GetType().Name; 110: historico.IdObjeto = (Int64) @event.Id; 111: historico.Acao = "Atualização"; 112: historico.Alteracoes = sb.ToString(); 113: historico.Data = DateTime.Now; 114: session.Save(historico); 115: session.Flush(); 116: } 117: } 118: }
Vamos dar uma entendida no código…. Todas informações que vamos precisar estão acessíveis no parêmetro PostUpdateEvent event. A primeira coisa que verificamos é se, o objeto que está vindo é Historico. Se for, não fazemos nada… senão ia ficar em loop, certo? Na sequência verificamos se o objeto possui um OldState. Essa propriedade representa o estado do objeto antes da alteração e só é preenchida se o objeto tiver sido carregado pelo Session que está em uso. Caso o objeto não tenha OldState, percorremos a propriedade State, que contém os valores atuais, e montamos com esses valores uma string para ser salva. Então instanciamos um objeto Historico e salvamos o objeto. Agora, se o objeto tem um OldState, podemos gerar histórico só do que foi alterado. Veja a linha 46. O NHibernate disponibiliza o método FindDirty, que retorna os índices das propriedades que sofreram alterações. Aí é só montar novamente o texto e salvar… Quero apontar um detalhe lá na linha 57. Estamos verificando se a propriedade que vem é do tipo ComponentType. Quando no mapeamento de uma classe, mapeamos outra nela como Component, ela aparece nesse evento PostUpdate como uma única propriedade. Assim, é necessário usarmos reflection pra extrair as propriedades….. Talvez precise melhorar algo aí nessa implementação, mas por enquanto… está funcionando :). Depois posto como registrar as exclusões.
TrackbacksThe trackback URL for this entry is: http://pauloquicoli.spaces.live.com/blog/cns!B27CCFAA07B93BE8!10055.trak Weblogs that reference this entry
|
|
|