I have a many-to-many relationship which I am trying to query. My problem is very similar to one detailed by Phillip Haydon here so I am going to liberally borrow his diagrams and explanation.
Phillip had the following many-to-many relationship between Jobs and Roles (sorry I can't embed images yet):
data schemaPhillip needed to query all the roles which were not assigned to a job. His solution was the following:
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();
This was very helpful however it appears that in this solution there is an entity for each table; Job, JobRole, and Role. JobRole has both a Job and a Role. Probably something like this:
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 开发者_JAVA技巧{get;set;}
}
This conflicts with the pattern I have seen for modeling many-to-many relationships, specifically in sharp architecture examples and from advice here. In those examples and in my case, I have only two classes, Job and Role. Something like this:
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;}
}
In my case, I need to find all jobs which only have roles. I've tried something like this
Job job = null;
Role role = null;
var jobs = Session.QueryOver(() => job)
.WithSubquery
.WhereExists(
QueryOver.Of(() => role)
.JoinAlias(() => role.Jobs, () => job))
.List().ToList();
but NHibernate requires that the projection for the select in the WhereExists and complains if one is not provided, which makes sense to me.
With my model is it even possible to do a QueryOver Subquery with WhereExists?
Thanks in advance.
var jobs = session
.QueryOver<Job>()
.WhereRestrictionOn(j => j.Roles)
.IsNotEmpty
.List();
Mapping for Job:
public class Job
{
public virtual int ID { get; set; }
public virtual string Name { get; set; }
public virtual IList<Role> Roles { get; set; }
}
and mapping
<class name="Job">
...
<bag name="Roles" table="JobRoleMap" fetch="join" lazy="false" >
<key column="JobID" />
<many-to-many column="RoleID" class="Role" />
</bag>
</class>
In my case produce following SQL (beautified):
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)
and returns only jobs with roles as you want
精彩评论