Search code examples
c#linqnhibernatehql

Generating a method for linq in NHibernate for avg timespan


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?


Solution

  • 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.