1

I've an object containing a collection:

public class Teacher
{
    public virtual string Name {get;set;}
    public virtual ISet<Student> Students {get;set;}
    // ...
}

public class Student
{
    public virtual string LastName {get;set;}
    // ... 
}

The DetachedCriteria of type "Teacher" populates correctly the "Teacher" object with a collection of the mapped students.

Example:

If teacher 'X' contains 3 students 'A', 'B' and 'C': one object (the 'Teacher') is returned containing a collection of 3 objects (students 'A', 'B' and 'C').

The mapping configuration is:

<class table="Teacher" name="...">
    <id name="Id" ... />
    <property name="Name" column="Name" />
    <set name="Students" table="Student" inverse="true">
        <key column="TeacherId" />
        <one-to-many class="Student" />
    </set>
</class>

I want to retrieve from the database one line/student. I've created a new class as follow:

public class TeacherWithFlattenCollection
{
    public virtual string Name {get;set;} // The name of the teacher
    public virtual string  LastName {get;set;} // The name of the student
}

The mapping configuration is:

<class table="Teacher" name="...">
    <id name="Id" ... />
    <property name="Name" column="Name" />
    <join table="Student" fetch="join">
        <key column="TeacherId" />
        <property name="LastName" column="LastName" />
    </join>
</class>

By using this new class for the DetachedCriteria, I receive the correct number of items but they are not correctly populated:

  • Name = 'X', LastName = 'A'
  • Name = 'X', LastName = 'A'
  • Name = 'X', LastName = 'A'

Instead of:

  • Name = 'X', LastName = 'A'
  • Name = 'X', LastName = 'B'
  • Name = 'X', LastName = 'C'

Do I miss something in the mapping configuration?

Radim Köhler
  • 122,561
  • 47
  • 239
  • 335
prorace
  • 265
  • 5
  • 17

1 Answers1

0

I do not have an answer how to properly tweak standard NHibernate usage - as requested above. But I can share how would I (hope some others will as well) do that.

Basic of ORM is the bidirectional mapping. It is bidirectional in DB, so it should be in C# as well:

// as is
public class Teacher
{
    public virtual ISet<Student> Students {get;set;}
    // ...
}

// has reference to Teacher
public class Student
{
    public virtual Teacher Teacher {get;set;}
    // ... 
}

The Student extended mapping:

<class table="Student" name="...">
    <id name="Id" ... />
    ...
    <many-to-one name="Teacher" column="TeacherId" ... />
</class>

BTW: And now, ONLY now, we can use mapping with inverse="true" on Teacher side, because we can inverse the control - to Student. That means, that we always should/have to assign both sides

var student = ...;
var teacher = ...;
teacher.Students.Add(student);
student.Teacher = teacher; // this is the ONLY way how to use inverse

Querying: And now we can use easy querying - from the Student side:

// aliasing to support type safe access to properties
Teacher teacher = null;
Student student = null;
TeacherWithFlattenCollection dto = null;

// now we query from Student side, to get proper data    
var query = session.QueryOver<Student>(() => student)
    .JoinAlias(x => x.Teacher, () => teacher)
    .Where(...)
    .SelectList(list => list
           .Select(x => x.LastName)    .WithAlias(() => dto.LastName)
           .Select(x => x.Teacher.Name).WithAlias(() => dto.Name)
    )
    .TransformUsing(Transformers.AliasToBean<TeacherWithFlattenCollection >())

NOTE: (we do not need to use DTO like above, check how to use a little help of this transformer - read more here: How to partially project a child object with many fields in nHibernate)

Community
  • 1
  • 1
Radim Köhler
  • 122,561
  • 47
  • 239
  • 335
  • I've already the double mapping, and the SQL query generated returns (in the SQL Server Management Studio) the correct result (3 lines with 3 differents students). The problem is more on how the objects are populated. I'll try the Transformers. – prorace Nov 23 '15 at 15:53