Domanda

I would like to achieve the desired sql using Nhibernate QueryOver (below) using the nHibernate code (below). The query generated does not produces any error, however, it does not do the inner join and as a result, the dataset return null for those properties using the managerAlias and supervisorAlias.

EDIT: I have included all relevant mapping/entity classes.

how can I achieve this?

desired sql:

select head.EmployeeID
        ,e1.EmployeeFirstName
        ,e1.EmployeeLastName
        ,head.SupervisorID
        ,e2.EmployeeFirstName
        ,e2.EmployeeLastName
        ,head.ManagerID
        ,e3.EmployeeFirstName
        ,e3.EmployeeLastName
from dbo.EvaluationHead head
inner join dbo.Employee e1 on head.EmployeeID = e1.EmployeeID
inner join dbo.Employee e2 on head.SupervisorID = e2.EmployeeID
inner join dbo.Employee e3 on head.ManagerID = e3.EmployeeID

I have the following code in my application:

                Employee employeeAlias = null;
                Employee managerAlias = null;
                Employee supervisorAlias = null;

                EvaluationHead headAlias = null;

                var query = session.QueryOver<EvaluationHead>(() => headAlias)
                                    .JoinQueryOver(() => headAlias.Employee, () => employeeAlias)
                                    .JoinQueryOver(() => headAlias.Manager, () => managerAlias)
                                    .JoinQueryOver(() => headAlias.Supervisor, () => supervisorAlias)
                                    .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 => h.SupervisorID).WithAlias(() => dto.SupervisorID)
                                        .Select(h => supervisorAlias.EmployeeFirstName).WithAlias(() => dto.SupervisorFirstName)
                                        .Select(h => supervisorAlias.EmployeeLastName).WithAlias(() => dto.SupervisorLastName)

                                        .Select(h => h.DateCreated).WithAlias(() => dto.DateCreated))
                                    .TransformUsing(Transformers.AliasToBean(typeof(EvaluationHeadDTO)))
                                    .List<EvaluationHeadDTO>();

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();
    References(x => x.Manager).Column("EmployeeID").Cascade.None(); ;
    References(x => x.Supervisor).Column("EmployeeID").Cascade.None();
}

public EmployeeMap()
{
    Id(x => x.EmployeeID).GeneratedBy.Identity();
    //other properties
    HasMany(x => x.Employees).KeyColumn("EmployeeID").Inverse();
    HasMany(x => x.Managers).KeyColumn("ManagerID").Inverse();
    HasMany(x => x.Supervisors).KeyColumn("SupervisorID").Inverse();
}

entities

public class EvaluationHead
{
    public virtual long EvaluationHeadID { get; set; }

    public virtual long EmployeeID { get; set; }
    public virtual Employee Employee { get; set; }

    public virtual long SupervisorID { get; set; }
    public virtual Employee Supervisor { get; set; }

    public virtual long ManagerID { get; set; }
    public virtual Employee Manager { get; set; }
}

public class Employee
{

    public virtual long EmployeeID { get; set; }
    public virtual string EmployeeFirstName { get; set; }
    public virtual string EmployeeMidName { get; set; }
    public virtual string EmployeeLastName { get; set; }

    public virtual ICollection<EvaluationHead> Employees { get; set; }
    public virtual ICollection<EvaluationHead> Supervisors { get; set; }
    public virtual ICollection<EvaluationHead> Managers { get; set; }

    public Employee()
    {
        Employees = new List<EvaluationHead>();
        Supervisors = new List<EvaluationHead>();
        Managers = new List<EvaluationHead>();
    }
}
È stato utile?

Soluzione

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:

  1. fully mapped EvaluationHead (fluent mapping would be similar),
  2. mapping of the int properties representing the ID columns and
  3. 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

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top