NHibernate Come eseguire una query su un IList < string > proprietà?
-
06-07-2019 - |
Domanda
Sto cercando di interrogare un IList < string > proprietà su una delle mie classi di dominio usando NHibernate. Ecco un semplice esempio da dimostrare:
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; }
}
Mappato in questo modo:
<class name="Demo">
<id name="Id" />
<property name="Name" />
<bag name="Tags">
<key column="DemoId"/>
<element column="Tag" type="String" />
</bag>
E sono in grado di salvare e recuperare bene. Ora per eseguire una query per istanze della mia classe di dominio in cui la proprietà Tags contiene un valore specificato:
var demos = this.session.CreateCriteria<Demo>()
.CreateAlias("Tags", "t")
.Add(Restrictions.Eq("t", "a"))
.List<Demo>();
Risultati nell'errore: collection non era un'associazione: Demo.Tags
var demos = (from d in this.session.Linq<Demo>()
where d.Tags.Contains("a")
select d).ToList();
Risultati nell'errore: riferimento all'oggetto non impostato su un'istanza di un oggetto.
var demos = this.session.CreateQuery("from Demo d where :t in elements(d.Tags)")
.SetParameter("t", "a")
.List<Demo>();
Funziona bene, ma poiché la mia vera classe di dominio ha molte proprietà e sto costruendo una complessa query dinamica, fare brutte manipolazioni di stringhe non è la mia prima scelta. Preferirei di gran lunga usare ICriteria o Linq. Ho un'interfaccia utente in cui è possibile inserire molti diversi criteri di ricerca possibili. Il codice che costruisce ICriteria in questo momento è lungo dozzine di righe. Odio davvero trasformarlo in manipolazione di stringhe HQL.
Soluzione
Quindi, a causa delle limitazioni dell'API dei criteri, ho deciso di piegare le mie classi di dominio per adattarle.
Ho creato una classe di entità per il tag. Non riuscivo nemmeno a crearlo come oggetto valore. Doveva avere un suo ID.
Adesso mi sento sporco. Ma essere in grado di costruire una query dinamica senza ricorrere alla manipolazione di stringhe è stato più importante per me che rimanere fedele al dominio.
Altri suggerimenti
Come documentato qui:
17.1.4.1. Alias ??e riferimenti di proprietà
possiamo usare:
...
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}
...
c'è un piccolo bug nel doc ... invece di " .element "
dobbiamo usare ".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>();
Devi usare SubCriterias non alias. Questo dovrebbe funzionare:
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)
Il passaggio a una classe su una stringa è un compromesso. L'uso di HQL invece di ICriteria è un altro. C'è un terzo compromesso, tuttavia ... usa SQL personalizzato. Prova questo.
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>();
Ciò si traduce nel fatto che l'SQL seguente viene generato da 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'
Vedi questo post per un altro esempio ...
NHibernate - Querying da una raccolta di Tipi di valore (non Entità) per risolvere Seleziona N + 1
Questo è possibile creando un criterio separato:
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>();