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 来执行查询并在我的代码库中维护对 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);
}
}
我正在使用 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 来执行查询并在我的代码库中维护对 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);
}
}