Reputation: 177
I have a view that lists the cities and I have a table that saves the code of the city per individual. Now the field on the individual table CAN be null, but when I tried to do this it tries to insert into the view and it throws the following exception.
object references an unsaved transient instance - save the transient instance before flushing or set cascade action for the property to something that would make it autosave. Type: iProduct.City, Entity: iProduct.City
If I try to set the mappings on the CaseIndividualMap to .Not.Insert or Not.Update then it ignores it and stores null but if it has a value it gets ignored as well.
Any ideas on how to map this correctly would be greatly appreciated.
public class CityMap : ClassMap<City>
{
public CityMap() {
Table("vCities");
ReadOnly();
LazyLoad();
Id(x => x.Code).Column("Code");
Map(x => x.Description).Column("Description");
Map(x => x.StateCode).Column("StateCode");
}
}
public class CaseIndividualMap : StagingBaseMap<CaseIndividual>
{
public CaseIndividualMap()
{
Table("CaseIndividuals");
Map(x => x.CaseId);
References(x => x.City)
.Column("CityCode")
.Cascade.None()
.Nullable();
}
}
BLL
public void AddIndividual(CaseIndividual individual, AuthenticatedUser user)
{
using (var transaction = _session.BeginTransaction())
{
_session.Save(individual);
transaction.Commit();
}
}
Upvotes: 2
Views: 687
Reputation: 123901
The exception thrown and the story described do not match together. Because the exception:
object references an unsaved transient instance ...
... Type: iProduct.City, Entity: iProduct.City
Says: There is a new instance of the City, referenced by CaseIndividual
, when the
...
_session.Save(individual);
transaction.Commit();
is called. Other words, to get this type of exception the code had to look like this:
var city = new City(); // or other way how to get reference (not null)
...
individual.City = city;
What does it mean? If we do not want to assign the existing City (already persisted, the one returned by the mapped view
) - The code must look like this:
...
individual.City = null; // here we go
_session.Save(individual);
transaction.Commit();
And the above code snippet, will never throw "..unsaved transient instance..." exception, because there is none passed to session. There is no need for a special Mapping of the CaseIndividualMap
, but...
The Id
of the entity City
is mapped as Code. I guess, it is not of int
type. It would be a string
/varchar
.
Now, NHibernate must now, if the incoming instance of a City
is already persisted or if it is representing "new" (transient) instance. To make it easier and explicit, we have to extend the "id" mapping of a City
:
public CityMap() { Table("vCities"); ... Id(x => x.Code) .Column("Code") .GeneratedBy.Assigned() // NHibernate expects that ID is managed by us .UnsavedValue("verydummyvalue or even null")
Read here more about defaults of the unsaved-value: 9.4.2. Updating detached objects
The trick above, the assinged id and/or unsaved-value are correctly instructing NHibernate:
When individual is coming, with assigned City.. which id (i.e.: code) differentiate from
unsaved-value
... do expect it is not transient - do not try to INSERT it
Upvotes: 3