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.

È stato utile?

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>();
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top