Paulo Roberto 的个人资料Somos a última geração照片日志列表更多 工具 帮助

日志


10月22日

Auditoria de dados, usando NHibernate

O 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:                      ? _noValueString
  11:                      : 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.

 

 

10月5日

Configurando o NHibernate por código

Olá pessoal. Aqui temos a necessidade de flexibilizar a string de conexão do sistema, de tal forma que o usuário possa mudá-la a partir  de uma tela de configurações. Outro detalhe que prevemos, é a mudança de banco de dados sem a necessidade de recompilar o projeto. Levando tudo isso em consideração, mais a possibilidade de adicionar algum parâmetro de execução e habilitar ou não a exibição de SQL ou geração de estatísticas, resolver configurar o NHibernate por código. Contudo o desafio em fazer isso estava no fato de que utilizamos o projeto uNHaddins.

Até o momento estávamos utilizando a classe DefaultSessionFactoryConfigurationProvider(),  responsável por carregar e configurar o NHibernate. A solução foi criar uma classe herdada de AbstractConfigurationProvider. Essa classe abstrata expoem os métodos necessários que devem ser implementados para que possam ter uma inicialização personalizada do NHibernate. Sendo assim temos a seguinte classe:

/// <summary>
    /// Esta classe configura manualmente, por código, as propriedades do NHibernate.
    /// Estamos fazendo por código para facilitar a troca de connection string e até mesmo
    /// a troca de banco de dados.
    /// </summary>
    public class ByCodeConfigurationProvider: AbstractConfigurationProvider
    {
        private readonly string _connectionString;
        private Configuration _configuration;

        /// <summary>
        /// Construtor da classe. Recebe como parâmetro <paramref name="connectionString"/>
        /// </summary>
        /// <param name="connectionString">String de conexão do banco de dados em uso</param>
        public ByCodeConfigurationProvider(string connectionString)
        {
            _connectionString = connectionString;
        }

        private void ConfigureByCode()
        {
            if (_configuration == null)
            {
                _configuration =  new Configuration();
                _configuration.SetProperty("connection.driver_class","NHibernate.Driver.SqlClientDriver");
                _configuration.SetProperty("hbm2ddl.auto","update");
                _configuration.SetProperty("show_sql","true");
                _configuration.SetProperty("connection.connection_string",@"Data Source=SRV2003NET\SQLEXPRESS;Initial Catalog=NotasBD;Persist Security Info=True;User ID=NotasMaster;Password=cont10rol");
                _configuration.SetProperty("adonet.batch_size","10");
                _configuration.SetProperty("dialect","NHibernate.Dialect.MsSql2005Dialect");
                _configuration.SetProperty("use_outer_join","true");
                _configuration.SetProperty("command_timeout","10");
                _configuration.SetProperty("query.substitutions","true 1, false 0, yes 'Y', no 'N'");
                _configuration.SetProperty("current_session_context_class","uNhAddIns.SessionEasier.Conversations.ThreadLocalConversationalSessionContext, uNhAddIns");
                _configuration.SetProperty("proxyfactory.factory_class","NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle");
                _configuration.SetProperty("use_proxy_validator","false");
                _configuration.AddAssembly(Assembly.GetAssembly(typeof (Pessoa)));
                
                var eventos =   _configuration.EventListeners;
                eventos.PreUpdateEventListeners = new List<IPreUpdateEventListener>(eventos.PreUpdateEventListeners){new EventListener()}.ToArray();
                eventos.PreInsertEventListeners = new List<IPreInsertEventListener>(eventos.PreInsertEventListeners){new EventListener()}.ToArray();
            }

        }


        public override IEnumerable<Configuration> Configure()
        {
            ConfigureByCode();
            return new SingletonEnumerable<NHibernate.Cfg.Configuration>(_configuration);
        }
    }

Como pode ser visto, o método ConfigureByCode instancia um objeto Configuration do NHibernate e utilizando o método SetProperty, configura suas propriedades. O construtor da classe recebe como parâmetro uma string que contém a connection string do banco, porém não estamos utilizando nesse momento. Mas o detalhe da flexibilização é que  como estamos fazendo tudo por código, é muito fácil ler algum arquivo de configuração e mudar essas propriedades como desejarmos, sem termos que recompilar código.

Aqui centralizamos a carga de nossos serviços em uma classe tipo ServiceLocator, e nela executamos a carga do NHibernate assim:

 

