NHibernate linq 匹配一组搜索值

NHibernate linq match against a collection of search values

我正在使用 linq to 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 + '%'));
    }
}

这似乎是一个已知问题。

Whosebug Question

NHibernate Jira Issue

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

我有满足我需求的解决方案。它不使用 Hql 生成器方法。相反,它构建了 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);
    }
}