c# - NHibernate ¿Cómo puedo consultar contra una propiedad IList<string>?
linq hql (6)
Estoy tratando de consultar contra una propiedad IList <cadena> 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 así:
<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 muy bien. Ahora para consultar instancias de mi clase de dominio donde la propiedad Tags 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: colección no era una asociación: Demo.Tags
var demos = (from d in this.session.Linq<Demo>()
where d.Tags.Contains("a")
select d).ToList();
Resultados en el error: Referencia de objeto no 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 construyendo una consulta dinámica complicada, hacer la manipulación fea de cadenas 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 posibles. El código que construye el ICriteria en este momento es de docenas de líneas de largo. Realmente odiaría convertir eso en manipulación de cadenas HQL.
Cambiar a una clase sobre una cadena es un compromiso. Usar HQL en lugar de ICriteria es otro. Sin embargo, hay un tercer compromiso ... use SQL personalizado. Probar 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 el siguiente SQL 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''
Vea esta publicación para otro ejemplo ...
NHibernate - Consulta de una colección de Value Types (no-Entity) para resolver Select N + 1
Como se documenta aquí:
17.1.4.1. Alias y referencias de propiedad
nosotros 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>();
Debido a las limitaciones de la API de Criteria, decidí adaptar mis clases de dominio.
Creé una clase de entidad para la etiqueta. Ni siquiera pude crearlo como un objeto de valor. Tenía que tener su propia identificación.
Me siento sucio ahora. Pero ser capaz de construir una consulta dinámica sin recurrir a la manipulación de cadenas era más importante para mí que mantenerme fiel al dominio.
Esto es posible creando 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>();
HQL:
from Demo d where :val in elements(d.Tags)
Necesita utilizar SubCriterias no alias. Esto debería funcionar:
var demos = this.session.CreateCriteria<Demo>()
.CreateCriteria("Tags")
.Add(Restrictions.Eq("Tag", "a"))
.List<Demo>();