The mapping seems to be the culprit. A scenario, you are looking for, is one of the most usual.
So, if we would have EvaluationHead
C# like this:
public class EvaluationHead
{
...
// Employee
public virtual Employee Employee { get; set; }
public virtual int EmployeeID { get; set; }
// Supervisor
public virtual Employee Supervisor { get; set; }
public virtual int SupervisorID { get; set; }
// Manager
public virtual Employee Manager { get; set; }
public virtual int ManagerID { get; set; }
}
The (xml) mapping of the EvaluationHead
like this:
<class ...
//<!-- Employee -->
<many-to-one name="Employee" column="EmployeeID" class="Employee" />
<property name="EmployeeID" column="EmployeeID" insert="false" update="false" />
//<!-- Supervisor -->
<many-to-one name="Supervisor" column="SupervisorID" class="Employee" />
<property name="SupervisorID" column="SupervisorID" insert="false" update="false" />
//<!-- Manager -->
<many-to-one name="Manager" column="ManagerID" class="Employee" />
<property name="ManagerID" column="ManagerID" insert="false" update="false" />
The fluent mapping
public EvaluationHeadMap()
{
Id(x => x.EvaluationHeadID).GeneratedBy.Identity();
Map(x => x.EmployeeID).Not.Insert().Not.Update();
Map(x => x.ManagerID).Not.Insert().Not.Update();
Map(x => x.SupervisorID).Not.Insert().Not.Update();
//other properties
References(x => x.Employee).Column("EmployeeID").Cascade.None();
// Instead of this
// References(x => x.Manager).Column("EmployeeID").Cascade.None(); ;
// References(x => x.Supervisor).Column("EmployeeID").Cascade.None();
// use this
References(x => x.Manager).Column("ManagerID").Cascade.None(); ;
References(x => x.Supervisor).Column("SupervisorID").Cascade.None();
}
So, what we do have now, is:
- fully mapped
EvaluationHead
(fluent mapping would be similar), - mapping of the
int
properties representing the ID columns and - the real references to
Employee
table (Employee, Supervisor, Manager)
We can adjust the query, like this:
var query = session
.QueryOver<EvaluationHead>(() => headAlias)
.JoinQueryOver(() => headAlias.Employee, () => employeeAlias)
// alias without EmployeeID
.JoinQueryOver(() => headAlias.Manager, () => managerAlias)
.JoinAlias(() => headAlias.Supervisor, () => supervisorAlias)
// this is done via mapping
//.Where(() => headAlias.SupervisorID == supervisorAlias.EmployeeID
// && headAlias.ManagerID == managerAlias.EmployeeID)
// .Where instead of And
//.AndRestrictionOn(() => headAlias.KRAApprovedDate).IsNotNull
.WhereRestrictionOn(() => headAlias.KRAApprovedDate).IsNotNull
.SelectList(l => l
.Select(h => h.EvaluationHeadID).WithAlias(() => dto.EvaluationHeadID)
.Select(h => h.Employee.EmployeeID).WithAlias(() => dto.EmployeeID)
.Select(h => employeeAlias.EmployeeFirstName).WithAlias(() => dto.EmployeeFirstName)
.Select(h => employeeAlias.EmployeeMidName).WithAlias(() => dto.EmployeeMidName)
.Select(h => employeeAlias.EmployeeLastName).WithAlias(() => dto.EmployeeLastName)
.Select(h => h.EvaluationStartPeriod).WithAlias(() => dto.EvaluationStartPeriod)
.Select(h => h.EvaluationEndPeriod).WithAlias(() => dto.EvaluationEndPeriod)
.Select(h => h.ManagerID).WithAlias(() => dto.ManagerID)
.Select(h => managerAlias.EmployeeFirstName).WithAlias(() => dto.ManagerFirstName)
.Select(h => managerAlias.EmployeeLastName).WithAlias(() => dto.ManagerLastName)
.Select(h => supervisorAlias.EmployeeFirstName).WithAlias(() => dto.SupervisorFirstName)
.Select(h => supervisorAlias.EmployeeLastName).WithAlias(() => dto.SupervisorLastName)
.Select(h => h.SupervisorID).WithAlias(() => dto.SupervisorID)
.Select(h => h.DateCreated).WithAlias(() => dto.DateCreated))
.TransformUsing(Transformers.AliasToBean(typeof(EvaluationHeadDTO)))
The JOIN is done for us by the mapping. In ORM world, it is the only way how to JOIN (based on mapping). The rest of the query is mostly simplification