Vinicius Quaiato

{tecnologia, conceitos, negócios, idéias, práticas, .NET, ruby, osx, ios e algo mais}

Morte aos XMLs: usando Fluent NHibernate


Pois é, algumas coisas incomodam a vida de um desenvolvedor. Dentre elas podemos destacar XMLs de configuração.Não sei exatamente a razão de não gostarmos muito deles, mas fato é que quase ninguém gosta. Como alguns de vocês devem saber o NHibernate é uma das ferramentas mais poderosas no que se diz respeito a ORM na plataforma .NET.

Além de podermos configurá-lo utilizando os cruéis costumeiros arquivos XML, existe uma forma de fazer isso via código, com uma sintaxe mais próxima da que costumamos trabalhar diariamente: Fluent NHibernate.

Fluent NHibernate

Este não é um post que vai te ensinar a usar o NHibernate. Este é um post que vai te ensinar, um pouco, a usar as configurações fluentes do NHibernate. A idéia é mostrar como configurar a conexão com a base de dados e um pouco de como mapear suas entidades e associações entre elas de uma forma mais simples e verificável utilizando o Fluent NH.

Baixando o Fluent NHibernate

Dentro da página oficial do Fluent NHibernate temos uma seção de Downloads: Let's go!O download do Fluent NHibernate nos trará uma pasta com uma série de dlls, que são as dlls do NHibernate e as dlls do Fluent NHibernate.

Referenciando o NHibernate e o Fluent NHibernate no projeto

Vamos adicionar as seguintes dlls ao nosso projeto:

Configurando a sessão do NHibernate com Fluent NHibernate

Agora precisamos configurar a sessão do NHibernate para se conectar ao nosso banco de dados. É claro, não farei isso usando XML e sim o Fluent NH, como mostra o código abaixo:

 1 public static ISession ObterSessao(){
 2   var session = Fluently.Configure()
 3                         .Database(MsSqlConfiguration
 4                         .MsSql2008
 5                         .ConnectionString(c => c.FromConnectionStringWithKey("ConexaoBanco")))
 6                         .Mappings(m => m.FluentMappings.AddFromAssembly(Assembly.GetExecutingAssembly()))
 7                         .BuildSessionFactory()
 8                         .OpenSession();
 9   return session;
10 }

O código por si só fica explicado, não é mesmo? Na linha 2 iniciamos nossa configuração. Na linha 3 começamos a configurar o banco de dados, e dizemos que é um banco de dados MS SqlServer. na linha 5.

Na linha 5 informo a connection string que está no app.config/web.config e o nome é "ConexaoBanco". Na linha 6 informamos onde estão nossos mapeamentos (este item está mais explicado abaixo). Neste caso dizemos o seguinte: "Nossos mapeamento estão neste assembly!". Isto fará com que essa nossa configuração procure no assembly sendo executado pelo nosso código de mapeamento de entidades.

Nas linhas 7 e 8 pedimos para que uma sessão seja criada à partir da nossa configuração. Bastante simples não?!

Mapeando as entidades e suas associações

Imaginando que meu sistema faz vendas vou utilizar as seguintes entidades:

 1 public class Venda{
 2   public virtual int Id { get; set; }
 3   public virtual IList<linhavenda> Linhas { get; set; }
 4 }
 5 
 6 public class LinhaVenda{
 7   public virtual int Id { get; set; }
 8   public virtual string Produto { get; set; }
 9   public virtual Venda Venda { get; set; }
10   public virtual decimal Preco { get; set; }
11   public virtual int Quantidade { get; set; }
12 }

Bastante simples, nada que precise ser comentado. Agora o mapeamento feito com Fluent NHibernate utiliza uma classe que herda de ClassMap<T>. Para cada classe que queremos mapear, precisamos criar uma classe que herda de ClassMap, veja abaixo o mapeamento para nossas 3 entidades:

 1 public class VendaMap : ClassMap<Venda>{
 2   public VendaMap()    {
 3     Id(v => v.Id);
 4     HasMany(v => v.Linhas).Cascade.All();
 5   }
 6 }
 7 
 8 public class LinhaVendaMap : ClassMap<LinhaVenda>{
 9   public LinhaVendaMap()    {
10     Id(l => l.Id);
11     References(l => l.Venda);
12     Map(l => l.Produto);
13     Map(l => l.Preco);
14     Map(l => l.Quantidade);
15   }
16 }

