How to access properties of arguments when creating custom NHibernate HQL generator
I have the following custom type with a method on it to determine if one time span overlaps another
public struct DateTimeSpan
{
public DateTime? Start { get; set; }
public DateTime? End { get; set; }
public bool Overlaps(DateTimeSpan overlap)
{
//....
}
}
I am trying to write my own HQL generator, so when I use this method in my LINQ queries to access data, it will generate the appropriate SQL when I query the database.
This is the beginning of mine BaseHqlGeneratorForMethod
, which tries to compare the property of End
one DateTimeSpan
to another
public class DateSpanOverlapsDateTimeSpanHqlGenerator : BaseHqlGeneratorForMethod
{
public DateSpanOverlapsDateTimeSpanHqlGenerator()
{
SupportedMethods = new[]
{
ReflectionHelper.GetMethodDefinition<DateTimeSpan>(x => x.Overlaps(new DateTimeSpan()))
};
}
public override HqlTreeNode BuildHql(MethodInfo method, Expression targetObject, ReadOnlyCollection<Expression> arguments, HqlTreeBuilder builder, IHqlExpressionVisitor visitor)
{
var endTargetProperty = ReflectionHelper.GetProperty<DateTimeSpan, DateTime?>(x => x.End);
Expression endTargetExpression = Expression.MakeMemberAccess(targetObject, endTargetProperty);
var endArgumentProperty = ReflectionHelper.GetProperty<DateTimeSpan, DateTime?>(x => x.End);
Expression endArgumentExpression = Expression.MakeMemberAccess(arguments[0], endArgumentProperty);
return builder.GreaterThanOrEqual(visitor.Visit(endTargetExpression).AsExpression(), visitor.Visit(endArgumentExpression).AsExpression());
}
}
I've proven that property End
in targetObject
is evaluating perfectly, but no matter what I do, I can't get it to evaluate property End
in arguments[0]
. The above code is just one example of what I have tried (and seems most obvious if it works for targetObject
), with most of the things I try to do with the exceptionAntlr.Runtime.NoViableAltException
The obvious difference between targetObject
and arguments[0]
is what targetObject
is of type PropertyExpression
and what arguments[0]
is of type ConstantExpression
. I'm guessing this means there must be different ways to access them, but I can't figure out what that is!
source to share
treeBuilder.Constant (arg.SubProperty); will cache your object so you have problems executing the request.
you have to add an overload to your DateTimeSpan
public bool Overlaps(DateTime? start, DateTime? end)
matches the signature in your DateSpanOverlapsDateTimeSpanHqlGenerator
SupportedMethods = new[]
{
ReflectionHelper.GetMethodDefinition<DateTimeSpan>(span => span.Overlaps(default(DateTime?), default(DateTime?)))
};
and get the values like this:
public override HqlTreeNode BuildHql(MethodInfo method, Expression targetObject, ReadOnlyCollection<Expression> arguments, HqlTreeBuilder builder, IHqlExpressionVisitor visitor)
{
var startProperty = ReflectionHelper.GetProperty<DateTimeSpan, DateTime?>(x => x.Start);
var endProperty = ReflectionHelper.GetProperty<DateTimeSpan, DateTime?>(x => x.End);
MemberExpression targetStartExpression = Expression.MakeMemberAccess(targetObject, startProperty);
MemberExpression targetEndExpression = Expression.MakeMemberAccess(targetObject, endProperty);
HqlExpression startDateExpression = visitor.Visit(arguments[0]).AsExpression();
HqlExpression endDateExpression = visitor.Visit(arguments[1]).AsExpression();
....
}
after that, as usual:
builder.LessThanOrEqual(visitor.Visit(targetStartExpression).AsExpression(), endDateExpression)
source to share
I don't know if you resolved this or were still interested in the answer, however I stumbled upon this question myself while looking for a solution to this exact problem, so I thought I'd add my solution for others.
While debugging, I noticed that the target parameter is passed as a type expression PropertyExpression
, whereas my argument was passed as a type ConstantExpression
, so if it's great to add to PropertyExpression
, using Expression.Property
that doesn't work with a constant value.
Instead, I retrieve the constant value and access the sub-property values directly, creating new constant expressions that I can then use to build the query expression.
I don't know if this is the best or most graceful way to do it, but it worked for me. Hope this helps.
public class MyObject
{
public double SubProperty { get; set; }
}
private HqlTreeNode GenerateHQL(
MethodInfo method,
Expression targetObject,
ReadOnlyCollection<Expression> arguments,
HqlTreeBuilder treeBuilder,
IHqlExpressionVisitor visitor)
{
MyObject arg = (MyObject)((ConstantExpression)arguments[0]).Value;
HqlConstant argExpression = treeBuilder.Constant(arg.SubProperty);
...
...
}
source to share