I have a bidirectional relationship in NHibernate:
<class name="Child" table="Children">
<many-to-one name="Parent" column="ParentId" not-null="true" />
</class>
<class name="Parent">
<set name="Children" lazy="true" table="Children" cascade="all">
<key column="ParentId" not-null="true" />
<one-to-many class="Child" />
</set>
</class>
How do I save it without setting inverse="true" and setting Parent property on Child?
I do not want to do this because it does not make much sense from the POCO perspective.
Alternatively, is it possible to intercept Add called on NHibernate proxy collection (Children)?
In that case, I would just put the Parent setting logic here.
Unless you're willing to make the foreign key nullable and accept an insert followed by an update, that's the way bidirectional one-to-many works in NHibernate.
I do have a generic implementation of this pattern that you can use... it's a proof of concept; it can be useful or not, depending on how you look at it, as it kinda breaks the POCO approach, but... well, here it is:
public interface IHaveParent<T>
{
T Parent { get; set; }
}
public interface IHaveMany<T>
{
ICollection<T> Children { get; }
}
public static class OneToManyHelper
{
public static void AddChild<TParent, TChild>(this TParent parent,
TChild child)
where TChild : IHaveParent<TParent>
where TParent : IHaveMany<TChild>
{
parent.Children.Add(child);
child.Parent = parent;
}
}
With this, you can all AddChild on any parent.
The problem with intercepting Add calls is that you'd always need to instantiate your collections using a special method (which, again, is not POCO).
Related
I am using NHibernate 3.1.0.4000 and AutoMapper 2.0.0.0 in a WCF. I have a parent-child relationship I want to maintain from the "many" end. I have no problems maintaining the objects if I do it from the "one" end but in this case that does not make sense. My issue is no matter how I change my mappings, POCOs, etc. the parent object when I attempt to add a child is null in the child causing the insert to fail. What am I missing to get the parent property in the child to populate?
I have a parent-child relationship defined in the following tables:
Create Table Attribute (AttributeUID uniqueidentifier, LongName varchar(20))
Create Table AnswerOption (AnswerOptionID int, AttributeUID uniqueidentifier)
I want the Attribute (parent) to be the owner so I declare the relationship in that mapping file and not in the AnswerOption (child). Though, I have tried with having the relationship bidirectional as well and that has not changed any behaviors in my tests. My mappings apear as follows. Attribute:
<class name="RCAttribute" table="rcs.tblAttribute">
<cache usage="read-write"/>
<id name="ID">
<column name="AttributeUID" />
<generator class="guid" />
</id>
<property name="LongName" type="string" not-null="true" length="200" column="LongName" />
<bag name="AnswerOptions" lazy="true" inverse="true" cascade="all">
<key column="AttributeUID"/>
<one-to-many class="AnswerOption" />
</bag>
</class>
AnswerOption:
<class name="AnswerOption" table="rcs.tblAnswerOption" lazy="true">
<cache usage="read-write"/>
<id name="ID">
<column name="AnswerOptionID" />
<generator class="native" />
</id>
</class>
Attribute Class:
[Serializable]
public class RCAttribute
{
public virtual Guid ID { get; set; }
public virtual string LongName { get; set; }
public virtual ICollection<AnswerOption> AnswerOptions { get; set; }
public RCAttribute() { ID = new Guid("00000000-0000-0000-0000-000000000000"); }
}
AnswerOption Class:
[Serializable]
public class AnswerOption
{
public virtual int ID { get; set; }
public AnswerOption() { ID = 0; }
}
My test procedure looks like this;
public void CreateAnswerOption()
{
AnswerOption newOpt = new AnswerOption();
Attribute.AnswerOptions.Add(newOpt);
Attribute = rc.RCAttributeSave(Attribute);
}
When it goes to create this the Attribute property of the AnswerOption is null so it cannot insert since the parent cannot be null in the child in this case. What am I missing to get it to populate the parent property on the child and be able to insert?
Mapping the collection as inverse without mapping the many-to-one on the other side makes no sense.
You have three options:
Use a bidirectional mapping with inverse="true" on the collection side and set the many-to-one propery (parent reference) in your code before saving the child (yes, I read that you don't want to do it that way).
Only map the collection side (not inverse). NHibernate will then first insert the child with NULL as parent reference, but will update it with the correct parent ID in the same transaction. So you can't have a not null constraint on the parent ID column in the child table (at least it must be deferrable).
(This option only works with NHibernate 3.2.0 or newer) Same as option 2, but add not-null="true" to the key tag in the collection mapping. Then NHibernate will insert the child with the parent ID already set.
How do I map relationship, where child endpoint is exposed via Id property and not via whole Parent object?
Here is the example:
class Parent {
public Guid Id { get; set; }
public List<Child> Chlidren { get; set; }
}
class Child {
public Guid Id { get; set; }
public Guid ParentId { get; set; }
}
Here are the equivalent mappings I'm using:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Blabla"
namespace="Blabla"
auto-import="false">
<typedef name="ChildrenList" class="Blabla" />
<class name="Parent" table="Parent" lazy="false">
<id name="Id" column="ID" type="Guid">
<generator class="guid" />
</id>
<bag name="Children" table="Child"
cascade="save-update"
collection-type="ChildrenList"
lazy="false">
<key column="ParentID" not-null="true" />
<one-to-many class="Child" />
</bag>
</class>
<class name="Child" table="Child" lazy="false">
<id name="Id" column="ID" type="Guid">
<generator class="guid" />
</id>
<!-- How to map ParentID here? -->
</class>
</hibernate-mapping>
When I create a parent, add some children to Children collection and then save the parent, everything is fine. But if save a parent object first, then create a child, setting its ParentID property to ID of the parent, then I get
NHibernate.PropertyValueException:
not-null property references a null or transient value Child._Parent.ChildrenBackref
All attempts to map many-to-one relationship resulted in different exceptions while creating NHibernate configuration. Mostly about object type mismatch.
I'm sure NHibernate is capable to handle this scenario. There must something fairly basic that I miss.
EDIT:
I think it make sense to the example test, which fails with above exception:
var child = new Child(Create.Saved<Parent>().Id); // this sets the ParentId property
this.Repository.Save(child); // here I get the exception
My thoughts why NHibernate is raising this: Children property of Parent class mapped in a way that says that a child cannot exist without a parent (<key column="ParentID" not-null="true" />). When I try to persist a child, NHibernate tries to resolve this relationship (to find a parent this child relates to) and fails, since being given no child endpoint (which otherwise would be ParentId property) in the mapping, it check for its own Child._Parent.ChildrenBackref endpoint, whatever it is.
This looks like a desired solution: Mapping ParentId property as child endpoint of the relationship. This would force NHibernate to resolve a parent by using value of ParentId property as parent's primary key.
The thing is I don't know if it's possible.
The one-to-many / many-to-one relationships you have in NHibernate always needs to have a dominant side (i.e. the side that manages the "saving").
<bag name="Children" table="Child"
cascade="save-update"
collection-type="ChildrenList"
lazy="false">
<key column="ParentID" not-null="true" />
<one-to-many class="Child" />
</bag>
The above is a one-to-many relationship where the dominant side is the parent. That means, you save the parent ... and that will save the parent first, then, the children (with the ParentId being null), then a subsequent update will be issued to set the child.ParentId.
Note:
The child is inserted first with ParentId=null ... if you have a db or mapping restriction to say ParentId cannot be null, this action will fail.
<bag name="Children" table="Child"
cascade="save-update"
collection-type="ChildrenList"
lazy="false"
inverse=true>
<key column="ParentID" not-null="true" />
<one-to-many class="Child" />
</bag>
Note the inverse=true attribute. This means the child object is dominant in the relationship, meaning the child object is in charge. The parent will be inserted, then the Id will be assiged to the child.ParentId, and then the child will be inserted with the ParentId already set.
In many cases, of course, you want to go either way. The easiest way to do this is to manage the relationship on both ends (unfortunately, you have to do this yourself).
On the Parent, you have a method:
public void AddChild(Child child)
{
Children.Add(child);
child.ParentId = Id;
}
public void RemoveChild(Child child)
{
Children.Remove(child);
child.ParentId = null;
}
On the Child, you have a method:
public void SetParent(Parent parent)
{
ParentId = parent.Id;
parent.Children.Add(this);
}
Using these methods to Add/Remove/Set, both sides are consistent after the action is performed. It, then, wouldn't matter whether you set inverse=true on the bag or not.
see http://www.nhforge.org/doc/nh/en/index.html#collections-example
I have the following class
public class Person
{
private IList<Person> _children;
public IEnumerable<Person> Children { get; }
public void AddChild(Person child)
{
// Some business logic and adding to the internal list
}
}
What changes would I have to make for NHibenrate to be able to persist the Child collection (apart from making everything virtual, I know that one).
Do I have to add a setter to the children property which does something like a _children.Clear(); _children.AddRange(value). Currently the model expresses my intent quite nicely but I'm not sure how much alteration is need for NH to be able to help me out with persistence.
NHibernate is able to map private fields. Access and naming strategies are discussed in the property section of the reference documentation.
Making your public members virtual is required for proxies to work. These will usually be runtime-generated subclasses of your entity classes.
In this example mapping the field _children will be Children in HQL and Criteria queries.
<class name="Person" table="person">
<bag name="Children" access="field.camelcase-underscore">
<key column="parentid" />
<one-to-many class="Person" />
</bag>
</class>
I'm looking to create a many to many relationship using NHibernate. I'm not sure how to map these in the XML files. I have not created the classes yet, but they will just be basic POCOs.
Tables
Person
personId
name
Competency
competencyId
title
Person_x_Competency
personId
competencyId
Would I essentially create a List in each POCO for the other class? Then map those somehow using the NHibernate configuration files?
You can put the many-to-many relation to either class, or even to both. This is up to your domain model. If you map it to both, one of them is inverse.
class Person
{
// id ...
IList<Competency> Competencies { get; private set; }
// you domain model is responsible to manage bidirectional dependencies.
// of course this is not a complete implementation
public void AddCompetency(Competency competency)
{
Competencies.Add(competency);
competency.AddPerson(this);
}
}
class Competency
{
// id ...
IList<Person> Persons { get; private set; }
}
Mapping:
<class name="Person">
<id ....>
<bag name="Competencies" table="Person_x_Competency">
<key column="personId"/>
<many-to-many class="Competency" column="competencyId"/>
</bag>
</class>
<class name="Competency">
<id ....>
<bag name="Persons" table="Person_x_Competency" inverse="true">
<key column="competencyId"/>
<many-to-many class="Person" column="personId"/>
</bag>
</class>
Only make it bidirectional if you really need it.
By the way: it is much better to write the classes first and create the database design afterwards. The database can be exported from the mapping files. This is very useful.
My class has a field of type Dictionary<string, List<string>>. What's the best way to map it with NHibernate? I'd better leave it as a field, don't want to expose it.
Thanks a lot!
ulu
You can't directly map it. There are two rules to consider:
Always use interfaces for collections (eg. IList<T>, IDictionary<K,V>)
NH does not support nested collections. I've never seen an application for it before
and never heard someone requesting it.
Put your list of string into a class and use interfaces:
class StringList
{
IList<string> Strings { get; private set; }
}
class Entity
{
private IDictionary<string, StringList> stringDict;
}
You might even see some advantages of having such a class.
Mapping:
<class name="Entity">
...
<map name="stringDict" table="Entity_StringDict" access="field">
<key column="Entity_FK"/>
<index column="Key" type="System.String"/>
<composite-element class="StringList">
<bag name="Strings" table="Entity_StringDict_Strings">
<key column="Entity_StringDict_FK"/>
<element type="System.String" column="String"/>
</bag>
</composite-element>
</map>
</class>
Maps to three Tables:
Table Entity
Table Entity_StringDict
Column Entity_FK
Column Key
Table Entity_StringDict_Strings
Column Entity_StringDict_FK
Column String