简体   繁体   中英

NHibernate RowCountInt64 returns wrong count with transformed query

I am experiencing a weird error while executing an NHibernate Query. I have a query of type IQueryOver<ExternalUser, ExternalUser> which is filtered and transformed (using DistinctRootEntity, which i am guessing is causing the problem). I create the query like this:

List<Guid> companyList = /* some guids */
Company company = null;
var query = _session.QueryOver<ExternalUser>()
                    .JoinAlias(x => x.Companies, () => company)
                    .Where(() => company.Id.IsIn(companyList))
                    .TransformUsing(Transformers.DistinctRootEntity);

When i execute query.RowCountInt64() i get 4.

When i execute query.List() i get 3 items back.

I have also tried query.ToRowCountInt64Query().List<long>().Sum() which also gives me 4.

I also tried query.ToRowCountInt64Query().FutureValue<long>().Value which also gives me 4.

Any ideas how to solve this?

I found a solution that works:

totalCount = query.Clone()
                  .Select(Projections.CountDistinct<User>(x => x.Id))
                  .SingleOrDefault<int>();

...but my solution restricts me to Int32 which i'm not happy about. It will probably suffice in the implementation where i'm using it, but there might be cases elsewhere where long is needed, so any other suggestions are appreciated.

EDIT: The only thing i didn't like with the solution above was that it returned an int, so with some digging, i managed to fix that with another projection class:

public class Int64CountProjection : CountProjection
{
    protected internal Int64CountProjection(string prop) : base(prop) {}
    protected internal Int64CountProjection(IProjection projection) : base(projection) {}

    public override IType[] GetTypes(ICriteria criteria, ICriteriaQuery criteriaQuery)
    {
        return new IType[] { NHibernateUtil.Int64 };
    }

    public static CountProjection Distinct<T>(Expression<Func<T, object>> expression)
    {
        return new Int64CountProjection(ExpressionProcessor.FindMemberExpression(expression.Body)).SetDistinct();
    }
}

...and with this class i can get my count like this (which could be refined further with an extension method, but this is enough for me):

totalCount = query.Clone()
                  .Select(Int64CountProjection.Distinct<User>(x => x.Id))
                  .SingleOrDefault<long>();

EDIT #2 I couldn't leave well alone, so i implemented an extension method aswell:

     public static long CorrectRowCount<TRoot>(this IQueryOver<TRoot> query) where TRoot : IEntity
     {
         return query.Clone()
                     .Select(Int64CountProjection.Distinct<TRoot>(x => x.Id))
                     .ClearOrders()
                     .Skip(0)
                     .Take(RowSelection.NoValue)
                     .SingleOrDefault<long>();
     }

The problems seems be Transformers.DistinctRootEntity is natural you get 4 in row count, and 3 on a list. The DistinctRootEntity works when the query has been executed retuning 4 rows and removing duplicated in memory.

You can use query.RowCountInt64() with the correct query.

//
// Summary:
//     Clones the QueryOver, removes orders and paging, and projects the row-count
//     (Int64) for the query
IQueryOver<TRoot, TRoot> ToRowCountInt64Query();

//
// Summary:
//     Short for ToRowCountInt64Query().SingleOrDefault<long>()
long RowCountInt64();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM