我构建了以下查询,使用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 页且可编辑的菜单
相反,新查询返回;
我已经划掉了所有返回但不应该的行。这里发生了什么 !?