Nhibernate在LINQ查询中转义特殊字符
作者:互联网
我有一张像这样的桌子:
TABLE_XY
|ID |SOURCE|
|1 |value_aa|
|2 |other_aa|
|3 |eeeaa|
生成的查询应为:
select * from TABLE_XY where SOURCE like '%\_aa' ESCAPE '\'
我知道有两个选项可以满足我的需求
var result =
session.QueryOver<TableXy>()
.WhereRestrictionOn(x => x.Source)
.IsLike("%\_aa", MatchMode.Exact, '\\')
.List();
要么
var result2 = session
.CreateCriteria<TableXy>()
.Add(LikeExpression("Source", "%\\_aa", MatchMode.Exact, '\\', false))
.List();
但是我必须使用基于Linq的实现.我正在使用动态创建的表达式树,有时会使用Linq to Object Provider或Linq to Nhibernate执行表达式树.
但目前仅支持此方法:
var result = session
.Query<TableXy>()
.Where(x => NHibernate.Linq.SqlMethods.Like(x.Source, "%\\_aa"))
.ToList();
如何扩展Nhibernate Linq提供程序以支持?
SqlMethods.IsLike(string source, string pattern, char? escape);
解决方法:
好的,这是一个相当复杂的答案,并且可能存在问题,但是我能够使逃生装置正常工作.
这涉及几个步骤,但是基本上我们正在做的是添加一种新型的HqlTreeNode,它可以处理like运算符的转义部分.
>创建将在LINQ查询中使用的扩展方法.此方法不需要实现-我们稍后会提供:
public static class LinqExtensions
{
public static bool IsLikeWithEscapeChar(
this string input,
string like,
char? escapeChar)
{
throw new NotImplementedException();
}
}
>创建一个HqlEscape树节点,我们将用它来表示like运算符的转义部分:
public class HqlEscape : HqlExpression
{
public HqlEscape(IASTFactory factory, params HqlTreeNode[] children)
: base(HqlSqlWalker.ESCAPE, "escape", factory, children)
{
}
}
>创建一个HqlLikeWithEscape树节点.默认的HqlLike节点无法处理转义部分,因此我们需要创建一个可以处理三个子节点的新节点:
public class HqlLikeWithEscape : HqlBooleanExpression
{
public HqlLikeWithEscape(IASTFactory factory, HqlExpression lhs, HqlExpression rhs, HqlEscape escape)
: base(HqlSqlWalker.LIKE, "like", factory, lhs, rhs, escape)
{
}
}
>为我们之前定义的IsLikeWithEscapeChar扩展方法创建一个生成器.此类的职责是获取调用方法所用的信息,并返回最终将被转换为SQL的HQL树结构:
public class CustomLikeGenerator : BaseHqlGeneratorForMethod
{
public CustomLikeGenerator()
{
this.SupportedMethods = new[]
{
ReflectionHelper.GetMethodDefinition(
() => LinqExtensions.IsLikeWithEscapeChar(null, null, null))
};
}
public override HqlTreeNode BuildHql(
MethodInfo method,
System.Linq.Expressions.Expression targetObject,
ReadOnlyCollection<System.Linq.Expressions.Expression> arguments,
HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor)
{
// Is there a better way to do this?
var factory = new ASTFactory(new ASTTreeAdaptor());
HqlTreeNode escapeCharNode = visitor.Visit(arguments[2]).AsExpression();
var escapeNode = new HqlEscape(factory, escapeCharNode);
HqlLikeWithEscape likeClauseNode =
new HqlLikeWithEscape(
factory,
visitor.Visit(arguments[0]).AsExpression(),
visitor.Visit(arguments[1]).AsExpression(),
escapeNode);
return likeClauseNode;
}
}
如您所见,我们利用了我们先前定义的新HQL树节点.这种方法的主要缺点是需要我手动创建ASTFactory和ASTTreeAdaptor.这些类的用法通常封装在HqlTreeBuilder中,但是HqlTreeBuilder并不适合于被子类化.如果有人提出建议,将不胜感激.
>创建一个新的LINQ to HQL生成器注册表.此类仅将我们的扩展方法与我们在步骤4中提供的HQL实现相关联:
public class LinqToHqlGeneratorsRegistry : DefaultLinqToHqlGeneratorsRegistry
{
public LinqToHqlGeneratorsRegistry() : base()
{
RegisterGenerator(
ReflectionHelper.GetMethodDefinition(() => LinqExtensions.IsLikeWithEscapeChar(null, null, null)),
new CustomLikeGenerator());
}
}
>更新您的配置以使用新的LinqToHqlGeneratorsRegistry:
cfg.LinqToHqlGeneratorsRegistry<LinqToHqlGeneratorsRegistry>();
>(最后)在查询中使用新的扩展方法:
session.Query<Person>().Where(p => p.FirstName.IsLikeWithEscapeChar("%Foo", '\\'))
请注意,您需要指定通配符.可以对此进行平滑处理,但是这样做并不难.
这是我第一次以这种方式扩展HQL,因此,此解决方案可能会出现问题.我只能在SQL Server上进行测试,但是我有理由相信,它可以创建与HQL查询相同的树结构,因此它应该可以工作.
标签:nhibernate,linq,sql,c,fluent-nhibernate 来源: https://codeday.me/bug/20191121/2049523.html