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?
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 linq-to-nhibernate without some changes in NHibernate code.