Search code examples
c#nhibernatefluent-nhibernatenhibernate-criteria

How to select top 10 most sold products in NHibernate?


This solution didn't work for me: C#/NHibernate - Get first 10 records ordered by grouped sum

My case is very similar, but I keep receveing the following error message:

Additional information: could not resolve property: Produto_Id of: SistemaVendas.Domain.venda.ProdutoVendido

My code so far:

ICriteria criteria = unitOfWork.Session
            .CreateCriteria<ProdutoVendido>("ProdutoVendido")
            .SetMaxResults(limite)
            .CreateCriteria("Produto_Id")
                .SetProjection(Projections.ProjectionList()
                    .Add(Projections.GroupProperty("Produto_Id"), "ID")
                    .Add(Projections.Sum("ProdutoVendido.Quantidade"), "QuantitySum")
                )
                .AddOrder(Order.Desc("QuantitySum"));

return criteria
            .SetResultTransformer(Transformers.AliasToBean<Produto>())
            .List<Produto>().ToList();

What am I doing wrong?

P.S. ProdutoVendido = SoldProduct and Produto = Product

ProdutoVendido mapping

    public ProdutoVendidoMap()
    {
        Id(x => x.Id).GeneratedBy.Identity();            

        Map(x => x.Quantidade);
        Map(x => x.Desconto);
        Map(x => x.Observacao).Length(ProdutoVendido.MAX_OBSERVACAO_LENGTH);            

        References(x => x.Venda)
            .Cascade.None();
        References(x => x.Produto)
            .Cascade.None();
    }

Edit:

Final solution

I just created this DTO, as @Radim Köhler pointed

public class ProdutoDto
{
    public ulong Id { get; set; }
    public int QuantidadeTotal { get; set; }
}

and my final NHibernate query is

ICriteria criteria = unitOfWork.Session
    .CreateCriteria<ProdutoVendido>("ProdutoVendido")
    .SetMaxResults(limite)            
    .CreateCriteria("Produto", "p")            
        .SetProjection(Projections.ProjectionList()
            .Add(Projections.GroupProperty("p.Id"), "Id")
            .Add(Projections.Sum("ProdutoVendido.Quantidade"), "QuantidadeTotal")                    
        )
        .AddOrder(Order.Desc("QuantidadeTotal"));

var ids = criteria
            .SetResultTransformer(Transformers.AliasToBean<ProdutoDto>())
            .List<ProdutoDto>();

return unitOfWork.Session
    .CreateCriteria<Produto>()                
    .Add(Restrictions.In("Id", ids.Select(x => x.Id).ToArray()))
    .List<Produto>().ToList();

Solution

  • To fix the error returned by NHibernate, we have to query existing Object model. In this case, the sold product (ProdutoVendido) references Produto, not Produto_Id

    ...
    Session
      .CreateCriteria<ProdutoVendido>("ProdutoVendido")
      ...
      // this line is about joining reference
      // not the column
      //.CreateCriteria("Produto_Id")
      .CreateCriteria("Produto", "p") // "p" is now alias which we can use
    

    Check similar working query e.g. here

    EXTEND

    What we would need to be able to project SUM property - is an DTO (not a POCO, just DTO for this purpose). Check this Q & A for inspiration Joining two tables with specific columns in nhibernate

    This could be it:

    public class ProdutoDto
    {
        public virtual int ID { get; set; }
        public virtual decimal QuantitySum { get; set; } // the type should match
    }
    

    and we can use that as a projection target:

    return criteria
            .SetResultTransformer(Transformers.AliasToBean<ProdutoDto>())
            .List<ProdutoDto>().ToList();