Pregunta

Estoy intentando realizar una consulta con un IList < string > propiedad en una de mis clases de dominio usando NHibernate. Aquí hay un ejemplo simple para demostrar:

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

Asignado de esta manera:

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

Y puedo guardar y recuperar bien. Ahora para consultar las instancias de mi clase de dominio donde la propiedad Etiquetas contiene un valor específico:

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

Resultados en el error: la colección no era una asociación: Etiquetas de demostración

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

Resultados en el error: la referencia de objeto no está establecida en una instancia de un objeto.

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

Funciona bien, pero como mi clase de dominio real tiene muchas propiedades, y estoy creando una consulta dinámica complicada, la manipulación de cadenas feas no es mi primera opción. Prefiero usar ICriteria o Linq. Tengo una interfaz de usuario donde se pueden ingresar muchos criterios de búsqueda diferentes. El código que construye la ICriteria en este momento tiene docenas de líneas. Realmente odiaría convertir eso en manipulación de cadenas HQL.

¿Fue útil?

Solución

Así que debido a las limitaciones de la API de criterios, decidí doblar mis clases de dominio para que se ajusten.

Creé una clase de entidad para la etiqueta. Ni siquiera podía crearlo como un objeto de valor. Tenía que tener su propia identificación.

Me siento sucio ahora. Pero poder construir una consulta dinámica sin tener que recurrir a la manipulación de cadenas era más importante para mí que permanecer fiel al dominio.

Otros consejos

Como se documenta aquí:

17.1.4.1. Alias ??y referencias de propiedad

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

hay un pequeño error en el documento ... en lugar de " .element " tenemos 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>();

Debe usar SubCriterias, no alias. Esto debería 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)

Cambiar a una clase sobre una cadena es un compromiso. Usar HQL en lugar de ICriteria es otro. Sin embargo, hay un tercer compromiso ... usar SQL personalizado. Prueba esto.

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

Esto da como resultado que el siguiente SQL sea generado por 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'

Ver este post para otro ejemplo ...

NHibernate - Consulta de una colección de tipos de valor (no entidad) para resolver Select N + 1

Esto es posible mediante la creación de un criterio separado:

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 bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top