public class ServiceLocatorProvider
   {
       public static void Initialize()
       {
           var container = new WindsorContainer();
           container.AddFacility<PersistenceConversationFacility>();

           var nhConfigurator = new ByCodeConfigurationProvider("");

           var sfp = new SessionFactoryProvider(nhConfigurator);

           container.Register(Component.For<ISessionFactoryProvider>().Instance(sfp));
           container.Register(Component.For<ISessionWrapper>().ImplementedBy<SessionWrapper>());
           container.Register(Component.For<IConversationFactory>().ImplementedBy<DefaultConversationFactory>());
           container.Register(Component.For<IConversationsContainerAccessor>().ImplementedBy<NhConversationsContainerAccessor>());
           container.Register(Component.For<ISessionFactory>().Instance(sfp.GetFactory(null)));

           container.Register(Component.For(typeof(IDao<>)).ImplementedBy(typeof(BaseDao<>)).LifeStyle.Transient);
     var sl = new WindsorServiceLocator(container);
     container.Register(Component.For<IServiceLocator>().Instance(sl));
     ServiceLocator.SetLocatorProvider(() => sl);
  }
}

 

Segue aí a dica!

4月23日

Imperdível!!! Ayende palestrando sobre ActiveRecord

 

Vale, e muito, assistir!

3月26日

Relacionamentos MxN com NHibernate

Todos sabemos que manter um relacionamento MxN é chato. E claro que ao utilizar uma ferramenta ORM podemos minimizar essa dor de cabeça.  Imaginem que em seu banco de dados exista a seguinte situação:

 

digrama-sql

Nosso modelo de objetos está da seguinte forma:

ClassDiagram1

Observe que em nenhum momento temos alguma classe que representa a tabela associativa TURMA_X_ALUNO. É no mapeamento das classes que vamos indicar a existência dessa tabela e informar ao NHibernate que a coleção Turmas e Alunos devem utilizá-la. Vamos ver primeiramento o mapeamento da classe Aluno.

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Dominio"  namespace="Dominio" >
  <class name="Aluno" table="ALUNO" >
    <id name="Id" column="ID_ALUNO" type="Int32" unsaved-value="0">
        <generator class="hilo"/>
    </id>
    <property name="Nome" column="NOME" type="string" length="100" not-null="true" />
    <idbag name="Turmas" table="TURMA_X_ALUNO" generic="true" lazy="true" cascade="save-update" inverse="true">
      <collection-id type="Int32" column="ID_TURMA_X_ALUNO">
        <generator class ="hilo"/>
      </collection-id>
      <key column="ID_ALUNO" />
      <many-to-many class="Turma" column="ID_TURMA"/>
    </idbag>

  </class>
</hibernate-mapping>

Destaquei em azul a parte que nos interessa. Veja que utilizamos uma idbag para representar a coleção de  Turmas. Ela foi utilizada porque nossa tabela associativa possui uma chave primária própria, que está mapeada na tag colection-id. Em vermelho estão destacadas coisas importantes também. Definimos que a tabela que mantém a coleção de Turmas é nossa tabela associativa e informamos ao NHibernate que não é por aqui que vamos realizar as associações. Isso quer dizer que não vamos adicionar turmas aos alunos mas sim, alunos às turmas existentes. Então se eu fizer algo assim:

Aluno.Turmas.Add(novaTurma);

Essa nova associação não será salva, porque a marcamos com inverse=true. Depois dizemos que essa mesma associação é many-to-many, informando também de qual classe. Dessa forma, automaticamente o NHibernate já sabe como preencher a lista Turmas.   Quanto ao mapeamento de Turma, é semelhante, veja.

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Dominio"  namespace="Dominio" >
  <class name="Turma" table="TURMA" >
    <id name="Id" column="ID_TURMA" type="Int32" unsaved-value="0">
      <generator class="hilo"/>
    </id>
    <property name="Descricao" column="DESCRICAO" type="string" length="100" not-null="true" />

    <idbag name="Alunos" table="TURMA_X_ALUNO" generic="true" lazy="true" cascade="save-update">
      <collection-id type="Int32" column="ID_TURMA_X_ALUNO">
        <generator class ="hilo"/>
      </collection-id>

      <key column="ID_TURMA" />
      <many-to-many class="Aluno" column="ID_ALUNO"/>
    </idbag>
  </class>
</hibernate-mapping>

