How to reference ID property in relationship with NHibernate? - nhibernate

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

Related

NHibernate constraints associations of a parent class are dropped on the subclass

I think that the main issue is that the subclass deletes all constraints related to the base mapped collections.
Using the table per concrete class strategy, I have found that the parent collections are not asociated with the subclasses also in another (maybe related) problem, the associations between Basetypes and ChildTypes are not created either.
I have a schema similar to this:
public class Parent{
public virtual Int64 Id{get; set;}
public virtual IList<Foo> foos{get; set;}
public virtual IList<ParentType> _pts{get; set;}
}
public class child: Parent{
public virtual int chilInt{get; set;}
}
public class BaseType{
public virtual Int64 Id{get; set;}
public virtual Parent ParentReference{get; set;}
}
public class ChildType: BaseType{
public virtual string childBacon{get; set;}
}
Mapping Files
<class name="Parent" abstract="true">
<id name="Id" type="Int64" column="Id" unsaved-value="0">
<generator class="native"/>
</id>
<set name="foos" inverse="false" >
<key column="Id"/>
<one-to-many class="Foo" />
</set>
<set name="pts" inverse="false" >
<key column="Id"/>
<one-to-many class="ParentType" />
</set>
</class>
<union-subclass name="Child" table="Child" extends="Parent">
<property name="childInt" type="int" />
</union-subclass>
<class name="ParentType" abstract="true">
<id name="Id" type="Int64" column="Id" unsaved-value="0">
<generator class="native"/>
</id>
<many-to-one name="ParentReference" class="Parent"/>
</class>
<union-subclass name="ChildType" table="ChildType" extends="ParentType">
<property name="childBacon" type="string" />
</union-subclass>
The result that the child table don't have any relation with foo table.
If you use the <union-subclass> mapping it's clear that there is no direct relation of the foo entries to your child table because the child table only contains the additional things declared in the child class.
When instantiating a child instance with union-subclass mapping you get a row in both, the parent and child tables. And if your child instance contains entries in the foo set, you get some rows in the foo table with relation to the parent table.
Using table per concrete class mapping does not make sense with associations pointing to the parent class (as the foo class not part of your code example seems to do) because then the different derived classes of parent all inherit the foo set but the foo table cannot have foreign keys to all those tables.
Well, there are three common approaches for ORM and inheritance (table per class hierarchy, table per subclass, table per concrete class). <union-subclass>, one you use, is used in table per concrete class and it should be embedded in parent <class>. Read about it here (8.1.5).
Maybe it won't resolve all your issues, but at least it should help with establishing mapping for inheritance.

nhibernate many-to-one parent is always null on insert

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.

NHibernate: bidirectional many-to-one problem

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).

NHibernate - Mapping tagging of entities

