0

我构建了以下查询,使用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 的页面的可编辑菜单对象。到目前为止,我还没有找到解决方案。我原以为对上述查询的 pages 部分进行简单的反转就足以导致:

// 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 */

)

乍一看,这似乎正是我想要的,但遗憾的是(可能是由于我对连接的理解不深)仍然没有完全返回我想要的。鉴于下表

菜单

菜单表的镜头

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

显示连接表

我们可以看到菜单 5 和 6 中没有引用 id 为 1 的页面。我希望有问题的查询只返回一行,这将是 ID 为 5 的菜单的 ID、名称和描述,因为这是仅包含第 1 页且编辑的菜单

相反,新查询返回;

在此处输入图像描述

我已经划掉了所有返回但不应该的行。这里发生了什么 !?

4

1 回答 1

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>();
    
于 2012-01-03T16:14:08.273 回答