Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fluent NHibernate: How to create one-to-one bidirectional mapping?

I had a similar question to Fluent NHibernate: How to create one-to-many bidirectional mapping? but I was interested in the situation when I have a one-to-one mapping. For instance

Umbrealla
  ID
  Owner

UmbreallaOwner
  ID
  Umbrella

As we know each umbrella can only be owned by one person and nobody owns more than one umbrella. In a fluent map I would have something like

UmbrellaMap()
{
   Id(x=>x.ID);
   References<UmbrellaOwner>(x=>x.Owner);
}

UmbrellaOwnerMap()
{ 
   Id(x=>x.ID);
   References<Umbrella>(x=>x.Umbrella);
}

When creating the tables fluent will create a field in umbrella referncing the ID of umbrellaOwner and a field in umbrellaOwner referencing umbrella. Is there any way to change the mapping such that only one foreign key will be created but the Umbrella property and the Owner property will both exist? The examples I have seen involve setting the relations up in both directions so adding a new Umbrella looks like

AddUmbrealla(UmbrellaOwner owner)
{
   var brolly = new Umbrella();
   brolly.Owner = owner;
   owner.Umbrella = brolly;
   session.Save(owner); //assume cascade
 }

which seems logical but a bit cumbersome.

like image 906
stimms Avatar asked Mar 31 '11 16:03

stimms


1 Answers

Well, a reference is a reference; one object has a reference to the other. The reverse is not necessarily true.

In your case, you MIGHT get away with a HasOne relationship. However, HasOne is normally for denormalized data. Say you wanted more info about the owner, but you could not change Owner's schema because other code depended on it. You'd create an AdditionalOwnerInfo object, and create a table in the schema in which the OwnerID field of the table was a foreign key to Owner, and also the primary key of the table.

Ayende recommends a two-sided References() relationship in 99.9% of one-to-one cases, where the second object is conceptually separate from the first, but there is an implicit "I alone own exactly one thing" type of relationship. You can enforce the "one and one only" nature of the reference using a Unique().Not.Nullable() modifier set on the References mapping.

To streamline the referential setup, consider defining one object (UmbrellaOwner) as the "parent" and the other (Umbrella) as the "child", and in the parent's property setter, set the child's parent to the current reference:

public class Umbrella
{
  public virtual string ID { get; set; }
  public virtual Owner Owner { get; set; }
}

public class UmbrellaOwner
{
  public virtual string ID { get; set; }
  private Umbrella umbrella;
  public virtual Umbrella Umbrella
  {
    get{
      return umbrella;
    }
    set{
      umbrella = value;
      if(umbrella != null) umbrella.Owner = this;
    }
  }
}

Now, when you assign the child to the parent, the backreference is automagically set up:

var owner = new UmbrellaOwner{Umbrella = new Umbrella()};
Assert.AreEqual(owner, owner.Umbrella.Owner); //true;
like image 135
KeithS Avatar answered Nov 16 '22 03:11

KeithS