NHibernate SetFirstResult导致重复的结果

时间:2015-09-17 21:20:24

标签: sql sql-server nhibernate paging nhibernate-criteria

我们遇到NHibernate(版本4.0.0.4000和4.0.4.4000测试)返回重复结果的方式有问题。在下面的示例中,我得到566个结果(正确的数字结果),但只有549个是唯一的,这意味着有17个重复。

#region Get Record IDs
public IList<string> GetRecordIds(string user, string agency, DateTime utcFrom, DateTime utcTo, SearchDateRangeType dateRangeType, IEnumerable<string> status, IEnumerable<string> billingStatus, IEnumerable<string> qaStatus, IEnumerable<string> transmissionStatus, IEnumerable<string> scheduledTransmissions, int pageSize = -1, int pageNumber = -1)
{
    using (ISession session = NHibernateHelper.OpenSession())
    {
        ICriteria crit = session.CreateCriteria<Metadata>();

        var dateDisjunction = Restrictions.Disjunction();

        dateDisjunction.Add(Restrictions.Between("IncidentDate", utcFrom, utcTo));

        crit.Add(dateDisjunction);

        if (string.IsNullOrEmpty(agency) == false)
        {
            crit.CreateAlias("Ownership._entities.AsIList", "entities");
            crit.Add(Restrictions.Eq("entities._entityName._value", agency));
            crit.Add(Restrictions.Eq("entities._isDeleted._value", false) || Restrictions.IsNull("entities._isDeleted._value"));
        }

        crit.AddOrder(Order.Asc(Projections.Property("RecordId")));

        crit.SetProjection(Projections.Property("RecordId"));

        if (pageSize > 0 && pageNumber > 0)
        {
            crit.SetFirstResult(pageSize * (pageNumber - 1)).SetMaxResults(pageSize);
        }

        var ret = crit.List<string>();

        return ret;
    }
}
#endregion

SQL Sample 1是NHibernate生成的第一次迭代代码。后续页面(第二页以后)使用ROW_NUMBER() OVER。 SQL示例2是手动创建的第一页,它使用ROW_NUMBER() OVER,就像它是后续页面一样。 NHibernate显然已经“优化”了第一页的ROW_NUMBER() OVER,似乎(?)成为我们问题的原因。

SQL示例1:由NHibernate生成。造成重复。

SELECT
    TOP (100) this_.RecordId as y0_ 
FROM
    PcrMetadata this_ 
inner join
    PcrEntities entities1_ 
        on this_.Id=entities1_.ListKey 
WHERE
    (
        this_.IncidentDate between '0001-01-01 00:00:00.0000000' and '9999-01-01 00:00:00.0000000'
    ) 
    and entities1_.Name = 'ClientIDNumber' 
    and (
        entities1_.Entities_IsDeleted = 0 
        or entities1_.Entities_IsDeleted is null
    )

SQL示例2:基于NHibernate第二页手动创建。不会造成重复。

SELECT
    TOP (100) this_.RecordId as y0_  
FROM
    (SELECT
        this_.Record as y0_,
        ROW_NUMBER() OVER(
    ORDER BY
        CURRENT_TIMESTAMP) as __hibernate_sort_row 
    FROM
        PcrMetadata this_ 
    inner join
        PcrEntities entities1_ 
            on this_.Id=entities1_.ListKey 
    WHERE
        (
            this_.IncidentDate between '0001-01-01 00:00:00.0000000' and '9999-01-01 00:00:00.0000000'
        ) 
        and entities1_.Name = 'ClientIDNumber' 
        and (
            entities1_.Entities_IsDeleted = 0 
            or entities1_.Entities_IsDeleted is null
        )) as query 
WHERE
    query.__hibernate_sort_row > 0 -- CHANGE THIS NUMBER

我做错了吗?或者我有什么办法可以强迫NHibernate使用ROW_NUMBER? 在此先感谢您的帮助!

1 个答案:

答案 0 :(得分:1)

我们无法加入集合并应用分页。因为我们正在获取cartesian product,这是分页(经验如上所述)。

我建议的解决方案是(我的方式永远)加入集合。为了得到类似的结果,我们应该:

  • 使用子查询应用WHERE
  • 使用获取批处理以便稍后收到所有收集项目,而不会发生1 + N问题

关于此问题有detailed answer

另见:

关于使结果与众不同的更多信息,但这在这里无济于事: