How to use fluent nhibernate properly?

5

One problem I have encountered due to inexperience with this kind of tool is how to use lazy load without interfering with the software architecture.

I use the following architecture:

IHM (Human Machine Interface): It has all the forms and communicates with the BLL

BLL (Business Logic Layer): It has all the rules of how a class should behave, throws all exceptions for display on HMI , does and returns all type of command that wraps database using the DAL layer

Data Access Layer (DAL): Here is Fluent Nhibernate and here you can ask.

I've read a lot about the great performance gain when using LazyLoad , however, I can not think, or even imagine, how I could use it without disrupting the architecture. Well, my HMI does not have access to the DAL layer session.

Imagine the following situation:

Function of BLL getSales () asks for all sales for the DAL layer. The HMI gets the result and displays the sales schedules. When you select a time, you must display the products sold in this sale. This requires a lazy load, but how to do this if the session has already been dropped? Is it bad architecture? Is there any other way?

NOTE: I know I can turn off Lazy Load, but I'm asking about a way to use lazyload for better performance.

EDIT

FormOnLoad

privatevoidFrmVisualizarVenda_Load(objectsender,EventArgse){try{List<Venda>Vendas;if(Acao==AcaoIHMVenda.PorData)Vendas=VendaBLL.getVendasDia(DataVenda.Value);else{Vendas=VendaBLL.getVendasCanceladasHoje();btnCancelar.Visible=false;btnReimpressao.Enabled=false;}lstVendas.Items.AddRange(Vendas.ToArray());}catch(Exceptionex){MessageBox.Show(ex.Message);}}

ListIndexChanged

publicvoidlstVendas_SelectedIndexChanged(objectsender,EventArgse){grdProdutos.Rows.Clear();VendaEscolhida=newVendaBLL((Venda)lstVendas.SelectedItem);Vendav=VendaEscolhida.Venda;lblCod.Text=v.CdVenda.ToString();lblOp.Text=v.Operador.Nome;lblDinheiro.Text=v.Dinheiro.ToString("C2");
        lblDebito.Text = v.Debito.ToString("C2");
        lblCredito.Text = v.Credito.ToString("C2");
        lblVoucher.Text = v.Voucher.ToString("C2");

        lblDesconto.Text = v.Desconto.ToString("C2");
        lblPago.Text = v.ValorPago.ToString("C2");
        lblTroco.Text = v.Troco.ToString("C2");

        foreach (ProdutoVendido pv in v.Vendidos)
        {
            grdProdutos.Rows.Add(
                pv.Produto,
                pv.Quantidade,
                pv.Produto.Valor,
                pv.Total,
                pv.Cortesia
                );
        }
    }
    
asked by anonymous 24.06.2014 / 21:01

2 answers

5

Example:

Connection

public interface IConnection: IDisposable
{
    void Close();
    ISession Open();
    FluentConfiguration Configuration { get; }
    ISessionFactory SessioFactory { get; }
    ISession Session { get; }
}
public class Connection : IConnection
{
    private FluentConfiguration _configuration;
    private ISessionFactory _sessiofactory;
    private ISession _session;

    public FluentConfiguration Configuration
    {
        get { return _configuration; }
        private set { _configuration = value; }
    }
    public ISessionFactory SessioFactory
    {
        get { return _sessiofactory; }
        private set { _sessiofactory = value; }
    }

    public ISession Session
    {
        get { return _session; }
        private set { _session = value; }
    }

    public Connection()
    {
        Init();
    }

    private void Init()
    {
        _configuration = Fluently.Configure()
        .Database(MySQLConfiguration.Standard.ConnectionString(x => x
                                                                 .Server("localhost")
                                                                 .Username("root")
                                                                 .Password("senha")
                                                                 .Database("site1")))
        .Mappings(c => c.FluentMappings.AddFromAssemblyOf<WindowsFormsApp.Code.Models.Cliente>());
        _sessiofactory = _configuration.BuildSessionFactory();
        _session = _sessiofactory.OpenSession();
    }
    public ISession Open()
    {
        if (_session.IsOpen == false)
        {
            _session = _sessiofactory.OpenSession();
        }
        return _session;
    }
    public void Close()
    {
        if (_session != null && _session.IsOpen)
        {
            _session.Close();
            _session.Dispose();
        }
        _configuration = null;
        if (_sessiofactory != null && _sessiofactory.IsClosed == false)
        {
            _sessiofactory.Close();
            _sessiofactory.Dispose();
        }
    }
    ~Connection()
    {

    } 
    public void Dispose()
    {
        Close();
    }      

}

