2
votes

The goal is to do something like this using NHibernate and Linq:

Session.Query<MyClass>().Where(x => x.DateGreaterThen(DateTime.Now))

This example is a little bit simplified, but the idea is that in Where predicate I would like to call MyClass method. So MyClass looks like:

public class MyClass
{
    public static readonly Expression<Func<MyClass, DateTime, bool>> GreaterThenExpression =
        (x, dt) => x.MyDateTimeProperty > dt.Date;

    private static readonly Func<MyClass, DateTime, bool> GreaterThenFunc = GreaterThenExpression.Compile();

    public Guid Id { get; set; }

    public DateTime MyDateTimeProperty { get; set; }

    public bool DateGreaterThen(DateTime dt)
    {
        return GreaterThenFunc(this, dt);
    }
}

Custom generator:

public class EntityMethodGenerator<T1, T2, TResult> : BaseHqlGeneratorForMethod
{
    private Expression<Func<T1, T2, TResult>>  _returnValueExpression;

    public EntityMethodGenerator()
    {
        SupportedMethods = new[]
                               {
                                   ReflectionHelper.GetMethodDefinition<MyClass>(myClass => myClass.DateGreaterThen(DateTime.Now))
                               };
    }

    public static void Register(ILinqToHqlGeneratorsRegistry registry, Expression<Action<T1>> method, Expression<Func<T1, T2, TResult>> returnValueExpression)
    {
        var generator = new EntityMethodGenerator<T1, T2, TResult> { _returnValueExpression = returnValueExpression };

        registry.RegisterGenerator(ReflectionHelper.GetMethodDefinition(method), generator);
    }

    public override HqlTreeNode BuildHql(
        MethodInfo method,
        Expression targetObject,
        ReadOnlyCollection<Expression> arguments,
        HqlTreeBuilder treeBuilder,
        IHqlExpressionVisitor visitor)
    {
        return visitor.Visit(_returnValueExpression);
    }
}

And finally custom custom generators registry:

public class OwnLinqToHqlGeneratorsRegistry : DefaultLinqToHqlGeneratorsRegistry
{
    public OwnLinqToHqlGeneratorsRegistry()
    {
        EntityMethodGenerator<MyClass, DateTime, bool>.Register(this, (myClass) => myClass.DateGreaterThen(DateTime.Now), MyClass.GreaterThenExpression);
    }   
}

But this is not working, I am getting System.Data.SqlClient.SqlException : Invalid column name 'dt'. I am suspecting that my BuildHql method is not implemented correctly, any help with fixing this?

By the way, I would like to use expressions in generator, like my GreaterThenExpression rather than building HqlTree manually in BuildHql method

1

1 Answers

1
votes

GreaterThenExpression is a lamba. In BuildHql() you need to also take notice of the targetObject and the arguments, otherwise you are just inserting some HQL conversion of the unapplied lambda in the HQL tree.

You need to extract the body of the lambda and replace its parameters with the targetObject and arguments. Then you can generate HQL from that. Try using Remotion.Linq.Parsing.ExpressionTreeVisitors.MultiReplacingExpressionTreeVisitor.

(On a side note, it should be GreaterThan, not GreaterThen.)