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

Posted

技术标签:

【中文标题】如果跟随 Skip(..) LINQ 部分,则 XML 字段上的自定义 SQLFunctionTemplate 会导致转义错误【英文标题】:Custom SQLFunctionTemplate on XML field causes escaping error if a Skip(..) LINQ section follows 【发布时间】:2016-12-06 15:31:09 【问题描述】:

我创建了一个自定义 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();    
..

调用 result.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)

【问题讨论】:

对,那么你从哪里得到异常,它到底在抱怨哪个字符串? @OskarBerggren 抱歉回复晚了,但我一直在度假。我包含了异常的完整堆栈跟踪,一旦 Nhibernate 尝试将 IQueryable“转换”为实际查询(即调用 ToList() 时),该异常就会出现。如果您可能需要任何其他信息,请告诉我如何检索它。非常感谢。 【参考方案1】:

在堆栈跟踪中,您可以看到问题来自 GetLimitString() 及其助手,这回答了您关于为什么它仅在存在 Skip() 时发生的问题。如果没有跳过/采取,则没有理由对 SQL 应用限制/偏移量,因此永远不会调用该方法。

至于问题本身,这有点困难。 NHibernate 必须对 SQL 做一些魔术,以便在 2012 年之前对 MSSQL 方言应用限制/偏移,因为它们缺乏针对此功能的直接 SQL 语法。

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

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

【讨论】:

以上是关于如果跟随 Skip(..) LINQ 部分,则 XML 字段上的自定义 SQLFunctionTemplate 会导致转义错误的主要内容,如果未能解决你的问题,请参考以下文章

动画文本跟随路径[关闭]

Linq 到实体 Skip() 和 Take()

PagedList 使用 LINQ Skip and Take,但使用 Count of results 显示分页

Linq分区操作之Skip,SkipWhile,Take,TakeWhile源码分析

Linq:使用Take和Skip实现分页

是否可以使用 LINQ 的 Skip 方法忽略列表项,然后在列表上应用 skip 而不会丢失该项目?