NHibernate QueryOver, где существует на многие ко многим - PullRequest
3 голосов
/ 05 мая 2011

У меня есть отношение многие ко многим, которые я пытаюсь запросить. Моя проблема очень похожа на ту, что подробно описал Филипп Хейдон здесь , поэтому я собираюсь свободно позаимствовать его диаграммы и объяснения.

У Филиппа были следующие отношения «многие ко многим» между Джобсом и ролями (извините, я пока не могу вставлять изображения):
схема данных

Филиппу нужно было запросить все роли, которые не были назначены на работу. Его решение было следующим:

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();

Это было очень полезно, однако, похоже, что в этом решении есть сущность для каждой таблицы; Работа, JobRole и Роль. JobRole имеет как работу, так и роль. Вероятно, что-то вроде этого:

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;}
}

Это противоречит шаблону, который я видел при моделировании отношений «многие ко многим», особенно в точных примерах архитектуры и из рекомендаций, приведенных здесь. В этих примерах и в моем случае у меня есть только два класса, Job и Role. Примерно так:

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;}
}

В моем случае мне нужно найти все рабочие места, которые имеют только роли. Я пробовал что-то вроде этого

  Job job = null;
  Role role = null;

  var jobs = Session.QueryOver(() => job)
                    .WithSubquery
                    .WhereExists(
                          QueryOver.Of(() => role)
                          .JoinAlias(() => role.Jobs, () => job))
                    .List().ToList();

но NHibernate требует, чтобы проекция для выбора в WhereExists и жалуется, если он не предоставлен, что имеет смысл для меня.

Можно ли даже с моей моделью выполнить подзапрос QueryOver с WhereExists?

Заранее спасибо.

1 Ответ

7 голосов
/ 05 мая 2011
var jobs = session
     .QueryOver<Job>()
     .WhereRestrictionOn(j => j.Roles)
         .IsNotEmpty
     .List();

Отображение для работы:

 public class Job
    {
        public virtual int ID { get; set; }
        public virtual string Name { get; set; }
        public virtual IList<Role> Roles { get; set; }
    }

и отображение

 <class name="Job">
       ...
       <bag name="Roles" table="JobRoleMap" fetch="join" lazy="false" >
          <key column="JobID" />
          <many-to-many column="RoleID" class="Role" />            
       </bag>
    </class>

В моем случае выведите следующий SQL (украшенный):

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)

и возвращает только задания с ролями, которые вы хотите

...