2009-08-14 6 views
3

Это было 2-недельное сражение для меня до сих пор без везения. :(Nhibernate Tag Cloud Query

Позвольте мне первым высказать свою цель. Для того, чтобы иметь возможность искать объекты, которые помечены «Foo» и «бар». Не думаю, что было слишком трудно правильно?

Я знаю, что это может можно легко сделать с HQL, но так как это динамически построен поисковый запрос, это не вариант Первый некоторый код:.

public class Foo 
{ 
    public virtual int Id { get;set; } 
    public virtual IList<Tag> Tags { get;set; } 
} 

public class Tag 
{ 
    public virtual int Id { get;set; } 
    public virtual string Text { get;set; } 
} 

Подключенные как многие-ко-многим, потому что класс Tag используется на многих различных типов. Следовательно, нет двунаправленной ссылки.

Поэтому я строю свои отдельные критерии, используя абстрактный класс фильтра. Предположим, что для простоты я просто ищу Foos с тегами «Яблоки» (TagId1) & & «Апельсины» (TagId3) это будет выглядеть примерно так.

SQL:

SELECT ft.FooId 
FROM Foo_Tags ft 
WHERE ft.TagId IN (1, 3) 
GROUP BY ft.FooId 
HAVING COUNT(DISTINCT ft.TagId) = 2; /*Number of items we are looking for*/ 

Критерии

var idsIn = new List<int>() {1, 3}; 
var dc = DetachedCriteria.For(typeof(Foo), "f"). 
      .CreateCriteria("Tags", "t") 
      .Add(Restrictions.InG("t.Id", idsIn)) 
      .SetProjection(Projections.ProjectionList() 
       .Add(Projections.Property("f.Id")) 
       .Add(Projections.RowCount(), "RowCount") 
       .Add(Projections.GroupProperty("f.Id"))) 
      .ProjectionCriteria.Add(Restrictions.Eq("RowCount", idsIn.Count)); 
} 
var c = Session.CreateCriteria(typeof(Foo)).Add(Subqueries.PropertyIn("Id", dc)) 

В основном это создает DC, который проецирует список Foo идентификаторов, которые имеют все теги указанные.

Это скомпилировано в NH 2.0.1, но не работает, поскольку он жаловался, что не смог найти Свойство «RowCount» в классе Foo.

После прочтения сообщения this Я надеялся, что это может быть исправлено в 2.1.0, поэтому я обновил его. К моему большому разочарованию я обнаружил, что ProjectionCriteria был удален из DetachedCriteria, и я не могу понять, как сделать динамическое построение запросов без DetachedCriteria.

Итак, я попытался подумать, как написать тот же запрос, не требуя печально известного предложения. Это можно сделать с помощью нескольких соединений в таблице тегов. Ура. Я думал, что это довольно просто. Поэтому я переписал его, чтобы выглядеть так.

var idsIn = new List<int>() {1, 3}; 
var dc = DetachedCriteria.For(typeof(Foo), "f"). 
      .CreateCriteria("Tags", "t1").Add(Restrictions.Eq("t1.Id", idsIn[0])) 
      .CreateCriteria("Tags", "t2").Add(Restrictions.Eq("t2.Id", idsIn[1])) 

В тщетной попытке создать нижний sql, который будет выполнять эту работу (я понимаю, что это не совсем правильно).

SELECT f.Id 
FROM Foo f 
JOIN Foo_Tags ft1 
ON ft1.FooId = f.Id 
    AND ft1.TagId = 1 
JOIN Foo_Tags ft2 
ON ft2.FooId = f.Id 
    AND ft2.TagId = 3 

К сожалению, я упал на первое препятствие с этой попыткой, получив исключение «Дубликат ассоциация Path». Чтение around это, кажется, ancient и все еще очень реальная ошибка/ограничение.

Что мне не хватает?

Я начинаю проклинать имя NHibernates при создании того, что вы думаете, так просто и обыденно, так сложно. Пожалуйста, помогите всем, кто сделал это раньше. Как вы обошли ограничения NHibernates.

Забудьте о репутации и щедрости. Если кто-то сделает мне твердую вещь, я пришлю вам 6 пакетов для вашей проблемы.

+0

Это помогло бы, если бы вы могли показать SQL, который хотите сгенерировать, тот, у кого есть предложение ... – sirrocco

+0

Добавлено. Извините, что забыл. Его довольно стандартный, просто ищет все элементы с любыми тегами и ограничивает те, которые имеют все. – madcapnmckay

ответ

0

Ян,

Поскольку я не уверен, что дб бэкенд вы используете, вы можете сделать какой-то след от произведенного запроса SQL и посмотреть на SQL, чтобы выяснить, что пошло не так?

Я знаю, что я делал это в прошлом, чтобы понять, как работают Linq-2-SQL и Linq-2-Entities, и смогли настроить некоторые случаи для улучшения доступа к данным, а также понять, почему что-то не работало, как ожидалось первоначально.

+0

Привет, Ричард. Если бы я понял, где он производит sql, я буду смеяться. Проблема, которую я имею, заключается в том, что, похоже, нет прямого способа заставить NH делать то, что вы хотите, когда требуется условие «Кому». – madcapnmckay

+0

Извините, Ян, я думал, что есть метод, похожий на то, что в Linq2SQL, что вы можете сказать «ToSQL()» и получить вывод sql ... Я еще не перешел на nHibernate, так как это крутой кривой обучения, на которую я буду смотреть, когда у меня есть время, чтобы учиться. –

2

мне удалось получить его работу, как это:

var dc = DetachedCriteria.For<Foo>("f") 
      .CreateCriteria("Tags", "t") 
      .Add(Restrictions.InG("t.Id", idsIn)) 
      .SetProjection(Projections.SqlGroupProjection("{alias}.FooId", "{alias}.FooId having count(distinct t1_.TagId) = " + idsIn.Count, 
         new[] { "Id" }, 
          new IType[] { NHibernateUtil.Int32 })); 

Единственная проблема здесь отсчет (t1_ .TagID) - но я думаю, что псевдоним должен быть сформирован таким же каждый раз, когда в этом DetachedCriteria - так что вы должны быть в безопасности, жестко кодируя это.

+0

Я дам вам попытку и дам вам знать. – madcapnmckay