2010-06-28 9 views
5

Sto usando NHibernate e ho le due seguenti classi che mappano il mio schema di database:Come utilizzare NHibernate per recuperare elementi con un criterio in un elenco

public class A 
{ 
    public virtual int Id { get; set;} 
    public virtual List<B> MyList { get; set; } 
} 

public class B 
{ 
    public virtual int Id { get; set; } 
    public virtual DateTime Date { get; set; } 
    public virtual A FKtoA { get; set; } 
} 

vorrei ottenere tutte le voci di tabella A che contiene tutti gli elementi della proprietà MyList con una data inferiore a un determinato valore.

Come posso farlo con un'elegante sintassi NHibernate?

risposta

1

io la parte "elegante" devono la ... :-)

Questo è un possibile HQL. Nota che invertito la tua condizione: invece di cercare "A che hanno tutti gli elementi della loro proprietà MyList con una Data inferiore a un determinato valore", cerco "A che non hanno alcun elementi della loro MyList proprietà con una data pari o superiore a un determinato valore ".

from A a 
where a not in 
     (select a1 
     from A a1, B b 
     where b.Date >= :date 
     and b in elements(a1.MyList)) 

Usage:

var results = session.CreateQuery("hql from above") 
        .SetParameter("date", DateTime.Today) 
        .List(); 

Si noti che, se si dichiara un rapporto bidirezionale tra A e B (con l'aggiunta di una proprietà A), la query è molto più semplice:

from A a 
where a not in 
     (select b.A 
     from B b 
     where b.Date >= :date) 

Aggiornamento: ecco come farlo con i criteri:

session.CreateCriteria<A>().Add(
    Subqueries.PropertyNotIn("id", 
          DetachedCriteria.For<A>() 
           .CreateCriteria("MyList") 
           .SetProjection(Projections.Property("id")) 
           .Add(Restrictions.Ge("Date", DateTime.Today)))) 
+0

buon punto per la relazione bidirezionale: aggiungerò una proprietà di tipo A nella mia classe B. HQL è ottimo ma mi chiedevo se non potevamo ottenere sth con i metodi DetachedCriteria() e Projections.Max() – PierrOz

+0

OK, Ho aggiunto l'opzione Criteria (non sarebbe difficile prenderla e cambiarla per usare 'Subqueries.PropertyIn' e invertire la subquery per usare una proiezione). Tuttavia, i criteri sono più utili per le query costruite dinamicamente (ricerca). Guarda tutto il rumore, rispetto all'HQL. –

+1

meraviglioso grazie mille !! – PierrOz

0

Utilizzare questa

ICriteria criteria = session.CreateCriteria<ClassOfTableOne>(); 
criteria.CreateAlias("FieldNameOfTypeTable2","aliasName"); 
criteria.SetFetchMode("aliasName", FetchMode.Join); 
criteria.Add(Restrictions.Lt("aliasName.Date", yourdate)); 
+0

Quella query NON restituisce ciò che PierrOz si aspetta. –

0

se la classe B simile a questa (in cui la proprietà MyList di A cerca questo FK)

public class B 
{ 
    public virtual int Id { get; set; } 
    public virtual DateTime Date { get; set; } 
    public virtual A FK_ToA { get; set; } 
} 

allora penso che sta cercando (HQL)

nhSes.CreateQuery("select b.FK_ToA from B b where b.Date < :passedDate") 
    .SetTimestamp("passedDate", DateTime.Now).List<A>() 
-1

La risposta attualmente accettata si basa su una sottoquery correlata, che come regola empirica è solo "SQL non valido".

È molto meglio semplicemente esprimere questo usando semantica basata su set piuttosto che un approccio più funzionale.

Essenzialmente volete che il vostro SQL a guardare come questo:

SELECT 
A.Id 
FROM A 
LEFT OUTER JOIN B ON A.Id = B.FKtoA 
WHERE B.Date < @MyDate 

Questo si legge come "Voglio un insieme di colonne da A come correlata ad una serie di B dove di B Data è minore di un certo valore". Ciò può essere ottenuto utilizzando l'API ICriteria:

ICriteria criteria = session.CreateCriteria<A>(); 
criteria.CreateAlias("MyList", "b", JoinType.LeftOuterJoin) 
criteria.Add(Restrictions.Lt("b.Date", myDate)); 
criteria.SetResultTransformer(new DistinctRootEntityResultTransformer()); 
criteria.List<A>(); 

parte del trucco sta usando di NHibernate costruito-in DistinctRootEntityResultTransformer: dal momento che il join esterno sinistro potrebbe tornare più istanze di A per B, vogliamo che i nostri ICriteria per restituire solo il istanze distinte (supponendo che non ci interessa l'ordine o qualsiasi altra cosa).

+0

Ti suggerisco di sostenere le tue "regole pratiche" con alcuni fatti concreti. Inoltre, il tuo SQL suggerito non funzionerebbe: usare B nell'istruzione WHERE lo rende un inner join (lo stesso accade con i tuoi Criteri, che ha l'onere aggiuntivo del trasformatore distinto lato client) –

+0

NON provocherebbe un inner join come ho specificato come un outer-join. Puoi verificarlo tramite un profiler. Essenzialmente ci sono due approcci al problema: una singola query (che è la soluzione che ho suggerito) o due query (tramite una sotto-query correlata o due query completamente separate). Entrambi hanno pro/contro. Hai menzionato lo svantaggio principale di una singola query. Nel caso della sottoquery correlata, controlla questo: http://stackoverflow.com/questions/141278/subqueries-vs-joins –

+0

Un filtro al di fuori della condizione JOIN lo trasforma in un inner join (* provalo *) . Puoi renderlo parte della condizione di join con HQL (usando la clausola WITH) ma non con Criteria. Inoltre: la sottoquery in questo caso NON è correlata (non sto utilizzando elementi della query esterna all'interno di quella interna), quindi è equivalente a un join, almeno con motori DB decenti. –