Repository

public interface IRepository<T>: IDisposable
        where T : class, new()
{
    IConnection Connection { get; }
    void Add(T model);
    void Edit(T model);
    void AddorEdit(T model);
    void Remove(T model);
    T Find(object Key);
    IQueryable<T> Query();
    IQueryable<T> Query(Expression<Func<T, bool>> Where);
}

using NHibernate.Linq;
public abstract class Repository<T> : IRepository<T> where T : class, new()
{
    private IConnection _connection;
    public IConnection Connection
    {
        get { return this._connection; }
        private set { this._connection = value; }
    }
    public Repository()
    {
        this._connection = new Connection();
        this.Connection.Open();
    }
    public Repository(IConnection Connection)
    {
        this._connection = Connection;
        this.Connection.Open();
    }

    public void Add(T model)
    {
        this._connection.Session.Transaction.Begin();
        this._connection.Session.Save(model);
        this._connection.Session.Transaction.Commit();
    }

    public void Edit(T model)
    {
        this._connection.Session.Transaction.Begin();
        this._connection.Session.SaveOrUpdate(model);
        this._connection.Session.Transaction.Commit();
    }

    public void AddorEdit(T model)
    {
        this._connection.Session.Transaction.Begin();
        this._connection.Session.SaveOrUpdate(model);
        this._connection.Session.Transaction.Commit();
    }

    public void Remove(T model)
    {
        this._connection.Session.Transaction.Begin();
        this._connection.Session.Delete(model);
        this._connection.Session.Transaction.Commit();
    }

    public T Find(object Key)
    {
        return (T)this._connection.Session.Get<T>(Key);
    }

    public IQueryable<T> Query()
    {
        try
        {
            return this._connection.Session.Query<T>();
        }
        catch (NHibernate.ADOException ex)
        {
            var er = ex.Data;
        }
        return null;
    }

    public IQueryable<T> Query(Expression<Func<T, bool>> Where)
    {
        return this._connection.Session.Query<T>().Where(Where);
    }

    ~Repository()
    {

    }
    public void Dispose()
    {
        if (_connection != null)
        {
            _connection = null;
        }
    }
}

Class Mapped

//CLIENTE E CLIENTEMAP
public class ClienteMap : ClassMap<Cliente>
{
    public ClienteMap()
    {
        Table("cliente");
        Id(x => x.Codigo).GeneratedBy.Increment().Not.Nullable().Column("codigo");
        Map(x => x.Nome).Nullable().Column("nome");

        HasMany(x => x.Telefone).Cascade.All().LazyLoad().KeyColumn("codigocliente");
    }
}
public class Cliente
{
    public Cliente()
    {
        this.Telefone = new List<Telefone>();
    }

    public virtual int Codigo { get; set; }
    public virtual String Nome { get; set; }       

    public virtual IList<Telefone> Telefone { get; set; }
}
//TELEFONE E TELEFONEMAP
public class TelefoneMap : ClassMap<Telefone>
{
    public TelefoneMap()
    {
        Table("telefone");
        Id(x => x.Codigo).Not.Nullable().UniqueKey("codigo").GeneratedBy.Increment().Column("codigo");
        Map(x => x.Ddd).Not.Nullable().Column("ddd").Length(3);
        Map(x => x.Numero).Not.Nullable().Column("numero").Length(14);

        References(x => x.Cliente).Cascade.All().LazyLoad().Column("codigocliente");
    }
}
public class Telefone
{
    public Telefone() { }
    public virtual int Codigo { get; set; }
    public virtual Cliente Cliente { get; set; }
    public virtual String Ddd { get; set; }
    public virtual String Numero { get; set; }
}

RepositoryClient and RepositoryTelephone

public sealed class RepositoryCliente : Repository<Cliente>
{
    public RepositoryCliente() : base() { }
    public RepositoryCliente(IConnection Connection)
        : base(Connection) { }
}
public sealed class RepositoryTelefone : Repository<Telefone>
{
    public RepositoryTelefone() : base() { }
    public RepositoryTelefone(IConnection Connection)
        : base(Connection) { }
}

Well this is all the code I usually do, now how to use it all: Explanation: I want to show in ListBox the Clientes and in GridView their respective Telephones

Form

FormCode

usingSystem;usingSystem.Collections.Generic;usingSystem.ComponentModel;usingSystem.Data;usingSystem.Drawing;usingSystem.Linq;usingSystem.Text;usingSystem.Windows.Forms;usingWindowsFormsApp.Code;usingWindowsFormsApp.Code.Abstract;usingWindowsFormsApp.Code.Models;namespaceWindowsFormsApp{publicpartialclassForm1:Form{publicForm1(){InitializeComponent();}privateRepository<Cliente>RepCliente;privatevoidForm1_Load(objectsender,EventArgse){DataGridFones.AutoGenerateColumns=false;RepCliente=newRepositoryCliente();ListClientes.DataSource=RepCliente.Query().ToList();ListClientes.DisplayMember="Nome";
            ListClientes.ValueMember = "Codigo";

        }

        ~Form1()
        {
            RepCliente.Dispose();
        }

        private void ListClientes_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (ListClientes.SelectedItems.Count > 0 &&
                ListClientes.SelectedIndex >= 0)
            {
                Cliente cliente = RepCliente.Find(((Cliente)ListClientes.SelectedItem).Codigo);
                if (cliente != null)
                {
                    DataGridFones.DataSource = cliente.Telefone.ToList();
                    DataGridFones.Update();
                }
            }
        }
    }
}

Good with only RepositoryCliente I can redeem without closing your session and only closes when the form closes. Also a little code point that does a lot of stuff in form , and the important one working with lazyload by the configuration given by framework .

About the doubt of several% open%, how to use 1 for all Session

Encoding Example:

using (IConnection connection = new Connection())
using (Repository<Cliente> RepClientes = new RepositoryCliente(connection))
using (Repository<Telefone> RepTelefones = new RepositoryTelefone(connection))
{
        //CODIFICAÇÃO           
}

Or

IConnection connection = new Connection();
Repository<Cliente> RepClientes = new RepositoryCliente(connection);
Repository<Telefone> RepTelefones = new RepositoryTelefone(connection);
//CODIFICAÇÃO   
//CODIFICAÇÃO   
//CODIFICAÇÃO   
RepClientes.Dispose();
RepTelefones.Dispose();
connection.Dispose();

A% w of% was then made that will serve two or more Repository .

    
25.06.2014 / 19:01
2

nHibernate is not good for loading data on demand the way you want it, which Entity Framework does well.

In the case of your getVendas() , the Entity Framework loads as follows:

  • Performs a benchmarked query with only the first level of data (that is, objects of type Models.Venda ;
  • Collections of Models.Produto of each sale each receive a Dynamic Proxy, that is, an object that implements ICollection , but is not exactly ICollection .
  • Only when the HMI accesses the product collection of each sale is the data effectively uploaded. In this case, the Entity Framework replaces the DynamicProxy with the effective collection of products.

nHibernate creates a complication for the programmer, which is having to control the data session. This is the same problem with Java Hibernate, where each DAO event requires the programmer to indicate which entities will be effectively loaded, and in what way ( lazy or eager ).

Consider changing the Framework so as not to undermine the cohesion of your architecture.

In any case, if really you need to keep nHibernate, you can write ActionFilter to open the data session for you at any time you deem necessary. It is enough to mark the methods of your BLL with Attribute of this ActionFilter that the data session will be opened during the execution of the method.

    
24.06.2014 / 21:25