A diferença é que não especificamos aqui a tag inverse=true  é porque é por aqui que vamos realizar as associações. Veja um teste feito:

            Configuration cfg = new Configuration();
            cfg.Configure();
            cfg.AddAssembly(typeof(Aluno).Assembly);
            ISessionFactory factory = cfg.BuildSessionFactory();
            ISession session = factory.OpenSession();
            ITransaction transaction = session.BeginTransaction();

            Aluno objAluno1 = new Aluno();
            objAluno1.Nome = "Paulo Quicoli";

            Aluno objAluno2 = new Aluno();
            objAluno2.Nome = "Sem Nome";

            session.Save(objAluno1);
            session.Save(objAluno2);

            Turma objTurma = new Turma();
            objTurma.Descricao = "Teste de MxN";

            objTurma.Alunos.Add(objAluno1);
            objTurma.Alunos.Add(objAluno2);

            session.Save(objTurma);
            try
            {
                transaction.Commit();
                session.Flush();
            }
            catch
            {
                transaction.Rollback();
            }
            finally
            {
                session.Close();
                Console.ReadLine();
            }

É muito simples manter esse tipo de relacionamento com o NHibernate, o detalhe como sempre, é acertar no mapeamento.   Observe também que não estou usando mais ids do tipo identity, após recomendação do meu último post…

Abraço a todos…

3月20日

Evite o uso do recurso Identity do SQLServer quando utilizar NHibernate

Hoje Tuna Toksoz fez um post interessante no site do NHibernate. Ele explica porque evitar o uso do Identity. Vou resumir o post aqui.

Basicamente quando se usa o Identiy, o recurso de batching do NHibernate fica impossibilitado de ser aplicado. Ele mostra isso utilizando os seguintes códigos:

   1:  [Test]
   2:  public void Should_not_insert_entity_in_a_transaction_HiLo()
   3:  {
   4:      var post = new PostWithHiLo {Title = "Identity Generators Revealed"};
   5:      var postComment = new PostCommentWithHiLo { Post = post, Comment = "Comment" };
   6:      using (ISession session = factory.OpenSession())
   7:      using (var tran = session.BeginTransaction())
   8:      {
   9:          session.Save(post); //No commit here
  10:          session.Save(postComment);
  11:          long insertCount = factory.Statistics.EntityInsertCount;
  12:          Assert.That(insertCount, Is.EqualTo(0), "Shouldn't insert entity in a transaction before commit.");
  13:      }
  14:  }
  15:   
  16:  [Test]
  17:  public void Should_not_insert_entity_in_a_transaction_Identity()
  18:  {
  19:      var post = new PostWithIdentity {Title = "Identity Generators Revealed"};
  20:      var postComment = new PostCommentWithIdentity {Post = post, Comment = "Comment"};
  21:      using (ISession session = factory.OpenSession())
  22:      using (var tran = session.BeginTransaction())
  23:      {
  24:          session.Save(post);
  25:          session.Save(postComment);
  26:          long insertCount = factory.Statistics.EntityInsertCount;
  27:          Assert.That(insertCount, Is.EqualTo(0), "Shouldn't insert entity in a transaction before commit.");
  28:      }
  29:  }

No primeiro teste, que não utiliza Identity, nada é enviado para o banco. Já no segundo teremos 2 comandos INSERT enviados ao banco.  Isso significa que podemos enfrentar problemas de performance, já que o banco será acessado várias vezes.

Pra finalizar ele mostra um último exemplo:

   1:  using (ISession session = factory.OpenSession())
   2:  using (var tran = session.BeginTransaction())
   3:  {
   4:      for (int i = 0; i < 3; i++)
   5:      {
   6:          var post = new PostWithHiLo {Title = string.Format("Identity Generators Revealed {0}", i)};
   7:          session.Save(post);
   8:      }
   9:      tran.Commit();
  10:  }

Neste caso os três comandos INSERT que são gerados, só são enviados após o commit e de uma vez só ao banco. Se estivéssemos utilizando Identity, a cada Save teríamos um INSERT sendo enviado.

Para ver o post completo, acesse o link: http://nhforge.org/blogs/nhibernate/archive/2009/03/20/nhibernate-poid-generators-revealed.aspx

Até mais!

11月11日

Palestra sobre NHibernate

Nesta semana está acontecendo na UNAERP, em Ribeirão Preto/SP, a 12ª semana da tecnologia. Ontem pude realizar uma palestra sobre NHibernate. Quero agradecer a todos que compareceram e à comissão organizadora.

