如果后面出现Skip(.)LINQ部分,则XML字段上的自定义SQLFunctionTemplate会导致转义错误

本文关键字:自定义 字段 SQLFunctionTemplate 错误 转义 XML Skip 部分 LINQ 如果 | 更新日期: 2023-09-27 17:59:36

我创建了一个自定义的SQLFunctionTemplate来查询特定名称/值对的XML字段:

RegisterFunction("_ExistInCaratteristiche", 
                new SQLFunctionTemplate(NHibernateUtil.Boolean, 
                    "?1.exist('/L/I/C[N=sql:variable('"?2'") and V=sql:variable('"?3'")]') = 1"));

我这样使用:

private IQueryable<MyEntity> _xmlFilter(IQueryable<MyEntity> input, string element, string value)
        {
            if (String.IsNullOrEmpty(element) || String.IsNullOrEmpty(value))
                return input;
            return input.Where(m => m.XMLField.XMLContains(element.ToUpper(),value));
        }

所以,我可以做:

..
IQueryable<MyEntity> result = Session.Query<MyEntity>();
            result = _xmlFilter(result, filter.element, filter.value);
            return result.ToList();    
..

工作正常

不幸的是,如果我在这个自定义过滤器后面添加一个跳过LINQ部分,比如:

..
IQueryable<MyEntity> result = Session.Query<MyEntity>();
            result = _xmlFilter(result, filter.element, filter.value);
            result = result.Skip(3);
            return result.ToList();    
..

当结果出来时,我得到以下错误。ToList()被调用:

对于带引号的文本,找不到终止字符"

我想我面临着一个逃避的问题,但我真的不知道为什么只有添加Skip LINQ部分才会发生这种情况。我在SQLFunctionTemplate中尝试了各种引号和单引号的组合,但没能解决这个问题。

根据Oskar Berggren的请求,以下是异常的完整堆栈跟踪,一旦nhibernate尝试将iqueryable转换为适当的查询(即调用.ToList()时),就会抛出该异常

