NHibernate Как мне запросить свойство IList <string>?
Я пытаюсь запросить IList <string> свойство на одном из моих классов домена с использованием NHibernate. Вот простой пример для демонстрации:
public class Demo
{
public Demo()
{
this.Tags = new List<string>();
}
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<string> Tags { get; set; }
}
Отображается следующим образом:
<class name="Demo">
<id name="Id" />
<property name="Name" />
<bag name="Tags">
<key column="DemoId"/>
<element column="Tag" type="String" />
</bag>
И я могу сохранить и получить просто отлично. Теперь для запроса экземпляров моего класса домена, где свойство Tags содержит указанное значение:
var demos = this.session.CreateCriteria<Demo>()
.CreateAlias("Tags", "t")
.Add(Restrictions.Eq("t", "a"))
.List<Demo>();
Результаты в ошибке: коллекция не была ассоциацией: Demo.Tags
var demos = (from d in this.session.Linq<Demo>()
where d.Tags.Contains("a")
select d).ToList();
Результат ошибки: ссылка на объект objct не установлена в экземпляр объекта.
var demos = this.session.CreateQuery("from Demo d where :t in elements(d.Tags)")
.SetParameter("t", "a")
.List<Demo>();
Работает нормально, но поскольку мой настоящий класс домена имеет много свойств, и я создаю сложный динамический запрос, выполнение уродливых манипуляций с строками не является моим первым выбором. Я бы предпочел использовать ICriteria или Linq. У меня есть пользовательский интерфейс, в котором можно ввести много разных возможных критериев поиска. Код, который сейчас создает ICriteria, длится десятки строк. Я действительно ненавижу превращать это в манипуляции с строкой HQL.
Ответы
Ответ 1
Итак, из-за ограничений API-адресов Criteria я решил согнуть свои классы домена, чтобы они соответствовали.
Я создал класс сущности для тега. Я даже не мог создать его как объект ценности. Он должен был иметь свой собственный идентификатор.
Теперь я чувствую себя грязным. Но возможность построения динамического запроса, не прибегая к манипулированию строками, для меня была более важна, чем оставаться верным домену.
Ответ 2
Как описано здесь:
мы можем использовать:
...
A collection key {[aliasname].key} ORGID as {coll.key}
The id of an collection {[aliasname].id} EMPID as {coll.id}
The element of an collection {[aliasname].element} XID as {coll.element}
...
есть небольшая ошибка в doc... вместо ".element"
мы должны использовать ".elements"
var demos = this.session.CreateCriteria<Demo>()
.CreateAlias("Tags", "t")
// instead of this
// .Add(Restrictions.Eq("t", "a"))
// we can use the .elements keyword
.Add(Restrictions.Eq("t.elements", "a"))
.List<Demo>();
Ответ 3
Вам нужно использовать SubCriterias, а не псевдоним.
Это должно работать:
var demos = this.session.CreateCriteria<Demo>()
.CreateCriteria("Tags")
.Add(Restrictions.Eq("Tag", "a"))
.List<Demo>();
Ответ 4
HQL:
from Demo d where :val in elements(d.Tags)
Ответ 5
Переключение на класс по строке - это один компромисс. Использование HQL вместо ICriteria - другое. Однако есть третий компромисс... используйте собственный SQL. Попробуйте это.
var demos = Session.CreateCriteria<Demo>()
.Add(Expression.Sql(
"EXISTS (SELECT 1 FROM [Tags] custom_sql_t WHERE custom_sql_t.[DemoId] = {alias}.[Id] AND custom_sql_t.[Tag] = ?)",
"a",
NHibernateUtil.String))
.List<Demo>();
Это приводит к тому, что SQL-запрос, созданный NHibernate 2.1.2.4000...
exec sp_executesql N'SELECT this_.Id as Id2_0_, this_.Version as Version2_0_, this_.Name as Name2_0_ FROM Demo this_ WHERE EXISTS (SELECT 1 FROM [Tags] custom_sql_t WHERE custom_sql_t.[DemoId] = this_.[Id] AND custom_sql_t.[Tag] = @p0)',N'@p0 nvarchar(1)',@p0=N'a'
Смотрите это сообщение для другого примера...
NHibernate - запрос из набора типов значений (не Entity) для решения Select N + 1
Ответ 6
Это возможно, создав отдельный критерий:
ICriteria demoCriteria = session.CreateCriteria<Demo>();
...
demoCriteria.Add(Restrictions...);
...
ICriteria tagCriteria = demoCriteria.CreateCriteria("Tags");
tagCriteria.Add(Restrictions.In("elements", new {"Tag1", "Tag2", ...}));
return demoCriteria.List<Demo>();