Infelizmente o tempo não foi suficiente pra mostrar tudo o que gostaria, de qualquer forma, estou aqui para retirar qualquer dúvida.

obrigado!

10月21日

WPF + Model-View-ViewModel + NHibernate

Estou liberando aqui um pequeno exemplo que mostra como utilizar o NHibernate em um aplicação WPF, empregando o padrão M-V-VM. Além disso utilizo também uma implementação do padrão Repository e Unit of Work, ambos descritos pelo NHibernate FAQ. Este demo utiliza a versão Embedded do Firebird 2.1 e para rodar é necessário apenas um pequeno ajuste no arquivo aliases.conf, localizado na pasta debug da aplicação. O ajuste a ser feito é apenas informar o path correto da localização do banco de dados. Qualquer dúvida é só deixar um recado aqui.

PS.: Estou com o tempo curto, por isso não fiz um post explicando como tudo foi feito….

6月30日

NHibernate 2.0 Beta 1

Acabei de ver no site do Ayende este anúncio !

 

 

 

 

 

 

 

 

 

 

Baixem  aqui

3月31日

NHibernate 2.0 ALPHA 1

É isso mesmo, depois de um tempo sem novidades saiu a nova versão. Mesmo sendo ALPHA existem várias pessoas utilizando, segundo o Ayende. Vou listar aqui algumas novidades...

  • VetoInterceptor - Possibilidade de cancelar operações de Delete, Update de Insert, utilizando a interface IInterceptor
  • Agora pode-se utilizar constantes no select de um HQL
  • Adicionado eventos OnPreLoad e OnPostLoad

E vários itens do Hibernate foram portados, entre eles:

  • Actions, Eventos and Listeners
  • StatelessSession
  • CacheMode
  • Statistics

E alguns subprojetos importantes foram adicionados

  • NHibernate.Validator
  • NHibernate.Shards
  • Atualização do NHibernate.Search para ficar como no Hibernate Search 3.0

Faça o download agora mesmo em: http://sourceforge.net/project/showfiles.php?group_id=73818

Lembrando... houve mudanças significativas, que podem quebrar seu código... saiba mais no blog do Ayende

PS.: Olha que legal o logo que o ayende fez

2月7日

Exemplos de mapeamento com NHibernate

Quem inicia com NHibernate com frequência se defronta com a pergunta: "como que posso mapear este relacionamento ?".

Para nos ajudar o Davy Brion, em seu blog, postou uma série de exemplos... para acessar o blog dele, cliquem aqui

12月13日

NHibernate deixando sua aplicação "lenta" ???

No último dia 10 o Ben Scheirman conta uma situação que ele enfrentou referente o desempenho de uma aplicação sua, que não estava satisfatório, e que usava o nhibernate. Ele relata aqui em detalhes e relaciona alguns pontos importantes... que tomei a liberdade fazer uma tradução livre deles.

  1. Entenda o mecanismo de lazy loading
  2. Prefira fazer uma busca ao invés de percorrer objetos por coleções associadas
  3. Ative o log, mesmo em desenvolvimento
  4. Utilize o otimizador de reflexão
  5. Considere utilizar uma UI ligada a DTOs ao invés de ligá-la diretamente em entidades
  6. Considere utilizar o modo ReadCommited nas trasações 
  7. Configure sempre o hibernate.defaul_schema

Não deixem de ler na íntegra o post dele...

12月12日

NHibernate e WPF

Quem começou a estudar o WPF percebeu que existe agora os tipos "observable collection". Quem usa o NHibernate e quer mapear uma lista Observable, sabe que não vai dar certo...

Aqui vai a dica então, encontrei neste blog uma implementação  de IUserCollectionType, disponível a partir da versão 1.2.0, que oferece suporte ao WPF para sets, list e bag.

Em seu blog, Adrian Alexander disponibiliza os fontes e um projeto de exemplo.

Baixem lá e não deixem de agredecê-lo pelo ótimo trabalho.

 

PS.: Adrian, really thanks !

11月30日

NHibernate 1.2.1

No último dia 26 saiu uma atualização do NHibernate.... não percam

Build 1.2.1
========================

