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

Как описано здесь:

17.1.4.1. Псевдоним и ссылки на свойства

мы можем использовать:

...
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>();