How to use fluent nhibernate properly?

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

I use the following Architecture:

HMI (human-machine Interface): has all forms and communicates with Layer BLL

BLL (Business Logic Layer): has all the rules of how a class should behave, throws all exceptions to be displayed in the HMI , it makes and returns any type of command involving database using the Dal

DAL (Data Access Layer): here is Fluent Nhibernate and here it makes the queries.

I've read a lot about the great performance gain when using LazyLoad, but I can't think, or imagine, how I could use it without disrupting the architecture. Yeah, my HMI doesn't have Session access from the Dal layer.

Picture a next situation:

BLL function get sales() asks for all sales to the Dal layer. HMI takes the result and displays the sales Times. When the user selects a time, they 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 means?

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

EDIT Sales preview screen

Form Onload

    private void FrmVisualizarVenda_Load(object sender, EventArgs e)
    {
        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(Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }

List IndexChanged

    public void lstVendas_SelectedIndexChanged(object sender, EventArgs e)
    {
        grdProdutos.Rows.Clear();

        VendaEscolhida = new VendaBLL((Venda)lstVendas.SelectedItem);
        Venda v = 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
                );
        }
    }
Author: Latrova, 2014-06-24

2 answers

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 that's all the code I usually do, now how to use it all: Explanation: I want to show in ListBox the Clientes and in the GridView their respective Phones

Form

insert the description of the image here

Form Code

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using WindowsFormsApp.Code;
using WindowsFormsApp.Code.Abstract;
using WindowsFormsApp.Code.Models;

namespace WindowsFormsApp
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        private Repository<Cliente> RepCliente;
        private void Form1_Load(object sender, EventArgs e)
        {

            DataGridFones.AutoGenerateColumns = false;

            RepCliente = new RepositoryCliente();

            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 quite a thing in form, and the important thing working with lazyload by the configuration given by framework.

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

coding 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();

then a connection was made that will serve two or more repository.

 5
Author: , 2014-06-26 03:13:46

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

In the case of your getVendas(), The Entity Framework performs the load as follows:

  • performs a database query bringing only the first level of data (i.e. objects of type Models.Venda;
  • collections of Models.Produto from each sale each receive a dynamic Proxy, i.e. an object that implements ICollection, but is not exactly a ICollection.
  • only when HMI accesses the product collection of each sale is the data actually loaded. In this case, the Entity Framework replaces DynamicProxy with the effective collection of products.

NHibernate creates a complication for the programmer, which is having to control the data session. It's the same problem as Java Hibernate, where each DAO event requires the programmer to indicate which entities will actually be loaded, and in which way (lazy or eager ).

Consider swapping the Framework so as not to harm the cohesion of your architecture.

In any case, if it is really necessary to keep nHibernate, you can write a ActionFilter that opens the data session for you at the time you deem necessary. Then just mark the methods of your BLL with the Attribute of this ActionFilter that the data session will open during the execution of the method.

 2
Author: Leonel Sanches da Silva, 2014-06-24 19:35:41