NHibernate, saving IDictionary : TransientObjectException
-
12-09-2019 - |
Question
In my class Case I have an IDictionary with Entity (a class) as key and Roles (an enum) as value. When trying to save a new instance (non-persisted) of Case, where the IDictionary is filled with new instances of Entity I get the following error:
NHibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing. Type: Entity
These are the classes (Roles is an enum):
public class Case
{
public Case { EntityCollection = new Dictionary<Entity, Roles>(); }
public virtual int Id { get; set; }
public virtual IDictionary<Entity, Roles> EntityCollection { get; set; }
}
and
public class Entity
{
public virtual int Id { get; set; }
}
And mapping is as follows:
<class name="Case" table="[Case]">
<id name="Id" column="Id" type="Int32" unsaved-value="any">
<generator class="hilo"/>
</id>
<map name="EntityCollection" table="CaseEntityRoles"
cascade="save-update" lazy="false" inverse="false">
<key column="CaseId" />
<index-many-to-many class="Entity"
column="EntityId" />
<element column="Roles" type="Roles" not-null="true" />
</map>
</class>
and
<class name="Entity" table="[Entity]">
<id name="Id" column="Id" type="Int32" unsaved-value="0">
<generator class="hilo"/>
</id>
</class>
Sample of the test code:
[Test]
public void Can_add_new_case()
{
var newCase = new Case();
newCase.EntityCollection.Add(new Entity(), Roles.Role1);
newCase.EntityCollection.Add(new Entity(), Roles.Role2);
/* At which point I try to persist newCase and get an exception */
}
In the testcode the newCase-instance is persisted, but the new entities are not. I've tried a lot of different things, like adding a <version
> tag to Entity and messing around with unsaved-value, but nothing seems to help. And as you can see from the mapping I do have cascade="save-update".
Any ideas?
Solution
I think you need to persist the Entity objects that are referenced by the Case first, before you try to persist the Case. I had a similar issue that I solved this way. For example, using the Rhino NHRepository:
[Test]
public void Can_add_new_case()
{
var newCase = new Case();
var entity1 = new Entity();
var entity2 = new Entity();
newCase.EntityCollection.Add(entity1, Roles.Role1);
newCase.EntityCollection.Add(entity2, Roles.Role2);
Rhino.Commons.NHRepository<Entity> entityRepository = new NHRepository<Entity>();
Rhino.Commons.NHRepository<Case> caseRepository = new NHRepository<Case>();
using (UnitOfWork.Start())
{
entityRepository.SaveOrUpdate(entity1);
entityRepository.SaveOrUpdate(entity2);
caseRepository.SaveOrUpdate(newCase);
}
}
OTHER TIPS
What happens if you set inverse to true ?
I dont know whether this wil solve your problem ...
What you could do, is make use of the Repository pattern, and create a 'CaseRepository' class. In that repository, you will have a Save method which would Save a given Case. In that save method, you could loop over all the entities for the given Case and explicitly call 'SaveOrUpdate' for each Entity.
I also wonder why you are using a Dictionary for this issue ?