Introdução

Fluent NHibernate oferece uma alternativa para os arquivos de mapeamento XML do NHibernate utilizando Fluent Interfaces. Ao invés de escrever documentos XML (arquivos .hbm.xml), Fluent NHibernate permite que você escreva mapeamentos em código C #. Isto permite uma refatoração de maneira fácil e melhora a legibilidade do código.

Também existem outras ferramenta para Fluent NHibernate, como:

  • Auto-Mapeamentos – onde os mapeamentos são gerados a partir do desenho de suas entidade.
  • Testes de Persistência – teste de ida e volta (round-trip testing) para as suas entidades, sem nunca ter que escrever uma linha de CRUD.
  • Configuração Full do aplicativo com Fluent Configuration API
  • Configuração de banco de dados – configurar o banco de dados em código fluente

Fluent NHibernate não faz parte do Core do NHibernate, mas é totalmente compatível com a versão 2.1 do NHibernate.

Background

NHibernate é um Object Relational Mapping, que (como afirma ORM) mapeia os dados relacionais aos objetos. Ele define o mapeamento em um formato XML chamado HBM, cada classe tem um HBM correspondente em formato XML mapeando para a estrutura do banco de dados. O Fluent NHibernate substitui exatamente esses arquivos XML de mapeamento.

Por que substituir hbm.xml?

Embora a separação de código e XML é bom, pode levar a várias situações indesejáveis.
Devido ao XML não ser validado pelo compilador, você pode re-nomear suas propriedades nas classes e esquecer-se de atualizar em seus mapeamentos, nesta situação, você só irá descobrir o erro em tempo de execução.
Mapeamentos Repetitivos – Mapeamentos HBM no NHibernate pode se tornar bastante trabalhoso se você precisar implementar a mesma regra para os vários mapeamentos. Por exemplo, se você precisa garantir que todas as propriedades do tipo string não deve ser nulo e deve ser do tamanho mínimo de 1000, e todos tipos int deve ter um valor padrão de -1.

Como o Fluent NHibernate resolve esses problemas?

Ele faz isso movendo o mapeamento para o código real, então eles estão compilados junto com o resto da sua aplicação; Ao efetuar um refactoring e renomear uma propriedade ou classe ele irá alterar os mapeamentos da forma devida, e o compilador irá falhar em qualquer erro de digitação. Quanto à repetição, Fluent NHibernate possui um sistema de configuração convencional, onde você pode especificar padrões para substituir as convenções de nomenclatura e muitas outras coisas, definindo a forma como as coisas devem ser somente uma vez, então Fluent NHibernate faz o resto.

Exemplo simples:

Mapeamento Tradicional por HBM XML

[xml] <?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
namespace="QuickStart" assembly="QuickStart">
<class name="Cat" table="Cat">
<id name="Id">
<generator class="identity" />
</id>
<property name="Name">
<column name="Name" length="16" not-null="true" />
</property>
<property name="Sex" />
<many-to-one name="Mate" />
<bag name="Kittens">
<key column="mother_id" />
<one-to-many class="Cat" />
</bag>
</class>
</hibernate-mapping>
[/xml]

Fluent NHibernate equivalente

[csharp] public class CatMap : ClassMap
{
public CatMap()
{
Id(x => x.Id);
Map(x => x.Name).Length(16).Not.Nullable();
Map(x => x.Sex);
References(x => x.Mate);
HasMany(x => x.Kittens);
}
}
[/csharp]

Instalação

Binários

Você pode baixar o release do projeto no site principal do projeto em http://fluentnhibernate.org/downloads
Agora que você tem Fluent NHibernate, você só precisa fazer referência a FluentNHibernate.dll em seu projeto.

Seu primeiro projeto

Para este exemplo vamos utilizar um simples mapeamento de um domínio para uma empresa de varejo. A empresa tem duas lojas, cada uma com produtos (alguns produtos estão em ambas as lojas, algumas são exclusivas), e cada uma com os empregados. Em termos de banco de dados temos as tabelas Store, Employe e Product, em um relacionamento de n-para-n entre as tabelas Store e Product.