O código de mapeamento fluente praticamente nos diz exatamente o que ele faz, mas vamos ajudar um pouco.

Dentro do construtor de cada classe, colocamos o código de mapeamento. Na linha 3 podemos ver que definimos que o id da nossa entidade vendas no banco deve ser mapeado para a propriedade Id do objeto Venda. Na linha 4 dizemos que nossa Venda possui uma relação "One to Many" com LinhaVenda. Ou seja, uma venda possui muitas linhas de venda. Cascade All diz respeito a "cascatear" os eventos nas entidades da coleção, algo como quando salvar a Venda também salvar as linhas de venda.

No mapeamento da linha de venda temos algumas coisas a notar também. O mapeamento do id é igual ao da classe Venda. Na linha 11 podemos notar que mapeamento a propriedade Venda utilizando o método References. Isso cria uma relação do tipo "Many to One", ou seja, muitas linhas de venda para uma Venda. Isso é o que permitirá que o id de uma venda esteja presente em cada tupla da linha de venda no banco de dados.

Depois nas linhas 12, 13 e 14 mapeamos as propriedades simples utilizando o método Map, que faz com que cada propriedade seja mapeada para uma coluna na tabela respectiva.

Utilizando

Feito isso podemos executar nosso programa e brincar um pouco. Eu utilizei o seguinte código abaixo para criar uma venda com 3 linhas de venda:

 1 static void Main(string[] args){
 2   var session = SessaoNHibernate.ObterSessao();
 3   var venda = new Venda();
 4   venda.Linhas = new List<linhavenda>();
 5 
 6   var linha = new LinhaVenda();
 7   linha.Preco = 102;
 8   linha.Quantidade = 4;
 9   linha.Produto = "Prod 1";
10   linha.Venda = venda;
11   venda.Linhas.Add(linha);
12 
13   var linha2 = new LinhaVenda();
14   linha2.Preco = 1;
15   linha2.Quantidade = 5;
16   linha2.Produto = "prod 2!";
17   linha2.Venda = venda;
18   venda.Linhas.Add(linha2);
19 
20   var linha3 = new LinhaVenda();
21   linha3.Preco = 333;
22   linha3.Quantidade = 1;
23   linha3.Produto = "prod 3";
24   linha3.Venda = venda;
25   venda.Linhas.Add(linha3);
26 
27   session.SaveOrUpdate(venda);
28   session.Close();
29 }

Esse código criará uma Venda, e suas respectivas LinhaVenda, fará a inserção desses dados no banco, de forma bastante transparente.

Gerando o script do Banco de Dados

Se você quiser que seus mapeamentos sejam transformados em script para o banco de dados e já executado contra o database, altere o código da listagem de configuração para este código abaixo:

 1 public static ISession ObterSessao(){
 2   var session = Fluently.Configure()
 3                         .Database(MsSqlConfiguration
 4                         .MsSql2008
 5                         .ConnectionString(c => c.FromConnectionStringWithKey("conexaopadrao")))
 6                         .Mappings(m => m.FluentMappings.AddFromAssembly(Assembly.GetExecutingAssembly()))
 7                         .ExposeConfiguration(cfg => new SchemaExport(cfg)
 8                         .Create(true, true))
 9                         .BuildSessionFactory()
10                         .OpenSession();
11   return session;
12 }

Desta forma o código das linhas 7 e 8 farão com que o script do banco seja gerado e executado.Não preciso mencionar que esse código NÃO deve ir para produção não é mesmo?!

Finalizando

Bom galera, é basicamente isso. O NHibernate é muito mais poderoso e possui uma série de recursos, bem como o Fluent NHibernate também. A idéia deste post era fornecer, de uma maneira simples e direta, um guia de como começar a trabalhar com o Fluent.Qualquer dúvida ou sugestão, basta entrar em contato.Espero que aproveitem.

Att,

Vinicius Quaiato.

Voltar

Fork me on GitHub