3

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

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

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

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

这是我BaseHqlGeneratorForMethod尝试将一个与另一个的End属性进行比较的开始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.NoViableAltException

targetObject和之间的一个明显区别arguments[0]是它targetObject是类型PropertyExpressionarguments[0]是类型ConstantExpression。我认为这意味着需要有不同的方法来访问它们,但我无法弄清楚它是什么!

4

2 回答 2

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)
于 2014-03-06T11:28:06.713 回答
0

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

在调试时,我注意到目标对象参数作为类型的表达式传入PropertyExpression,而我的参数作为类型传入ConstantExpression,所以虽然添加到PropertyExpressionusing是完全有效的,但它Expression.Property不适用于常量值。

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

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

 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);

    ...
    ...
}
于 2013-06-07T12:39:41.587 回答