Primeiro, crie um aplicativo de console e referencie FluentNHibernate.dll e NHibernate.dll, para esse exemplo, vamos utilizar o banco de dados SQLite, então você precisará da biblioteca System.Data.SQLite que é distribuído com NHibernate Fluent.

Veja a estrutura do projeto que eu usei acima. A pasta Entities é para os objetos de domínio, enquanto a pasta Mappings é onde vamos colocar as classes de mapeamento fluente.

Para o restante deste artigo vou assumir que você usou a mesma estrutura.

Entidades

Agora vamos começar criando as nossas entidades. Nós temos três tabelas e precisamos mapeá-las (estamos ignorando a tabela de n-para-n), de modo que é uma entidade por tabela. Crie as seguintes classes na sua pasta Entities.

[csharp] public class Employee
{
public virtual int Id { get; private set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual Store Store { get; set; }
}
[/csharp]

Nossa entidade de Funcionário possui uma identificação, o nome da pessoa (Nome e Sobrenome) e uma referência à loja onde trabalha.
Há duas coisas que podem ser diferentes para você se você não estiver familiarizado com o NHibernate. Em primeiro lugar, o Id propriedade tem um setter privado, é porque apenas o NHibernate que deve setar o valor do Id. Em segundo lugar, todas as propriedades estão marcadas virtual, isto é porque o NHibernate cria “proxies” de suas entidades em tempo de execução para permitir o lazy loading (carregamento preguiçoso), e para ele fazer isso ele precisa ser capaz de substituir as propriedades.

[csharp] public class Product
{
public virtual int Id { get; private set; }
public virtual string Name { get; set; }
public virtual double Price { get; set; }
public virtual IList StoresStockedIn { get; private set; }

public Product()
{
StoresStockedIn = new List();
}
}
[/csharp]

Produto tem Id, nome, preço, e uma coleção de produtos.

[csharp] public class Store
{
public virtual int Id { get; private set; }
public virtual string Name { get; set; }
public virtual IList Products { get; set; }

public virtual IList Staff { get; set; }
public Store()
{
Products = new List();
Staff = new List();
}

public virtual void AddProduct(Product product)
{
product.StoresStockedIn.Add(this);
Products.Add(product);
}

public virtual void AddEmployee(Employee employee)
{
employee.Store = this;
Staff.Add(employee);
}
}
[/csharp]

Finalmente, temos a nossa entidade Store que possui Id e nome, com uma coleção de produtos de que são estocados nela, e uma coleção de Empregados. Esta entidade tem um pouco de lógica para tornar o código mais simples, são os métodos AddProduct e AddEmployee, esses métodos são usados para adicionar itens em coleções, e configurar o outro lado do relacionamento.
Vamos à explicação: em um relacionamento onde ambos os lados são mapeados, NHibernate precisa que você defina os dois lados antes dele salvar corretamente. Então, para não ter todo esse código repetindo em vários lugares, vamos reduzir a estes dois métodos adicionais na classe Store.

Mapeamentos

Agora que temos as nossas entidades criadas, é hora de mapeá-los usando Fluent NHibernate. Vamos começar com a classe mais simples, que é Employee. Todos os seguintes mapeamentos devem ser criados dentro da pasta Mapping.
Para mapear uma entidade, temos que criar uma classe de exclusiva (normalmente esta segue a convenção de nomenclatura EntityNameMap), então vamos criar uma classe EmployeeMap, essas classes de mapeamento derivam de ClassMap onde T é a sua entidade .

[csharp] public class EmployeeMap : ClassMap<Employee>
{
}
[/csharp]

Os mapeamentos são feitos dentro do construtor da classe EmployeeMap, então vamos adicionar um construtor e escrever os mapeamentos.

[csharp] public class EmployeeMap : ClassMap<Employee>
{
public EmployeeMap()
{
Id(x => xId);
}
}
[/csharp]

Para começar mapeamos a coluna Id, e dizemos ao Fluent NHibernate quem é o identificador. O x , neste exemplo é a instancia de Employee para o Fluent NHibernate recuperar as propriedades, então tudo que você está fazendo é dizer quem é sua propriedade Id. Fluent NHibernate vai ver se sua propriedade é do tipo de int, e automaticamente vai decidir que ele deve ser mapeado como um auto-incremento no banco de dados. Para usuários do NHibernate, isso significa que ele cria automaticamente o elemento generator em identity. Vamos continuar nosso mapeamento

[csharp] public class EmployeeMap : ClassMap<Employee>
{
public EmployeeMap()
{
Id(x => x.Id);
Map(x => x.FirstName);
Map(x => x.LastName);
References(x => x.Store);
}
}
[/csharp]

Veja agora temos mais dois novos métodos Map e References, Map é usado para mapear de uma maneira simples as propriedades FistName e LastName e References é utilizado para dizer que a classe Employee possui uma propriedade que faz referêcia a uma Loja (Store) ou seja um funcionário pertence a uma loja. Vamos continuar mapeando a loja (Classe Store).

[csharp] public class StoreMap : ClassMap<Store>
{
public StoreMap()
{
Id(x => x.Id);
Map(x => x.Name);
HasMany(x => x.Staff).Inverse().Cascade.All();
HasManyToMany(x => x.Products).Cascade.All().Table("StoreProduct");
}
}
[/csharp]

Novamente há dois novos métodos que utilizamos no mapeamento acima HasMany e HasManyToMany. HasMany está criando um mapeamento de um-para-muitos com Empregados (Employees) ou seja uma loja possui vários empregados e HasManyToMany cria um relacionamento de um-para-muitos com classe produtos, sendo assim um produto pode existir em mais de uma loja.

  • O HasMany método tem uma segunda chamada o Inverse (), e HasManyToMany tem Cascade.All (), está chamada a partir de outra chamada, isso que chamamos de Fluent ou método de encadeamento, e é usado para criar uma linguagem mais natural em sua configuração.
  • Inverse em HasMany é um termo do NHibernate, que significa que a outra ponta do relacionamento que será responsável por salvar.
  • Cascade.All em HasManyToMany diz que os eventos do NHibernate serão executados em cascata sobre as entidades da coleção (assim quando você salvar a loja, todos os produtos da loja também serão salvos).
  • Table seta o nome da tabela de relacionamento de muitos-para-muitos.

Finalmente, vamos mapear o produto.

[csharp] public class ProductMap : ClassMap<Product>
{
public ProductMap()
{
Id(x => x.Id);
Map(x => x.Name);
Map(x => x.Price);
HasManyToMany(x => x.StoresStockedIn).Cascade.All().Inverse().Table("StoreProduct");
}
}
[/csharp]

O produto foi mapeado apenas com métodos que já conhecemos, foi utilizado também HasManyToMany para o mapeamento do outro lado do relacionamento bidirecional de muitos-para -muitos com Store.

Agora vamos inicializar alguns dados e a saída para o console.

[csharp] static void Main()
{
var sessionFactory = CreateSessionFactory();

using (var session = sessionFactory.OpenSession())
{
using (var transaction = session.BeginTransaction())
{
// create a couple of Stores each with some Products and Employees
var barginBasin = new Store { Name = "Bargin Basin" };
var superMart = new Store { Name = "SuperMart" };

var potatoes = new Product { Name = "Potatoes", Price = 3.60 };
var fish = new Product { Name = "Fish", Price = 4.49 };
var milk = new Product { Name = "Milk", Price = 0.79 };
var bread = new Product { Name = "Bread", Price = 1.29 };
var cheese = new Product { Name = "Cheese", Price = 2.10 };
var waffles = new Product { Name = "Waffles", Price = 2.41 };

var daisy = new Employee { FirstName = "Daisy", LastName = "Harrison" };
var jack = new Employee { FirstName = "Jack", LastName = "Torrance" };
var sue = new Employee { FirstName = "Sue", LastName = "Walkters" };
var bill = new Employee { FirstName = "Bill", LastName = "Taft" };
var joan = new Employee { FirstName = "Joan", LastName = "Pope" };

// add products to the stores, there’s some crossover in the products in each
// store, because the store-product relationship is many-to-many
AddProductsToStore(barginBasin, potatoes, fish, milk, bread, cheese);
AddProductsToStore(superMart, bread, cheese, waffles);

// add employees to the stores, this relationship is a one-to-many, so one
// employee can only work at one store at a time
AddEmployeesToStore(barginBasin, daisy, jack, sue);
AddEmployeesToStore(superMart, bill, joan);

// save both stores, this saves everything else via cascading
session.SaveOrUpdate(barginBasin);
session.SaveOrUpdate(superMart);

transaction.Commit();
}

// retreive all stores and display them
using (session.BeginTransaction())
{
var stores = session.CreateCriteria(typeof(Store)).List();

foreach (var store in stores)
{
WriteStorePretty(store);
}
}

Console.ReadKey();
}
}

public static void AddProductsToStore(Store store, params Product[] products)
{
foreach (var product in products)
{
store.AddProduct(product);
}
}

public static void AddEmployeesToStore(Store store, params Employee[] employees)
{
foreach (var employee in employees)
{
store.AddEmployee(employee);
}
}

[/csharp]

Você ainda não será capaz de executar, porque há uma coisa. Precisamos implementar o método CreateSessionFactory, que é onde a nossa configuração vai amarrar e NHibernate e Fluent NHibernate.

Configuração

Vamos implementar o CreateSessionFactory método

[csharp]

private static ISessionFactory CreateSessionFactory()
{
}

[/csharp]

Veja que está retornando um NHibernate ISessionFactory Agora vamos usar o Fluent NHibernate Fluently.Configure API para configurar nossa aplicação.

[csharp] private static ISessionFactory CreateSessionFactory()
{
return Fluently.Configure() .BuildSessionFactory();
}
[/csharp]

Ainda não está pronto, estamos criando uma SessionFactory, mas ainda não configuramos nada, por isso vamos configurar nosso banco de dados.

[csharp] private static ISessionFactory CreateSessionFactory()
{
return Fluently.Configure()
.Database( SQLiteConfiguration
.Standard.UsingFile("firstProject.db") )
.BuildSessionFactory();
}
[/csharp]

Nós especificado que estamos usando um arquivo baseado em banco de dados SQLite.
Só mais uma coisa, é preciso fornecer ao NHibernate os mapeamentos que nós criamos. Para fazer isso, adicione uma chamada para os mapeamentos na configuração.

[csharp] private static ISessionFactory CreateSessionFactory()
{
return Fluently.Configure()
.Database(SQLiteConfiguration
.Standard
.UsingFile("firstProject.db"))
.Mappings(m => m.FluentMappings.AddFromAssemblyOf())
.BuildSessionFactory();
}
[/csharp]

É isso aí, essa é a sua aplicação configurada!
Lá vai, é o seu primeiro projeto NHibernate Fluent criado e funcionando!

Geração do Banco de Dados

Se você não tiver criado manualmente o esquema para esta aplicação, então ele irá falhar na primeira vez que você executá-lo. Há algo que você pode fazer, mas precisa ser feito diretamente no objeto NHibernate Configuration, podemos fazer isso usando o método ExposeConfiguration. Combine a chamada a um método para gerar o esquema, então você é capaz de criar o seu esquema durante a execução.

[csharp]

private static ISessionFactory CreateSessionFactory()
{
return Fluently.Configure()
.Database(
SQLiteConfiguration.Standard
.UsingFile("firstProject.db")
)
.Mappings(m =>
m.FluentMappings.AddFromAssemblyOf
())
.ExposeConfiguration(BuildSchema)
.BuildSessionFactory();
}

private static void BuildSchema(Configuration config)
{
// delete the existing db on each run
if (File.Exists(DbFile))
File.Delete(DbFile);

// this NHibernate tool takes a configuration (with mapping info in)
// and exports a database schema from it
new SchemaExport(config)
.Create(false, true);
}

[/csharp]

Fonte: http://wiki.fluentnhibernate.org/Getting_started

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Preencha esse campo
Preencha esse campo
Digite um endereço de e-mail válido.
Você precisa concordar com os termos para prosseguir

Menu