Bidi associations and NHibernate mapping
Asked Answered
H

1

1

I have classes BidiParent and BidiChildList which link parents and children in a bidirectional parent-child relationship. If a child's parent is updated by e.g. the service layer, the old and new parents' children lists are automatically updated to reflect the change. Likewise, if a parent's children list is updated by e.g. adding a new child, the child's parent is automatically changed as is the old parent's children list. I want to try to build up a "smart" domain model.

First question, obviously, is: is this even a good idea?

Second question is: is it possible to tell NHibernate to access and modify the field _Children or _Parent, but to consider the property Children or Parent to be entirely synonymous with the field? That NHibernate should load and save the internal fields, but HQL or LINQ queries should use the public properties?

public class BidiParent<C, P> { ... }

public class BidiChildList<C, P> : IList<C> { ... }

public class Parent {
    public string Name { get; set; }
    public IList<Child> Children {
        get { return ChildrenBidi; }
        set { ChildrenBidi.Set(value); }
    }
    private BidiChildList<Child, Parent> ChildrenBidi {
        get { return BidiChildList.Create(this, p => p._Children, c => c._Parent, (c, p) => c._Parent = p); }
    }
    internal IList<Child> _Children = new List<Child>();
}

public class Child {
    public string Name { get; set; }
    public Parent Parent {
        get { return ParentBidi.Get(); }
        set { ParentBidi.Set(value); }
    }
    private BidiParent<Child, Parent> ParentBidi {
        get { return BidiParent.Create(this, p => p._Children, () => _Parent, p => _Parent = p); }
    }
    internal Parent _Parent = null;
}

[Test]
public void MultilevelConstruction_Succeeds() {
    var p = new Parent {
        Name = "Bob",
        Children = new List<Child> {
            new Child { Name = "Kate" },
            new Child { Name = "Billy" }
        }
    };
    Assert.AreEqual(2, p.Children.Count);
    Assert.AreEqual("Kate", p.Children[0].Name);
    Assert.AreEqual("Billy", p.Children[1].Name);
    Assert.AreSame(p, p.Children[0].Parent);
    Assert.AreSame(p, p.Children[1].Parent);
}
Hattie answered 12/10, 2008 at 14:10 Comment(0)
H
1

Answering my own question: I need to use a naming strategy, as detailed in the docs. RTFM right?

<class name="Parent">
    <property name="Name" />
    <bag name="Children" access="field.pascalcase-underscore">
        <key />
        <one-to-many class="Child" />
    </bag>
</class>
<class name="Child">
    <property name="Name" />
    <many-to-one name="Parent" access="field.pascalcase-underscore" />
</class>
Hattie answered 12/10, 2008 at 20:22 Comment(2)
Did you ever see any drawbacks using this solution?Dignitary
Sometimes I think "bugger it" allow my foreign key to be a nullable column as all of this extra plumbing is then redundant.Dignitary

© 2022 - 2024 — McMap. All rights reserved.