I've been wrecking my mind on how to get my tagging of entities to
work. I'll get right into some database structuring:
tblTag
TagId - int32 - PK
Name
tblTagEntity
TagId - PK
EntityId - PK
EntityType - string - PK
tblImage
ImageId - int32 - PK
tblBlog
BlogId - int32 - PK
class Image
Id
EntityType { get { return "MyNamespace.Entities.Image"; }
IList<Tag> Tags;
class Blog
Id
EntityType { get { return "MyNamespace.Entities.Blog"; }
IList<Tag> Tags;
The obvious problem I have here is that EntityType is an identifer but
doesn't exist in the database. If anyone could help with the this
mapping I'd be very grateful.
You don't need the entity type. Take a look at any-type mapping (it stores the type name in the database in the relation table, but you don't need it in the entity model).
See this blog post by ayende.
Edit: tried to write an example.
You could have an own table for each tagged object, this is easy and straight forward, you don't even need any types:
<class name="Tag">
<!-- ... -->
<property name="Name"/>
</class>
<class name="Image">
<!-- ... -->
<bag name="Tags" table="Image_Tags">
<key column="Image_FK"/>
<many-to-many class="Tag" column="TagId "/>
</bag>
</class>
Tried to use some advanced features to map it into a single table, but I think it doesn't work this way:
<class name="Tag">
<!-- ... -->
<property name="Name"/>
<bag name="Objects" table="tblTagEntity" access="noop">
<key column="TagId"/>
<many-to-any id-type="System.Int64" meta-type="System.String">
<meta-value
value="IMAGE"
class="Image"/>
<meta-value
value="BLOG"
class="Blog"/>
<column name="EntityType"/>
<column name="EntityId"/>
</many-to-any>
</bag>
</class>
<class name="Image">
<!-- ... -->
<bag name="Tags" table="tblTagEntity" where="EntityType='IMAGE'">
<key column="EntityId"/>
<many-to-many class="Tag" column="TagId "/>
</bag>
</class>
The tricks here are:
access="noop" to specify the foreign key without having a property in the entity model, see this post.
where="EntityType='IMAGE'" to filter the loaded data.
The problem is that most probably the EntityType is not set to any useful value. This could be fixed somewhere, but I don't think that it is worth the effort.
Someone else has probably a better idea.
Edit 2: another (working) solution
make the association table an entity:
in short:
Tag => TagEntity: not mapped or one-to-many inverse (noop)
TagEntity => Tag: many-to-one
TagEntity => Object: any
Object => TagEntity: one-to-many inverse
This should work straight forward.
classes:
class Tag
{
string Name { get; set; }
}
class TagEntity
{
Tag Tag { get; set; }
object Entity { get; set; }
}
class Image
{
IList<TagEntity> tags { get; private set; }
}
The only drawback seems to be that you have to make sure that the bidirectional associations are consistent without loading to much data. Note that inverse collections are not stored.
Edit 2: Performance notes
When you add / remove tags, you could do a trick. TagEntity has a reference to the tagged entity. The Entity also has a list of TagEntities, but this is marked as inverse. (This means, they are loaded, but not stored.)
You can add and remove tags without loading the Entity an without loading all the tags.
Adding:
Get Tag to add (or load proxy if you have the id of the tag)
Load Entity (just proxy, using session.Load, no db access here)
create new TagEntity, assign tag and entity-proxy
save TagEntity
Removing:
Get TagEntity to remove
delete TagEntity.
Within the session, you don't have this tag assigned to/removed from the TagEntity. This works fine assumed that you only add or remove tags within this transaction.
I you define a list of TagEntities on the Tag, you can do the same, without loading all the TagEntities just to add or remove one.
You could make EntityType an Enum in your code. And/or, you could try making EntityType an actual entity in your database (tblEntityType).
Got Stefans final solution to work! Here's my final mappings:
Image
<bag name="TagEntites" table="tblTagEntity" cascade="all" fetch="join" inverse="true" where="EntityType='EntityImage'">
<key column="EntityId"></key>
<one-to-many class="TagEntity" />
</bag>
TagEntity
<id name="Id">
<column name="TagEntityId"></column>
<generator class="identity" />
</id>
<any name="Entity" id-type="System.Int32" meta-type="System.String">
<meta-value value="EntityImage" class="Image" />
<column name="EntityType"></column>
<column name="EntityId"></column>
</any>
<many-to-one name="Tag" class="Tag" cascade="all" fetch="join">
<column name="TagId"></column>
</many-to-one>

Nhibernate - Mapping List doesn't update List indexes

I'm having one self-referencing class. A child has a reference to its parent and a parent has a list of children. Since the list of children is ordered, I'm trying to map the relation using NHibernate's .
This is my mapping:
<class name="MyClass">
<id name="Id">
<generator class="native"/>
</id>
<list name="Children" cascade="delete" inverse="true">
<key column="ParentId"/>
<index column="ListOrder"/>
<one-to-many class="MyClass"/>
</list>
<many-to-one name="Parent" class="MyClass" column="ParentId"/>
</class>
The problem I'm having is when having a bi-directional mapping child<->parent, the list index (ListOrder) isn't updated in the database when I do my CRUD dance. This means that when I e.g. remove a child, I get holes in the children list after saving to the database and fetching the parent again. If I remove the bidirectionality, by not having a many-to-one from the children to the parent (and no inverse=true), the ListOrder is updated correctly.
Have any of you seen this before? Is there any simple solution?
Yes, it's because of inverse=true, an alternate solution would be to use a set or bag instead of list with order="ListOrder", add the ListOrder column as a property to the MyClass class with an empty setter and a getter that always returns it's index from it's parent's child collection. Like this:
<class name="MyClass">
<id name="Id">
<generator class="native"/>
</id>
<bag name="Children" cascade="delete" inverse="true" order-by="ListOrder">
<key column="ParentId"/>
<one-to-many class="MyClass"/>
</bag>
<property name="ListOrder" column="ListOrder"/>
<many-to-one name="Parent" class="MyClass" column="ParentId"/>
</class>
and the class
public class MyClass
{
public virtual int ID { get; set; }
public virtual IList<MyClass> Children { get; set; }
public virtual MyClass Parent { get; set; }
public virtual int ListOrder
{
get
{
if (Parent == null || !Parent.Children.Contains(this)) return -1;
return Parent.Children.IndexOf(this);
}
set { }
}
}