문제

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 schema

Phillip 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 {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

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top