Реверсивная логика запроса NHibernate - PullRequest
0 голосов
/ 03 января 2012

Я построил следующий запрос, используя 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>();

Это отлично работает; однако теперь я хочу сделать наоборот: я хочу запросить все редактируемые объекты меню, которые не содержат страницу с указанным идентификатором. Я до сих пор не нашел решения для этого. Я бы подумал, что будет достаточно простого изменения раздела страниц в приведенном выше запросе, что приведет к:

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

Который все еще возвращает результаты пунктов меню, которые do содержат страницу с идентификатором 8. Почему это простое изменение логики не так просто с точки зрения кода?

[Update] Принимая предложения от 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 WHERE PageID = 1)

Show of join table

Мы видим, что на страницы с идентификатором 1 НЕ ссылаются в меню 5 и 6. Я ожидаю, что рассматриваемый запрос вернет только одну строку, которая будет идентификатором, именем и описанием меню с идентификатором 5 в качестве это единственное меню, для которого страница 1 не включена, а редактируемая

Вместо этого возвращается новый запрос;

enter image description here

Я вычеркнул все строки, которые возвращены, но не должны быть. Что здесь происходит!?

1 Ответ

1 голос
/ 03 января 2012

Обновление:

  • удалено .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>();
    
...