其他分享
首页 > 其他分享> > Nhibernate在LINQ查询中转义特殊字符

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