Question

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();
    }
}
Was it helpful?

Solution

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

Unsaved-value and City mapping

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

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top