特定のページ (ページ ID で参照) を含むアイテムNHibernate
のコレクションを取得する次のクエリを作成しました。MenuView
// 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 のページを含むメニュー項目の結果をまだ返しているのはどれですか。この単純なロジックの反転が、コードの点でそれほど単純ではないのはなぜですか?
[更新] フィーロからの提案を受けて、提案されたクエリの変更は次のようになります。
// 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、名前、および説明である 1 つの行のみを返すと予想されます。ページ 1が含まれず、編集可能な唯一のメニュー
代わりに、新しいクエリが返されます。
返されたすべての行を取り消しましたが、そうすべきではありません。ここで何が起こっているのですか!?