Ehsan
Ehsan

Reputation: 3491

How linq-to-nhibernate between 2 lists by StartsWith

I need to run this query by NHibernate 4 :

var filterList = new List<string> { "1" , "2" } ;

var q = SessionInstance.Query<Book>()
          .Where(x => filterList.Any(s => s.StartsWith(x.Code)));
var list = q.ToList();

But it has exception by this message : Specified method is not supported.

StackTrace :

at NHibernate.Hql.Ast.ANTLR.PolymorphicQuerySourceDetector.GetClassName(IASTNode querySource)
   at NHibernate.Hql.Ast.ANTLR.PolymorphicQuerySourceDetector.Process(IASTNode tree)
   at NHibernate.Hql.Ast.ANTLR.AstPolymorphicProcessor.Process()
   at NHibernate.Hql.Ast.ANTLR.ASTQueryTranslatorFactory.CreateQueryTranslators(IASTNode ast, String queryIdentifier, String collectionRole, Boolean shallow, IDictionary`2 filters, ISessionFactoryImplementor factory)
   at NHibernate.Hql.Ast.ANTLR.ASTQueryTranslatorFactory.CreateQueryTranslators(IQueryExpression queryExpression, String collectionRole, Boolean shallow, IDictionary`2 filters, ISessionFactoryImplementor factory)
   at NHibernate.Engine.Query.QueryPlanCache.GetHQLQueryPlan(IQueryExpression queryExpression, Boolean shallow, IDictionary`2 enabledFilters)
   at NHibernate.Impl.AbstractSessionImpl.GetHQLQueryPlan(IQueryExpression queryExpression, Boolean shallow)
   at NHibernate.Impl.AbstractSessionImpl.CreateQuery(IQueryExpression queryExpression)
   at NHibernate.Linq.DefaultQueryProvider.PrepareQuery(Expression expression, IQuery& query, NhLinqExpression& nhQuery)
   at NHibernate.Linq.DefaultQueryProvider.Execute(Expression expression)
   at NHibernate.Linq.DefaultQueryProvider.Execute[TResult](Expression expression)
   at Remotion.Linq.QueryableBase`1.GetEnumerator()
   at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)

Upvotes: 1

Views: 517

Answers (1)

Radim K&#246;hler
Radim K&#246;hler

Reputation: 123861

This is not implemented, but we can convert such WHERE criteria into OR expression:

var q = SessionInstance
    .Query<Book>()
    //.Where(x => filterList.Any(s => s.StartsWith(x.Code)))
    .Where(x => x.Code.StartsWith("1")
             || x.Code.StartsWith("2")
    )
    ;

Obviously, this is not dynamic. We have to name all OR statements, we cannot use the filterList... Unless we will ask for help the LinqKit

And create a method to automate that. We would need these using statements:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using LinqKit;

namespace MyNamespace
{
    public static partial class Ext
    {
        ...

And the extension itself:

public static Expression<Func<T, bool>> AnyStarts<T>(
    this IList<string> startsWithValues, Expression<Func<T, string>> property)
{
    var nothingToDo = startsWithValues == null
                      || startsWithValues.Count == 0
                      || property == null;
    // nothing to process here, return null
    if (nothingToDo)
    {
        return null;
    }
    // first value ...
    string firstValue = startsWithValues.First();
    Expression<Func<T, bool>> predicate = 
        x => property.Invoke(x).StartsWith(firstValue);
    Expression<Func<T, bool>> result = predicate.Expand();

    if (startsWithValues.Count == 1)
    {
        // first value is enough
        return result;
    }

    // let's append Or if there are many string values
    for (int index = 1; index < startsWithValues.Count; index++)
    {
        string nextValue = startsWithValues[index];

        predicate = x => result.Invoke(x)
                         || property.Invoke(x).StartsWith(nextValue);

        result = predicate.Expand();
    }
    return result;
}

And we can call it like this:

var filterList = new List<string> { "1" , "2" }; // or more

var q = SessionInstance
    .Query<Book>()
    //.Where(x => filterList.Any(s => s.StartsWith(x.Code)))
    .Where(filterList.AnyStarts<Contact>(x => x.Code))
    ;

And we will receive any Book, which Code starts with "1", "2", ....

Upvotes: 1

Related Questions