NHibernate.Exceptions.SqlParseException: Cannot find terminating ''' character for quoted text.
   at NHibernate.SqlCommand.Parser.SqlParserUtils.ReadDelimitedText(String text, Int32 maxOffset, Int32 offset)
   at NHibernate.SqlCommand.Parser.SqlTokenizer.<GetEnumerator>d__0.MoveNext()
   at NHibernate.SqlCommand.Parser.SqlTokenizerExtensions.TryParseUntil(IEnumerator`1 tokenEnum, String keyword)
   at NHibernate.SqlCommand.Parser.SqlTokenizerExtensions.TryParseUntilFirstOrderColumn(IEnumerator`1 tokenEnum, SqlToken& orderToken)
   at NHibernate.SqlCommand.Parser.MsSqlSelectParser..ctor(SqlString sql)
   at NHibernate.Dialect.MsSql2005DialectQueryPager.PageByLimitAndOffset(SqlString offset, SqlString limit)
   at NHibernate.Dialect.MsSql2005DialectQueryPager.PageBy(SqlString offset, SqlString limit)
   at NHibernate.Dialect.MsSql2005Dialect.GetLimitString(SqlString queryString, SqlString offset, SqlString limit)
   at NHibernate.Dialect.Dialect.GetLimitString(SqlString queryString, Nullable`1 offset, Nullable`1 limit, Parameter offsetParameter, Parameter limitParameter)
   at NHibernate.Hql.Ast.ANTLR.SqlGenerator.GetSqlStringWithLimitsIfNeeded(QueryWriter queryWriter)
   at NHibernate.Hql.Ast.ANTLR.SqlGenerator.EndQuery()
   at NHibernate.Hql.Ast.ANTLR.SqlGenerator.selectStatement()
   at NHibernate.Hql.Ast.ANTLR.SqlGenerator.statement()
   at NHibernate.Hql.Ast.ANTLR.HqlSqlGenerator.Generate()
   at NHibernate.Hql.Ast.ANTLR.QueryTranslatorImpl.DoCompile(IDictionary`2 replacements, Boolean shallow, String collectionRole)
   at NHibernate.Hql.Ast.ANTLR.QueryTranslatorImpl.Compile(IDictionary`2 replacements, Boolean shallow)
   at NHibernate.Hql.Ast.ANTLR.ASTQueryTranslatorFactory.CreateQueryTranslators(IASTNode ast, String queryIdentifier, String collectionRole, Boolean shallow, IDictionary`2 filters, ISessionFactoryImplementor factory)
   at NHibernate.Hql.Ast.ANTLR.ASTQueryTranslatorFactory.CreateQueryTranslators(IQueryExpression queryExpression, String collectionRole, Boolean shallow, IDictionary`2 filters, ISessionFactoryImplementor factory)
   at NHibernate.Engine.Query.QueryExpressionPlan.CreateTranslators(IQueryExpression queryExpression, String collectionRole, Boolean shallow, IDictionary`2 enabledFilters, ISessionFactoryImplementor factory)
   at NHibernate.Engine.Query.QueryExpressionPlan..ctor(IQueryExpression queryExpression, Boolean shallow, IDictionary`2 enabledFilters, ISessionFactoryImplementor factory)
   at NHibernate.Engine.Query.QueryPlanCache.GetHQLQueryPlan(IQueryExpression queryExpression, Boolean shallow, IDictionary`2 enabledFilters)
   at NHibernate.Impl.AbstractSessionImpl.GetHQLQueryPlan(IQueryExpression queryExpression, Boolean shallow)
   at NHibernate.Impl.AbstractSessionImpl.CreateQuery(IQueryExpression queryExpression)
   at NHibernate.Linq.DefaultQueryProvider.PrepareQuery(Expression expression, IQuery& query, NhLinqExpression& nhQuery)
   at NHibernate.Linq.DefaultQueryProvider.Execute(Expression expression)
   at NHibernate.Linq.DefaultQueryProvider.Execute[TResult](Expression expression)
   at Remotion.Linq.QueryableBase`1.GetEnumerator()
   at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
   at TRIM.Chibro.Services.Base.Operations.ListOdPOperation.Execute(ListOdPDto filter) in c:'Progetti.SVN.Cloud'Chibro'ChibroMES'src'TRIM.Chibro.Services'Base'Operations'ListOdPOperation.cs:line 30
   at TRIM.Chibro.Services.Implementations.ListOdPService.Execute(ListOdPDto filter) in c:'Progetti.SVN.Cloud'Chibro'ChibroMES'src'TRIM.Chibro.Services'Implementations'ListOdPService.cs:line 20
   at TRIM.Chibro.Web.Controllers.ListOdPController.Index(ListOdPModel model) in c:'Progetti.SVN.Cloud'Chibro'ChibroMES'src'TRIM.Chibro.Web'Controllers'ListOdPController.cs:line 75
   at lambda_method(Closure , ControllerBase , Object[] )
   at System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters)
   at System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters)
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters)
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.ActionInvocation.InvokeSynchronousActionMethod()
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.<BeginInvokeSynchronousActionMethod>b__39(IAsyncResult asyncResult, ActionInvocation innerInvokeState)
   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult`2.CallEndDelegate(IAsyncResult asyncResult)
   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResultBase`1.End()
   at System.Web.Mvc.Async.AsyncResultWrapper.End[TResult](IAsyncResult asyncResult, Object tag)
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethod(IAsyncResult asyncResult)
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.AsyncInvocationWithFilters.<InvokeActionMethodFilterAsynchronouslyRecursive>b__3d()
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.AsyncInvocationWithFilters.<>c__DisplayClass46.<InvokeActionMethodFilterAsynchronouslyRecursive>b__3f()
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass33.<BeginInvokeActionMethodWithFilters>b__32(IAsyncResult asyncResult)
   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult`1.CallEndDelegate(IAsyncResult asyncResult)
   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResultBase`1.End()
   at System.Web.Mvc.Async.AsyncResultWrapper.End[TResult](IAsyncResult asyncResult, Object tag)
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethodWithFilters(IAsyncResult asyncResult)
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass21.<>c__DisplayClass2b.<BeginInvokeAction>b__1c()
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass21.<BeginInvokeAction>b__1e(IAsyncResult asyncResult)

如果后面出现Skip(.)LINQ部分,则XML字段上的自定义SQLFunctionTemplate会导致转义错误

在堆栈跟踪中,您可以看到问题来自GetLimitString()及其助手,这回答了您的问题,即为什么它只在Skip()存在的情况下发生。如果没有skip/take,就没有理由对SQL应用限制/偏移量,因此永远不会调用该方法。

至于这个问题本身就有点难。在2012年之前,NHibernate必须对SQL进行一些神奇的处理,才能使用MSSQL方言应用限制/偏移量,因为它们缺乏用于此功能的直接SQL语法。

我不知道你的代码中是否存在转义问题,或者你是否遇到了NHibernate的MsSqlSelectParser不是为其设计的情况。

如果您获取了NHibernate源代码和pdb文件,您可以尝试调试到堆栈跟踪的前5行,并逐步查看它到底哪里出了问题。