SetMaxResult用于查询和子查询

时间:2014-05-13 06:52:49

标签: c# nhibernate criteria

我正在尝试使用NHibernate创建分页复杂查询。像这样:

SELECT TOP 10 * FROM (
   SELECT
      ROW_COUNT() ... as 'rowNum',
      Value,
      (SELECT TOP 1 FROM Table1 WHERE Condition ORDER BY Column)
   FROM Table2
   WHERE EXISTS(SELECT null FROM Table1 WHERE Condition)
) WHERE rowNum > 10

重现问题的最简单的测试用例是:

var criteria = CurrentSession.CreateCriteria<Table>();

var subCriteria = DetachedCriteria.For<Table>()
        .SetProjection(Projections.Id())
        .SetMaxResults(1);

criteria.SetProjection(Projections.ProjectionList()
        .Add(Projections.Constant(1))
        .Add(Projections.SubQuery(subCriteria)))
    .SetMaxResults(10)
    .SetFirstResult(1)
    .List();

不幸的是,这会产生错误的SQL - 子查询SetMaxResult所有问题。我该如何解决?

1 个答案:

答案 0 :(得分:0)

为了克服这个错误,我不得不改写方言。它可能不是那么好和干净,但在这里。所有更改都在此方法中:

    private static int GetFromIndex(SqlString querySqlString)
    {
        /* A large amount of modifications were made to the following code in order to work around the bug here: https://nhibernate.jira.com/browse/NH-3286*/

        var queryRealString = querySqlString.ToString();

        // Remove any 'TOP (?)' clauses from the query because 'GetSubselectString' doesn't handle them
        var querySqlStringWithoutTops = new SqlString(queryRealString.Replace(" TOP (?)", ""));

        string subselect = querySqlStringWithoutTops.GetSubselectString().ToString();

        // Regex match the subselect - the original code got this part wrong too
        int fromIndex = Regex.Match(querySqlStringWithoutTops.ToString(), Regex.Escape(subselect) + @"($|\s|\,|\)|\n)", RegexOptions.Multiline).Index;

        // Not sure if the next bit is going to work with our changes...
        if (fromIndex == -1)
        {
            fromIndex = queryRealString.ToLowerInvariant().IndexOf(subselect.ToLowerInvariant());
        }

        // Work out the length of all the 'TOP (?)' that were removed above
        var currentStart = 0;
        var lengthOfDeletedTops = 0;
        int ixOfTops;
        var topLength = " TOP (?)".Length;
        while (currentStart < fromIndex
            && (ixOfTops = queryRealString.IndexOf(" TOP (?)", currentStart, fromIndex - currentStart, StringComparison.OrdinalIgnoreCase)) >= 0)
        {
            lengthOfDeletedTops += topLength;
            currentStart = ixOfTops + topLength;
        }

        return fromIndex + lengthOfDeletedTops;
    }