Это было 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 пакетов для вашей проблемы.
Это помогло бы, если бы вы могли показать SQL, который хотите сгенерировать, тот, у кого есть предложение ... – sirrocco
Добавлено. Извините, что забыл. Его довольно стандартный, просто ищет все элементы с любыми тегами и ограничивает те, которые имеют все. – madcapnmckay