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>

And I am able to save and retrieve just fine. Now to query for instances of my domain class where the Tags property contains a specified value:

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

Приводит к ошибке: ссылка на объект не установлена ​​на экземпляр объекта.

var demos = this.session.CreateQuery("from Demo d where :t in elements(d.Tags)")
            .SetParameter("t", "a")
            .List<Demo>();

Работает нормально, но поскольку мой настоящий предметный класс имеет множество свойств, и я создаю сложный динамический запрос, уродливые манипуляции со строками - не мой первый выбор. Я бы предпочел использовать ICriteria или Linq. У меня есть пользовательский интерфейс, в котором можно ввести множество различных критериев поиска. Код, который сейчас формирует критерии ICriteria, состоит из десятков строк. Мне бы очень не хотелось превращать это в манипуляции со строками HQL.


person JohnRudolfLewis    schedule 30.07.2009    source источник


Ответы (6)


Поэтому из-за ограничений API критериев я решил подогнать свои доменные классы.

Я создал класс сущности для тега. Я даже не мог создать его как объект-ценность. У него должен был быть свой идентификатор.

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

person JohnRudolfLewis    schedule 31.07.2009
comment
Есть способ сделать это по критерию api. Запаковал в отдельный ответ. - person Tobias; 05.12.2016

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

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}
...

в документе есть небольшая ошибка ... вместо ".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>();
person Radim Köhler    schedule 05.11.2014

Вам нужно использовать SubCriterias, а не псевдоним. Это должно работать:

var demos = this.session.CreateCriteria<Demo>()
            .CreateCriteria("Tags")
            .Add(Restrictions.Eq("Tag", "a"))
            .List<Demo>();
person zoidbeck    schedule 30.07.2009
comment
Это также приводит к появлению сообщения об ошибке: коллекция не была ассоциацией: Demo.Tags - person JohnRudolfLewis; 31.07.2009
comment
Вы не указали столик для своей сумки ‹Tag›. Я не знаю такого картографирования. Это предназначено? - person zoidbeck; 31.07.2009
comment
Не имеет значения, укажу ли я имя таблицы или позволю ей использовать значение по умолчанию (то же самое, что и имя свойства в этом случае). - person JohnRudolfLewis; 31.07.2009
comment
Хорошо, наконец-то понял, в чем твоя проблема. Теги - это список ‹string›, а не список ‹Tag›. Вы не можете сделать это с помощью CriteriaAPI. См. Эту ветку: mail-archive.com/nhibernate-development@ googlegroups.com/ - person zoidbeck; 31.07.2009
comment
Это должно быть отмечено как ответ, это первое, к чему я пришел, что действительно помогло! - person bastianwegge; 11.12.2013

HQL:

from Demo d where :val in elements(d.Tags)
person Ayende Rahien    schedule 31.07.2009
comment
Я знаю, что это работает. Выше я подтверждаю, что это так. Но я кричу и кричу о том, что мне нужно написать огромный динамический запрос, основанный на вводе моего пользователя. Спасибо, что посмотрели на это. Я ценю это, Орен. - person JohnRudolfLewis; 31.07.2009

Один из компромиссов - переключение на класс по строке. Другое дело - использование 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>();

В результате NHibernate 2.1.2.4000 создает следующий SQL ...

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 - Запросы из коллекции типов значений (не-сущностей) для решения Выберите N + 1

person Adam Boddington    schedule 27.10.2010

Это возможно, если создать отдельный критерий:

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>();
person Tobias    schedule 05.12.2016