NHibernate linq匹配搜索值的集合

时间:2015-02-11 16:37:35

标签: .net nhibernate hql linq-to-nhibernate

我正在使用linq来nhibernate来搜索实体名称及其别名。

class Entity
{
    string Name { get; set; }
    string[] Aliases { get; set; }
}

enityQueryable.Where(x =>
    x.Name.StartsWith(searchParam) ||
    x.Aliases.Any(a => a.StartsWith(searchParam)));

那部分工作正常。

我现在要求匹配可能的搜索字词列表。我可以在linq中进行查询,但是正如预期的那样,Nhibernate无法将其转换为Hql。

enityQueryable.Where(x => MatchOnNameOrAlias(x));

private bool MatchOnNameOrAlias(Entity e, string[] searchTerms)
{
    foreach (var searchTerm in searchTerms)
    {
        if (e.Name.StartsWith(searchTerm))
        {
            return true;
        }

        if (e.Aliases.Any(a => a.StartsWith(companySearchTerm)))
        {
            return true;
        }
    }
    return false;
}

我开始考虑使用LinqToHqlGenerator,实施是相对直接的,并且在第一次检查时似乎有效但是它只在第一次工作。后续调用会重复使用相同的搜索参数集合。

public class MatchesAnySearchTermGenerator : BaseHqlGeneratorForMethod
{
    public MatchesAnySearchTermGenerator()
    {
        SupportedMethods = new[] { ReflectionHelper.GetMethod(() => SearchLinqExtensions.MatchesAnySearchTerm(null, null)) };
    }

    public override HqlTreeNode BuildHql(MethodInfo method, Expression targetObject, ReadOnlyCollection<Expression> arguments,             HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor)
    {
        var likes = ((IEnumerable<string>)((ConstantExpression) arguments[1]).Value).ToArray();

        HqlBooleanExpression lastBooleanExpression = CreateHqlLike(treeBuilder, visitor.Visit(arguments[0]).AsExpression(), likes[0]);

        for (int i = 1; i < likes.Length; i++)
        {
            lastBooleanExpression = treeBuilder.BooleanOr(lastBooleanExpression,
                CreateHqlLike(treeBuilder, visitor.Visit(arguments[0]).AsExpression(), likes[i]));
        }
        return lastBooleanExpression;
    }

    private HqlLike CreateHqlLike(HqlTreeBuilder treeBuilder, HqlExpression nameExpression, string like)
    {
        return treeBuilder.Like(nameExpression, treeBuilder.Constant(like + '%'));
    }
}

这似乎是一个已知问题。

StackOverflow Question

NHibernate Jira Issue

因此,无需直接使用NHibernate来执行查询并在我的代码库中维护对IQueryable的依赖。是否有Nhibernate支持的第一个查询的替代方法,或者我是否可以构造HqlGenerator以使其不会缓存第一个搜索项列表?

1 个答案:

答案 0 :(得分:0)

我有一个满足我需求的解决方案。它不使用Hql Generator方法。相反,它构建了linq查询,以便Nhibernate可以将其转换为Hql本身。以下链接有助于此,第二个实际上是第一个如果您阅读评论的改进。

NHibernate Linq provider: dynamic filtering using lambda expressions

Dynamically built LINQ query no longer working in NH3.0

我能够做到以下几点:

Expression<Func<Entity, bool>> predicate = null;

predicate = searchTerms.Aggregate(predicate, (current, source1) => current == null ? NameStartsWith(source1) : current.Or(NameStartsWith(source1)));

enityQueryable.Where(predicate);

private static Expression<Func<Entity, bool>> NameStartsWith(string searchTerm)
{
    return p => p.Name.ToLower().StartsWith(searchTerm) || p.Aliases.Any(x => x.StartsWith(searchTerm));
}

使用两个链接中提到的方法:

static class PredicateBuilder
{
    public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
    {
        var adaptedExpr2Body = ReplacingExpressionTreeVisitor.Replace(
            expr2.Parameters[0],
            expr1.Parameters[0],

            expr2.Body);
        return Expression.Lambda<Func<T, bool>>(
            Expression.AndAlso(expr1.Body, adaptedExpr2Body),
            expr1.Parameters);
    }
    public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
    {
        var adaptedExpr2Body = ReplacingExpressionTreeVisitor.Replace(
            expr2.Parameters[0],
            expr1.Parameters[0],

            expr2.Body);
        return Expression.Lambda<Func<T, bool>>(
            Expression.OrElse(expr1.Body, adaptedExpr2Body),
            expr1.Parameters);
    }
}