代码之家  ›  专栏  ›  技术社区  ›  Liedman

在NHibernate中使用业务规则避免N+1选择

  •  2
  • Liedman  · 技术社区  · 15 年前

    我的应用程序中的标准查询是获取给定客户的所有项目。我使用以下条件来执行此操作,尝试急切地获取项目的类别,以避免在检查项目的categories属性时进行N+1选择:

    ICriteria criteria = mySession.CreateCriteria(typeof(Item));
        .CreateCriteria("Categories", NHibernate.SqlCommand.JoinType.InnerJoin)
            .Add(Expression.Eq("Customer", c));
    criteria.SetFetchMode("Categories", FetchMode.Eager);
    
    return criteria.List();
    

    但是,这不起作用,NHibernate以后仍然通过每个项目的一个选择来获取类别。

    我相信NHibernate知道第一个查询的结果是过滤的(在Customer上),并且查询返回的类别可能不完整,因此它以后必须进行单独的查询来获取类别。(这个假设正确吗?在我看来,NHibernate必须以这种方式工作,以确保正确的结果。)

    我的问题是:我能以任何方式告诉NHibernate这个商业规则吗?在这种情况下,有没有其他方法可以避免N+1选择(这似乎很常见)?

    1 回复  |  直到 15 年前
        1
  •  2
  •   Liedman    15 年前

    我会尽量回答我自己的问题,因为到目前为止我还没有得到任何答案。

    我的解决方案是将问题分为两个查询:首先获取属于相关客户的项目的ID:

    IQuery query = mySession.CreateQuery("select item.Id from Item as item "
        + "join item.Categories as category "
        + "join category.Customer customer "
        + "where customer.id=:id")
        .SetInt32("id", c.Id);
    IList itemIds = query.List();
    

    然后,我获取实际的项目,而不涉及对客户的任何限制,只使用id。通过这种方式,NHibernate知道它可以从一个连接中获取所有类别,从而避免了问题中提到的N+1选择:

    ICriteria criteria = mySession.CreateCriteria(typeof(MapItem))
        .SetFetchMode("Categories", FetchMode.Eager)
        .SetResultTransformer(new DistinctRootEntityResultTransformer())
        .Add(Expression.In("Id", itemIds));
    IList items = criteria.List();
    

    我无法想出任何解决方案,将其简化为一个有效的、单一的查询。此外,这种方法迫使程序员对NHibernate的内部工作方式了解得太多,而且在编写新的查询或条件时,很容易遗漏。一个更普遍的解决方案会更好。