Bug Fixed:

    * [NH-111] - Oracle "Invalid identifier" exception
    * [NH-989] - Assemblies are not registered in the correct order
    * [NH-995] - Problem with CompositeId+"key-many-to-one"+Caching
    * [NH-999] - One Shot Delete doesn't work - and cause reference violations
    * [NH-1006] - Invalid SQL order generated by JetDriver
    * [NH-1011] - update=false attribute ignored
    * [NH-1012] - DetachedCriteria CreateAlias with joinType (new in1.2) is broken
    * [NH-1018] - 'DistinctRootEntity' result transformer throws InvalidCastException
    * [NH-1023] - using projections and transformer causes invalid column name when property and alias are the same
    * [NH-1039] - NullReferenceException  for dynamic-component containing a set
    * [NH-1061] - Schema name missing when quering for highest key value
    * [NH-1064] - wrong association owner when fetching eagerly
    * [NH-1068] - Typo in example-mappings.html
    * [NH-1086] - SerializationException when using MemCacheProvider as cache because some classes miss the SerializableAttribute.
    * [NH-1124] - Problem in NHibernate.Type.ComponentType.NullSafeSet
    * [NH-1155] - SubselectFetch doesn't take into account paging
    * [NH-1156] - MS2005Dialect doesn't handle same column & alias names correctly
    * [NH-1167] - SubCriteria.CreateCriteria(string associationPath, string alias, JoinType joinType) always uses JoinType.InnerJoin 

Improvements:

    * [NH-901] - ComponentType mappings for with value types (structs) cause incorrect dirty checking
    * [NH-1049] - classes which inherit Order can't override ToSqlString

New Features:

    * [NH-1022] - Add command batching support for OracleClient driver

Patches Applied:

    * [NH-585] - Unknown version when using replicate and joined-subclass
    * [NH-903] - IQuery.SetFirstResult and SetMaxResults break in MsSql2005Dialect for ISQLQuery using WITH keyword
    * [NH-990] - Abstract CurrentSessionContext management and add more implementations
    * [NH-1014] - NHibernate Cross Join Syntax Causes Issues With SQL Server 2000/2005
    * [NH-1054] - Add hibernate.transaction.factory_class setting
    * [NH-1056] - Command batching support for OracleDataClientDriver
    * [NH-1076] - Sybase11 Dialect
    * [NH-1080] - HQL parser incorrectly registers a many-to-one association as a one-to-one.
    * [NH-1119] - valuetypes in uniqueresult<T> give an error when query result is null
    * [NH-1160] - Parameter compatibility problem in cached Sql command.
    * [NH-1193] - Limit string in MsSql2005 dialect can sort incorrectly on machines with multiple processors

Task Completed:

    * [NH-1002] - Document undocumented configuration properties
5月28日

Sistema MinhaClínica

Durante esta semana cheguei a uma conclusão a respeito do projeto que será desenvolvido. Será um controle de fichas de pacientes médicos. Durante o desenvolvimento vamos explorar os requisitos. Originalmente esse pequeno sistema foi desenvolvido por mim em Delphi possuindo como base de dados um banco Firebird 2.0. A idéia por traz deste projeto é mostrar como o desenvolvimento OO pode facilitar nossas vidas de desenvolvedores. E para iniciar quero expor minhas razões do porque utilizar uma ferramenta ORM, em especial o NHibernate.

Porque usar uma ferramenta ORM:

Há vários anos os bancos de dados relacionas são utilizados como armazenamento de dados e sua eficiência para isso não se discute. Com o passar do tempo vários recursos foram adicionados aos SGBDs, como recursos de relatórios e até mesmo de geração de interface. Nesse mesmo tempo as tecnologias para desenvolvimento de sistemas, a programação propriamente dita, também evoluíram em resposta aos novos desafios que lhe eram impostos.

Por exemplo os prazos foram diminuindo, novas plataformas como WEB e dispositivos móveis se popularizaram, a variedade de banco de dados relacionais existentes sem contar com a necessidade de se melhor representar o mundo real. Por esses e outros motivos a programação OO tem se firmado e crescido em uso.

Ao utilizar OO como programação possuindo um banco relacional como fonte de dados será percebido o que formalmente é conhecido como “impendância objeto/relacional”. Isso se dá ao fato de que os objetos de negócio podem ser representados de forma diferente em um banco relacional. Por isso que surgiram as ferramentas ORM. ORM significa Object Relational Mapping, mapeamento objeto relacional.

Uma ferramenta ORM tem por objetivo diminuir as diferenças entre objetos e tabelas de um SGBD. Devem oferecer formas para o desenvolvedor definir como que seus objetos serão persistidos (salvos) de forma física, e até mesmo como podem ser obtidos e manipulados do SGBD.

Se você ainda não compreendeu imagine uma classe Pessoa que possui os seguintes atributos: Nome, RG, Endereço, sendo que Endereço é uma outra classe que possui os atributos Logradouro, Número, Complemente, Bairro, Cidade, Estado e CEP. Como vamos salvar esse objeto? Em que banco de dados? Para qual tabela? Em quais campos? É isso que uma ferramenta ORM oferece e utilizando uma poderíamos responder as questões acima da seguinte forma, a classe Pessoa é mapeada para uma Tabela Pessoa, e a classe Endereço poderia ter seus atributos mapeados para a mesma tabela Pessoa.

Além de permitir isso, uma ferramenta ORM deve abstrair o banco de dados, seu acesso, sua manipulação através de SQL e até mesmo sua troca para permitir que o desenvolvedor mantenha o foco no que creio ser realmente importante, as regras de negócios que serão aplicadas ao sistema em desenvolvimento.

Porque utilizar o NHibernate ?

Além de oferecer os recursos comuns a qualquer ferramenta ORM que se preze temos:

  • Free – Além de ser free é open source.
  • Otimizações para cada SGBD que suporta – O NHibernate possui o conceito de dialetos que são otimizações de cada SGBD suportado. Por exemplo para se retornar os 10 primeiros registros de um consulta o ORACLE pode oferecer um comando particular que traz mais desempenho para a consulta. Igualmente pode acontecer com o SQL Server, MySQL ou Firebird. É aí que entram os dialetos. Cada dialeto implementa as peculiaridades de SQL dos gerenciadores SQL suportados. Isso significa que independentemente do banco de dados utilizado, sua aplicação o está utilizando da melhor forma.
  • Suporte à Stored Procedures – para os mais “tradicionais”, ou em algum momento de performance, pode-se especificar que uma determinada classe será carregada por uma stored procedure. Eu particularmente nunca tive a necessidade de substituir minhas sentenças por uma stored procedure.
  • Segurança contra SQL Injection – Esse é um problema que é resolvido facilmente com o NHibernate porque todo SQL gerado é parametrizado e fortemente tipado, além de que para o desenvolvedor seu uso está encapsulado.
  • Suporte a Lazy Loading – Isso quer dizer que só são trazidos os dados que são utilizados, ou seja, são obtidos por demanda. Por exemplo a classe Pessoa citada anteriormente, os dados do atributo Endereço só seriam retornados se fizéssemos uma chamada a eles. Isso implica em melhoria de performance e menos consumo de memória.
  • Suporte Ativo - O fórum do NHibernate é muito ativo, uma questão lá é rapidamente respondida, podendo ser utilizado também como base de conhecimento.
  • “Parentes” ilustres – O NHibernate é um porte para .NET da tão aclamada ferramenta Hibernate. Troque uma idéia com qualquer desenvolvedor sério de Java e verá que esta é melhor ferramenta ORM.
5月21日

NHibernate + Windows forms

Há algum tempo ando meio "revoltado" com os exemplos de uso do NHibernate que encontro na net. Posso dizer que praticamente todos são para ASP.NET.  No mercado onde a empresa que trabalho atua é impossível aplicar um sistema web, isto devido à exeriência de uso do usuário. Então quero propor aqui um  início de um projeto que mostrará como utilizar o NHibernate com windows forms. Além do NHibernate quero trocar experiências aqui sobre NUnit, NMvp, NCover e quem sabe chegar até mesmo nos builds automáticos. Quero contar com a ajuda de quem tem lido este blog, atraavés de sugestões e dicas, porque tbém não sei tudo, quero aprender com quem sabe mais do que eu :)
 
Fica aqui então.... vou definir durante a semana qual será o projeto... 
 
PS.: O projeto não andará a passos largos, visto que faço isso no meu tempo livre...
 
5月14日

LINQ para NHibernate

A coqueluche do momento é o LINQ... utilizar sentenças da forma que o LINQ utiliza pode ser útil em alguns momentos e pensando nisso alguns colaboradores do projeto NHibernate já possuem uma implementação do LINQ para o NHibernate. Abaixo alguns links:

Implementando LINQ para NHibernate - por Oren Eini

Ordenação e Paging

Agregação e operadores

Até mais