Pergunta

Eu estou tentando consulta contra uma propriedade IList em uma das minhas classes de domínio usando NHibernate. Aqui está um exemplo simples para demonstrar:

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

mapeada dessa forma:

<class name="Demo">
<id name="Id" />
<property name="Name" />
<bag name="Tags">
  <key column="DemoId"/>
  <element column="Tag" type="String" />
</bag>

E eu sou capaz de salvar e recuperar muito bem. Agora a consulta para casos de minha classe de domínio onde o Tag propriedade contém um valor especificado:

var demos = this.session.CreateCriteria<Demo>()
            .CreateAlias("Tags", "t")
            .Add(Restrictions.Eq("t", "a"))
            .List<Demo>();

resulta em erro: coleção não era uma associação: Demo.Tags

var demos = (from d in this.session.Linq<Demo>()
                     where d.Tags.Contains("a")
                     select d).ToList();

Os resultados do erro:. Referência Objct não definida para uma instância de um objeto

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

funciona bem, mas como a minha classe de domínio real tem muitas muitas propriedades, e eu estou construindo uma consulta dinâmica complicada, fazendo a manipulação de cadeia feio não é a minha primeira escolha. Eu prefiro usar ICriteria ou Linq. Eu tenho uma interface de usuário onde muitos critérios de pesquisa diferentes possíveis podem ser inseridos. O código que constrói o ICriteria agora é dezenas de linhas. Eu realmente odeio a transformar isso em seqüência de manipulação HQL.

Foi útil?

Solução

Assim, devido a limitações da API Criteria, decidi dobrar minhas classes de domínio para o ajuste.

Eu criei uma classe de entidade para o Tag. Eu não poderia mesmo criá-la como um objeto de valor. Ele tinha que ter o seu próprio ID.

Eu me sinto sujo agora. Mas ser capaz de construir uma consulta dinâmica, sem recorrer a manipulação de cadeia era mais importante para mim do que permanecer fiel ao domínio.

Outras dicas

Como documentado aqui:

17.1.4.1. Alias ??e propriedade referências

podemos usar:

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

há um pequeno bug no doc ... em vez de ".element" temos que usar ".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>();

Você precisa usar SubCriterias não de alias. Isso deve funcionar:

var demos = this.session.CreateCriteria<Demo>()
            .CreateCriteria("Tags")
            .Add(Restrictions.Eq("Tag", "a"))
            .List<Demo>();

HQL:

from Demo d where :val in elements(d.Tags)

A mudança para uma classe mais de uma string é um compromisso. Usando HQL em vez de ICriteria é outra. Há um terceiro compromisso no entanto ... uso personalizado SQL. Tentar fazer isso.

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

Isso resulta na SQL follwing sendo gerado pelo 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'

Veja este post para um outro exemplo ...

NHibernate - Consultando a partir de uma coleção de Valor Tipos (não-Entity) para resolver Select N + 1

Isto é possível através da criação de um critério separadas:

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>();
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top