1

I'm trying to make avg for timespan work in linq for NHibernate.

I've added registry:

public class CustomLinqToHqlGeneratorsRegistry : DefaultLinqToHqlGeneratorsRegistry
{
    public CustomLinqToHqlGeneratorsRegistry()
    {
        this.Merge(new AvgTimeSpanGenerator());
    }
}

Generator:

public class AvgTimeSpanGenerator : BaseHqlGeneratorForMethod
{
     public AvgTimeSpanGenerator()
    {
        SupportedMethods = new[]
        {
             NHibernate.Linq.ReflectionHelper.GetMethodDefinition<IEnumerable<TimeSpan>>(x => x.Avg())
        };
    }

    public override HqlTreeNode BuildHql(MethodInfo method, Expression targetObject, ReadOnlyCollection<Expression> arguments, HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor)
    {
        return treeBuilder.MethodCall("Avg", visitor.Visit(targetObject).AsExpression());
    }
}

Please note that my BuildHql implementation might be incorrect (haven't really thought it through much), but I placed breakpoint there and it is never hit.

And a method:

public static class NHibernateLinqExtension
{
    public static TimeSpan Avg(this IEnumerable<TimeSpan> timeSpan)
    {
        return new TimeSpan((long)timeSpan.Select(x => x.Ticks).Average());
    }
}

I've also registered the registry in NHibernate:

configuration.LinqToHqlGeneratorsRegistry<CustomLinqToHqlGeneratorsRegistry>();

But when I execute a query:

var data = (from tcdr in uow.UnitOfWorkSession.Query<TCDR>()
                group tcdr.Duration by tcdr.Name into g
                select new
                {
                    MaxDuration = g.Max(),
                    MinDuration = g.Min(),
                    AvgDuration = g.Avg()
                }).ToList();

It throws InvalidOperationException Code supposed to be unreachable

Any clue?

Archeg
  • 7,785
  • 5
  • 34
  • 81

1 Answers1

1

Aggregating expression requires a special handling, see MergeAggregatingResultsRewriter. Unfortunately, this class is not injectable with more behavior, and the rewriter pipeline calls custom rewriters (see setting query.query_model_rewriter_factory) too late (line 72), since aggregations needs to be handled before the group by rewriters (line 39).

All this applies to NHibernate 5.1.2.

It seems to me you are using a version lower than the 5, but your are likely stuck the same.

I do not think you may be able to handle your custom TimeSpan average in without some changes in NHibernate code.

Frédéric
  • 8,372
  • 2
  • 51
  • 102
  • I wish `linq-to-nhibernate` was more flexible and easy to extend. I've started a new project where I have a lot of complex queries, and almost every query I write I end up rewriting to `HQL` either because it is impossible to write in `LINQ` or the possible approach is not efficient. Thanks for your response though! – Archeg May 30 '18 at 07:37