我使用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 */
)
一开始似乎正是我想要的,但遗憾的是(可能是因为我对连接的理解不充分)仍然没有按照我想要的方式返回。鉴于以下表格
菜单
然后是 PagesInMenu 的连接表(WHERE PageID = 1的WHERE子句)
我们可以看到id为1的页面在菜单5和6中没有被引用。我希望有问题的查询只返回一行,即ID为5的ID的ID,Name和Description这是第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>();