NHibernate QueryOver WhereExists su Many-to-Many
-
28-10-2019 - |
Domanda
Ho una relazione molti a molti che sto cercando di interrogare. Il mio problema è molto simile a quello descritto da Phillip Haydon qui quindi prenderò liberamente in prestito i suoi diagrammi e le sue spiegazioni.
Phillip aveva la seguente relazione molti-a-molti tra lavori e ruoli (mi spiace, non posso ancora incorporare immagini):
schema dei dati
Phillip doveva interrogare tutti i ruoli che non erano stati assegnati a un lavoro. La sua soluzione era la seguente:
Role roleAlias = null;
var existing = QueryOver.Of<JobRole>()
.Where(x => x.Job.JobId == jobId)
.And(x => x.Role.RoleId != roleId)
.And(x => x.Role.RoleId == roleAlias.RoleId)
.Select(x => x.Role);
result = Session.QueryOver<Role>(() => roleAlias)
.Where(x => x.IsEnabled)
.WithSubquery.WhereNotExists(existing)
.OrderBy(x => x.Name).Asc
.List();
Questo è stato molto utile, tuttavia sembra che in questa soluzione ci sia un'entità per ogni tabella; Lavoro, ruolo lavorativo e ruolo. JobRole ha sia un lavoro che un ruolo. Probabilmente qualcosa del genere:
public class Job
{
public int JobId {get;set;}
public string Name {get; set;}
public bool IsEnabled {get;set;}
public bool IsDefault {get;set;}
}
public class Role
{
public int RoleId {get;set}
public string Name {get;set}
public bool IsEnabled {get;set}
public string RoleType {get;set}
}
public class JobRole
{
public Job Job {get;set}
public Role Role {get;set;}
}
Questo è in conflitto con il modello che ho visto per modellare relazioni molti-a-molti, in particolare in esempi di architettura nitidi e dai consigli qui. In quegli esempi e nel mio caso, ho solo due classi, Lavoro e Ruolo. Qualcosa di simile:
public class Job
{
public int JobId {get;set;}
public string Name {get; set;}
public bool IsEnabled {get;set;}
public bool IsDefault {get;set;}
public IList<Role> Roles {get;set;}
}
public class Role
{
public int RoleId {get;set}
public string Name {get;set}
public bool IsEnabled {get;set}
public string RoleType {get;set}
public List<Job> Jobs {get;set;}
}
Nel mio caso, devo trovare tutti i lavori che hanno solo ruoli. Ho provato qualcosa di simile
Job job = null;
Role role = null;
var jobs = Session.QueryOver(() => job)
.WithSubquery
.WhereExists(
QueryOver.Of(() => role)
.JoinAlias(() => role.Jobs, () => job))
.List().ToList();
ma NHibernate richiede che la proiezione per la selezione in WhereExists e si lamenta se non viene fornita, il che ha senso per me.
Con il mio modello è persino possibile eseguire una sottoquery QueryOver con WhereExists?
Grazie in anticipo.
Soluzione
var jobs = session
.QueryOver<Job>()
.WhereRestrictionOn(j => j.Roles)
.IsNotEmpty
.List();
Mappatura per lavoro:
public class Job
{
public virtual int ID { get; set; }
public virtual string Name { get; set; }
public virtual IList<Role> Roles { get; set; }
}
e mappatura
<class name="Job">
...
<bag name="Roles" table="JobRoleMap" fetch="join" lazy="false" >
<key column="JobID" />
<many-to-many column="RoleID" class="Role" />
</bag>
</class>
Nel mio caso produce il seguente SQL (abbellito):
SELECT j.ID,
j.Name,
roles.JobID,
r.ID,
r.Name
FROM Job j
left outer join JobRoleMap roles
on j.ID = roles.JobID
left outer join Role r
on roles.RoleID = r.ID
WHERE exists (select 1
from JobRoleMap
where j.ID = JobID)
e restituisce solo i lavori con i ruoli che desideri