views:

161

answers:

2

This is a problem of unidirectional one-to-one mapping in NHibernate.

Student.cs

public class Student
{
    public int ID { get; set; }
    public int Roll { get; set; }
    public int RegNo { get; set; }
    public string Name { get; set; }

    public StudentDetail StudentDetail { get; set; }
}

StudentDetail.cs

public class StudentDetail
{
    public int ID { get; set; }

    public string Father { get; set; }
    public string Mother { get; set; }
}

How can I map these classes (how do the hbm mapping files look like) to the following case of one-to-one relationship?

alt text

Please have a look at the classes and the table very carefully.

Where can I put the <many-to-one> tag in Student.hbm.xml or StudentDetail.hbm.xml? If I put it in Student.hbm.xml, how can I map the column StudentDetail.StudentID, coz it is in a different table?

So this mapping:

<class name="Student" table="Student">
    <id name="ID" column="ID">
      <generator class="native"/>
    </id>

    .......

    <many-to-one class="StudentDetail" name="StudentDetail" column="StudentID" unique="true" cascade="all" />
  </class>

generates the following exception:

{"Invalid column name 'StudentID'."}

On the other hand <many-to-one> can't be placed in StudentDetail.hbm.xml. Coz, StudentDetail.cs doesn't contain any property of type Student.

Can I use <one-to-one>-tag? If yes where should I place it, in Student.cs or StudentDetail.cs? And how should I configure it?

+1  A: 

You can map it as a one-to-many, with the collection property hidden and only its first element publicly exposed:

public class Student
{
    public virtual int ID { get; set; }
    public virtual int Roll { get; set; }
    public virtual int RegNo { get; set; }
    public virtual string Name { get; set; }

    protected virtual IList<StudentDetail> StudentDetails { get; set; }

    public virtual StudentDetail StudentDetail
    {
        get
        {
            if (StudentDetails.Count == 0) return null;
            return StudentDetails[0];
        }
        set
        {
            if (StudentDetails.Count != 0) throw new Exception();
            StudentDetails.Add(value);
            value.Student = this;
        }
    }
}

You could handle the setter better than this - the point is to make sure you don't add multiple rows to the one-to-many. Obviously in this, StudentDetails is mapped but StudentDetail isn't in your .hbm.xml or Fluent mappings.

David M
+2  A: 

Case #1:

In Student...

<one-to-one name="StudentDetail" 
            cascade="save-update,delete" 
            property-ref="Student" />

In StudentDetail...

<many-to-one name="Student" 
             column="StudentID" 
             unique="true" 
             cascade="none" />

Note that you'll have to have a property in your StudentDetail class that refers to a Student oobject (called Student). Also, your cascades might be different depending on your usage. You most likely want the delete cascade in there, though.

The unique="true" ensures the one-to-one mapping on the StudentDetail side.

Case #2:

Just exchange the two mappings, making sure you change the property names to the opposite class.

Look here for more info: http://nhforge.org/blogs/nhibernate/archive/2009/04/19/nhibernate-mapping-lt-one-to-one-gt.aspx

Chris Dwyer
Sorry, I overlooked that. Try this:http://fabiomaulo.blogspot.com/2010/03/conform-mapping-one-to-one.html
Chris Dwyer
I don't think you can do Case #2. Besides, Case #1 is a better design. One of the tables needs to be the master, and it should be Student, since a StudentDetail can't really exist without there being a Student. StudentDetail is logically dependent on Student, so the foreign key should be in StudentDetail (which is Case #1).
Chris Dwyer
I have solved Case#2. And surprisingly I am unable to solve case #1. The details are updated.
JMSA