0
votes

Hi I'm trying to map a one-many parent-child relationship in NHibernate using XML mapping. Easy enough if the parent class instances store the collection of children in a list, but I want to use a dictionary. Can anyone point me to an example that shows how to set up this mapping in XML?

In other words I want my parent class to look like this

public class Parent {

   IDictionary<string, Child> children; // key should be Child.Name

}

It's a standard primary key-foreign key relationship in the database. The Child table has Name column which should be mapped to the dictionary key.

Thanks

2

2 Answers

-1
votes

Child.Name can't be both a property and the dictionary key.

4
votes

This certainly is possible. The Hibernate (not NHibernate) documentation does a good job of explaining this. 7.3.3. Bidirectional associations with indexed collections. There is an issue in the NHibernate bug tracker to have this error in the documentation corrected: NH-3554

Below is a complete example of how to do this...

Example

Entity Classes

public class Parent
{
    public virtual int Id { get; private set; }

    private IDictionary<string, Child> _children = new Dictionary<string, Child>();
    public virtual IDictionary<string, Child> Children
    {
        get { return _children; }
        private set { _children = value; }
    }

    public virtual void AddChild(Child child)
    {
        child.Parent = this;
        _children[child.Name] = child;
    }
}

public class Child
{
    public virtual int Id { get; private set; }
    public virtual Parent Parent { get; protected internal set; }
    public virtual string Name { get; set; }
}

NHibernate Mappings

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="so" namespace="so.Q3398624">
  <class name="Parent">
    <id name="Id">
      <column name="Id" />
      <generator class="hilo" />
    </id>
    <map name="Children" inverse="true" cascade="all-delete-orphan">
      <key column="ParentId" />
      <index column="Name" type="string" />
      <one-to-many class="Child" />
    </map>
  </class>
  <class name="Child">
    <id name="Id">
      <column name="Id" />
      <generator class="hilo" />
    </id>
    <many-to-one name="Parent" column="ParentId" cascade="save-update" not-null="true" unique-key="U1" />
    <property name="Name" not-null="true" unique-key="U1" />
  </class>
</hibernate-mapping>

Tables

CREATE TABLE dbo.Parent (
    Id int NOT NULL PRIMARY KEY
);

CREATE TABLE dbo.Child (
    Id int NOT NULL PRIMARY KEY,
    ParentId int NOT NULL,
    Name nvarchar(255) NOT NULL,
    FOREIGN KEY (ParentId) REFERENCES dbo.Parent (Id),
    UNIQUE (ParentId, Name)
);

Insert some records...

var parent = new Parent();
parent.AddChild(new Child { Name = "abc" });
parent.AddChild(new Child { Name = "123" });

session.Save(parent);

Final Notes

Be careful when changing a Child's Name. I think the best approach would be to remove the Child from the Parent, change its name, and finally re-add it to the parent. Otherwise Child.Name and Parent.Children.Keys would be out of sync.