颠倒NHibernate查询的逻辑

时间:2012-01-02 21:02:51

标签: nhibernate nhibernate-criteria

我使用NHibernate构建了以下查询,它将为我提供包含给定页面的MenuView项集合(由页面ID引用)。

// Only retrieve the required properties from Menu object
ProjectionList menuViewProjections = Projections.ProjectionList()
    .Add(Projections.Property("ID"), "ID")
    .Add(Projections.Property("Name"), "Name")
    .Add(Projections.Property("Description"), "Description");

var menus = session.CreateCriteria(typeof(Menu))
    // Only menu's that are editable
    .Add(Restrictions.Eq("IsEditable", true))

    // Only project required properties
    .SetProjection(menuViewProjections)

    // Only menu's that contain this page (Menu object has IList<Page> property called 'Pages')
    .CreateCriteria("Pages")
    // Restrict to menu's containing the pages with an id of the specified value
    .Add(Restrictions.Eq("ID", pageId))

    // Transform results into required, light-weight, view objects
    .SetResultTransformer(Transformers.AliasToBean(typeof(MenuView)))
    .List<MenuView>();

这很好用;但是,现在我想做相反的事情:我想查询包含具有指定ID的页面的所有可编辑菜单对象。到目前为止,我还没有找到解决方案。我原以为上述查询的页面部分的简单反转就足以导致:

// Only retrieve the required properties from Menu object
ProjectionList menuViewProjections = Projections.ProjectionList()
    .Add(Projections.Property("ID"), "ID")
    .Add(Projections.Property("Name"), "Name")
    .Add(Projections.Property("Description"), "Description");

var menus = session.CreateCriteria(typeof(Menu))
    // Only menu's that are editable
    .Add(Restrictions.Eq("IsEditable", true))

    // Only project required properties
    .SetProjection(menuViewProjections)

    // Only retrieve menus that do NOT contain this referenced page
    .CreateCriteria("Pages")
    .Add(Restrictions.Not(Restrictions.Eq("ID", pageId)))

    // Transform results into required view objects
    .SetResultTransformer(Transformers.AliasToBean(typeof(MenuView)))
    .List<MenuView>();

但是,这导致以下SQL:

SELECT this_.ID          as y0_,
   this_.Name        as y1_,
   this_.Description as y2_
FROM   [Menu] this_
       inner join PagesInMenu pages3_
         on this_.ID = pages3_.MenuID
       inner join [Page] page1_
         on pages3_.PageID = page1_.ID
WHERE  this_.IsEditable = 1 /* @p0 */
       and not (page1_.ID = 8 /* @p1 */)

仍然会返回执行包含ID为8的页面的菜单项的结果。为什么这种简单的逻辑反转在代码方面不那么简单?

[更新] 接受Firo的建议,建议查询改动;

// Only retrieve menus that do NOT contain this referenced page
.CreateCriteria("Pages")
.Add(Subqueries.PropertyNotIn("Id", querymenuItemswithPage))   <--- query you have would be here

现在生成以下sql语句;

    SELECT this_.ID          as y0_,
       this_.Name        as y1_,
       this_.Description as y2_
FROM   [Menu] this_
       inner join PagesInMenu pages3_
         on this_.ID = pages3_.MenuID
       inner join [Page] page1_
         on pages3_.PageID = page1_.ID
WHERE  this_.IsEditable = 1 /* @p0 */
       and page1_.ID not in (SELECT this_0_.ID as y0_
                             FROM   [Page] this_0_
                             WHERE  this_0_.ID = 1 /* @p1 */

一开始似乎正是我想要的,但遗憾的是(可能是因为我对连接的理解不充分)仍然没有按照我想要的方式返回。鉴于以下表格

菜单

Shot of menu table

然后是 PagesInMenu 的连接表(WHERE PageID = 1的WHERE子句)

Show of join table

我们可以看到id为1的页面在菜单5和6中没有被引用。我希望有问题的查询只返回一行,即ID为5的ID的ID,Name和Description这是第1页中包含的唯一菜单, 可编辑

相反,新查询返回;

enter image description here

我已经删除了所有返回但不应该的行。这是怎么回事??

1 个答案:

答案 0 :(得分:1)

更新

  • 已移除.CreateCriteria("Pages")
  • 添加了子查询

    var querymenuItemswithPage = DetachedCriteria.For<Menu>()
    .CreateCriteria("Pages")
        .Add(Restrictions.Eq("ID", pageId))
    .SetProjection(Projections.Id())
    
    // Only retrieve the required properties from Menu object
    ProjectionList menuViewProjections = Projections.ProjectionList()
        .Add(Projections.Property("ID"), "ID")
        .Add(Projections.Property("Name"), "Name")
        .Add(Projections.Property("Description"), "Description");
    
    var menus = session.CreateCriteria(typeof(Menu))
        // Only menu's that are editable
        .Add(Restrictions.Eq("IsEditable", true))
    
        // Only project required properties
        .SetProjection(menuViewProjections)
    
        // Only retrieve menus that do NOT contain this referenced page
        .Add(Subqueries.PropertyNotIn("Id", querymenuItemswithPage))
    
        // Transform results into required view objects
        .SetResultTransformer(Transformers.AliasToBean(typeof(MenuView)))
        .List<MenuView>();