How to use the nhibernate Fluent appropriately?

Asked

Viewed 2,331 times

5

A problem I have encountered due to the 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 the layer BLL

BLL (Business Logic Layer): Has all the rules of how a class should behave, throws all the exceptions to be displayed in IHM, makes and returns every type of command that involves database using the layer DAL

DAL (Data Access Layer): Here you find the Fluent Nhibernate and here you make the queries.

I have read a lot about the great performance gain when using the Lazyload, however, I can’t think, nor imagine, how I could use it without disturbing the architecture. Because, my HMI does not have access to the DAL layer session.

Imagine the following situation:

Function of the BLL getVendas() asks for all sales to the DAL layer. The HMI picks up the result and displays sales schedules. When the user selects a time, he 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?

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

EDIT Tela de visualização de vendas

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
                );
        }
    }
  • I have a question: are you accepting answers that focus on changing DAL technology? I ask this because the Entity Framework is not based on the concept of data sessions. That is, the data is only loaded when the aggregate object is actually used.

  • I do, but I’m very concerned about the other layers, they can’t (shouldn’t) change.

  • @Lizard, your doubt would be at the time of the time the schedules click have to open again?

  • @Harrypotter not necessarily, but it’s an affair. It turns out that if I open a new Ssion, it doesn’t work (unless I’ve performed some wrong procedure). Anyway, if you have any solution for all situations (not just this one) put (:

  • 1

    @Lizard so I asked, because, it will depend a lot on the way you implemented this screen, or even the classes, how I work with patterns for me does not need to change your repository in Ibernate (although the Entity Framework is the best solution)What I need to see honestly was parts of your code.

  • 1

    @Harrypotter I would very much like to know your proposal, because of my little experience with this type of tools. What do you suggest? That I post screen image and code?

  • You can put both!!!

  • 1

    @Harrypotter Done, what do you think?

  • is the way to do it in my mind, because I would do it differently and I would not close the session right away, I would work with addiction injection and solve the part of Sesssion, what I could do is a simple example of the way this screen !

  • @Harrypotter If it wouldn’t be in your way and you’re not too busy, I’d love to (but it would change the layers a lot?

  • @Lizard I’ll propose you something else then you can decide ok!

  • 1

    @Lizard if you need the source code send me an e-mail [email protected] that I send the full example more ta ai as I would reply!

  • 1

    @Harrypotter I already sent the e-mail (:

  • I’ll send you the zip with those changes

  • Perhaps a simple and practical suggestion would be to use Yield. http://msdn.microsoft.com/pt-br/library/9k7k7cf0.aspx

Show 10 more comments

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; }
}

RepositoryCliente e RepositoryTelefone

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 on Gridview their respective Phones

Form

inserir a descrição da imagem aqui

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 close when the form closes. Also a little code point that does a lot of stuff on form, and the important 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();

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

  • 1

    I really enjoyed it and I will join. Not that the proposal of @Gypsy Morrison Mendez is bad, is great, even intend to use in the future. But for now what will serve is your proposal. I would just like to ask about three things. Is the form destructor mandatory? Doesn’t your Connection class run the risk of having many sessions open at the same time? In the Connection class I would advise a method Dispose(), or just Close() is enough?

  • Blz, it is not. Mandatory, realize that you can inject in several repositories I will put an example in the answer, I will implement Dispose for you.

  • I would also use the idea with Ef

2

nhibernate is not good for load of data on demand the way you want it, which Entity Framework does well.

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

  • Performs a database query bringing only the first level of data (i.e., objects of the type Models.Venda;
  • The collections of Models.Produto of each sale each receive a Dynamic Proxy, ie an object that implements ICollection, but it’s not exactly a ICollection.
  • Only when HMI accesses the product collection of each sale are the data effectively loaded. In this case, the Entity Framework replaces the DynamicProxy by the effective collection of products.

nhibernate creates a complicator for the programmer, which is having to control the data session. It’s the same Java Hibernate problem, where each DAO event requires the programmer to indicate which entities will be effectively loaded, and how (Lazy or ager).

Consider switching the Framework so it doesn’t hurt 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 it deems necessary. Then just mark the methods of your BLL with the Attribute of this ActionFilter that the data session will be opened during the execution of the method.

  • Just taking advantage, because I’ve never met EF, does it work similar to nhibernate? In the sense of one class for property and another for mapping? I saw that this EF is from microsoft, you can use it with a Mysql database?

  • 1

    It works pretty much the same. Mapping doesn’t need it: it’s simpler to set up. It has this introductory article in English: http://www.devmedia.com.br/usando-mysql-com-ado-net-entity-framework-4-parte-i/21667. It is a bit old, but it is a good starting point.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.