如何在编写自定义NHibernate HQL生成器时访问参数属性

时间:2013-02-19 09:32:56

标签: linq nhibernate hql expression linq-expressions

我有以下自定义类型,其上有一个方法来确定一个时间跨度是否与另一个

重叠
public struct DateTimeSpan
{
    public DateTime? Start { get; set; }
    public DateTime? End { get; set; }

    public bool Overlaps(DateTimeSpan overlap)
    {
        //....
    }
}

我正在尝试编写一个自定义HQL生成器,以便在我的数据访问LINQ查询中使用此方法时,它将在查询数据库时生成适当的SQL。

这是我的BaseHqlGeneratorForMethod尝试比较一个End的{​​{1}}属性与另一个<{p}}的开头

DateTimeSpan

我已经证明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()); } } 中的End属性被评估为正常,但无论我做什么,我都无法评估targetObject中的End属性。上面的代码只是我尝试的一个例子(并且看起来最明显,因为它适用于arguments[0])我尝试以targetObject

为例

Antlr.Runtime.NoViableAltExceptiontargetObject之间的一个明显不同之处是arguments[0]的类型为targetObject,而PropertyExpression的类型为arguments[0]。我认为这意味着需要有不同的方式来访问它们,但我无法弄清楚它是什么!

2 个答案:

答案 0 :(得分:2)

treeBuilder.Constant(arg.SubProperty);将缓存您的对象,以便在运行查询时最终会出现问题。

你应该在DateTimeSpan中添加一个重载

public bool Overlaps(DateTime? start, DateTime? end)

将签名匹配到DateSpanOverlapsDateTimeSpanHqlGenerator

SupportedMethods = new[]
{
ReflectionHelper.GetMethodDefinition<DateTimeSpan>(span => span.Overlaps(default(DateTime?), default(DateTime?)))
};

并以这种方式获取值:

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();
    ....
}

在照常营业之后:

builder.LessThanOrEqual(visitor.Visit(targetStartExpression).AsExpression(), endDateExpression)

答案 1 :(得分:0)

我不知道你是否曾经解决这个问题,或者仍然对答案感兴趣,但是我在寻找这个确切问题的解决方案时偶然发现了这个问题,所以我想我会为其他人添加我的解决方案。

调试时我注意到目标对象参数是作为PropertyExpression类型的表达式传入的,而我的参数是作为ConstantExpression的类型传入的,所以虽然它完全有效添加到使用PropertyExpression的{​​{1}},不能使用常量值。

相反,我提取常量的值并直接访问子属性的值,创建新的常量表达式,然后我可以使用它来构建查询表达式。

我不知道这是否是最佳或最优雅的方式,但它对我有用。我希望它有所帮助。

Expression.Property