4

I want to extend the default LINQ provider for NHibernate 3 with methods of my own. I want to be able to use some methods from my POCOs. I have a component named Range which is used quite often in many of my POCOs. This nhibernate component class has a method Contains(int value) that I want to use in LINQ query expressions

Mapping:

<class name="Foo">
  ...
  <component name="AgeRange">
    <property name="Min" column="age_min" />
    <property name="Max" column="age_max" />
  </component>
</class>

Class

public class Range {
  public int Min { get; set; }
  public int Max { get; set; }

  public bool Contains(int value) {
    return value >= this.Min && value <= this.Max;
  }
}

// this is the LINQ query I want to be able to write
// which will generate 'SELECT * FROM Foo WHERE 25 BETWEEN age_min AND age_max'
var s = from x in session.Query<Foo> where x.AgeRange.Contains(25) select x;

// I know the following works
var s = from x in session.Query<Foo> where x.AgeRange.Min <= 25 && x.AgeRange.Max >= 25 select x;

I looked at several blog posts explaining how to extend the LINQ provider but I don't know how to build the expressions required for this to work.

public class RangeContainsGenerator : BaseHqlGeneratorForMethod
{
    public MemberInfo RangeMin;
    public MemberInfo RangeMax;

    public RangeContainsGenerator() {
        SupportedMethods = new[] { 
            ReflectionHelper.GetMethodDefinition<Range>(x=> x.Contains(0)),
        };

        RangeMin = ReflectionHelper.GetProperty<Range, int>(x => x.Min);
        RangeMax = ReflectionHelper.GetProperty<Range, int>(x => x.Max);
    }

    public override NHibernate.Hql.Ast.HqlTreeNode BuildHql(
        System.Reflection.MethodInfo method, 
        System.Linq.Expressions.Expression targetObject, 
        System.Collections.ObjectModel.ReadOnlyCollection<System.Linq.Expressions.Expression> arguments, 
        NHibernate.Hql.Ast.HqlTreeBuilder treeBuilder, 
        NHibernate.Linq.Visitors.IHqlExpressionVisitor visitor) {
            // The targetObject parameter contains the "Foo.AgeRange" member access expression
            throw new NotImplementedException(); 
    }
}

In the BuildHql method I don't know how to access Min and Max properties of my Range class to build a HqlTreeNode

Vasea
  • 5,043
  • 2
  • 26
  • 30
  • did you ever get this worked out? I have the same problem. – BenCr Apr 05 '11 at 15:22
  • @BenCr, I never got this worked out - I had only 2 places where I would have used this extesion to LINQ. Sorry – Vasea Apr 07 '11 at 14:42
  • I changed my extension method to a static method which accepts multiple parameters instead of an object. It worked but the SQL that the NHibernate 3 LINQ provider generates isn't particularly optimal so I've abandoned it in favour of the QueryOver api at the moment. This is probably less performant than the LINQ sql so will probably be replaced with raw HQL. – BenCr Apr 07 '11 at 14:47
  • @BenCr, named hql queries are my favourite now when trying to do a more complex query – Vasea Apr 07 '11 at 14:57
  • It looks like this [answer to another question](/a/21060461/1178314) sorts it out in its updated part. In its first version (beginning of answer) field name was hard-coded, in its second version (end of answer), it is resolved from entity property. – Frédéric Mar 11 '16 at 22:40

2 Answers2

2
  1. You can a manual approach: the easiest way is to create a LINQ tree that represents what you want: arguments[0] >= targetObject.Min && arguments[1] <= targetObject.Max. Here >= is Expression.GreaterThenOrEqual, . is Expression.Property and so on.

    When you have an expression tree, just apply visitor to it and return what it returns (I do not remember the exact API, but I can look into it if additional help is needed).

  2. Another solution may be to try my little library: Expressive.
    It attempts to convert method IL into expressions, so you could do a LinqToHqlGeneratorsRegistry or IRuntimeMethodHqlGenerator that tries to inline any unknown property/method.

Community
  • 1
  • 1
Andrey Shchekin
  • 21,101
  • 19
  • 94
  • 162
1

There are a few good examples of extending the LINQ provider here: http://www.primordialcode.com/

Diego Mijelshon
  • 52,548
  • 16
  • 116
  • 154
  • do you have a concrete answer for the question as I have a smiliar question now 6 months down the line. None of the examples I could find on that site actually answer the OPs question. – BenCr Apr 05 '11 at 15:10