Using
.NET Core SDK (reflecting any global.json): Version: 2.2.300 Commit: 73efd5bd87
and
Nhibernate 5.2.5
Have the following entities
public class Customer : Entity {
public Customer() {
Initialize();
}
public virtual string Name { get; set; }
public virtual string LegalName { get; set; }
public virtual string VATCode { get; set; }
public virtual ICollection<Site> Sites { get; set; }
public virtual DateTime Created { get; set; } = DateTime.UtcNow;
private void Initialize() {
Sites = new List<Site>();
}
}
public class Site : Entity {
public virtual string Address { get; set; }
public virtual string City { get; set; }
public virtual string Country { get; set; }
public virtual Customer Customer { get; set; }
}
with the following mappers
internal class CustomerConfiguration : ClassMapping<Customer> {
public CustomerConfiguration() {
Table( TableNames.CustomersTable );
Id( x => x.EntityID, im => {
im.Column( "CustomerID" );
im.Generator( Generators.Identity );
} );
[... code omitted for brevity ...]
Set<Site>( property => property.Sites,
collection => {
collection.Fetch( CollectionFetchMode.Join );
collection.Lazy( CollectionLazy.Lazy );
collection.Cascade( Cascade.Persist.Include( Cascade.DeleteOrphans ) );
collection.Inverse( true );
collection.Key( keyMapping => {
keyMapping.Column( "CustomerID" );
} );
},
mapping => {
mapping.OneToMany( relationalMapping => {
relationalMapping.Class( typeof( Site ) );
} );
} );
}
}
internal class SiteConfiguration : ClassMapping<Site> {
public SiteConfiguration() {
Table( TableNames.SitesTable );
Id( x => x.EntityID, im => {
im.Column( "SiteID" );
im.Generator( Generators.Identity );
} );
[... code omitted for brevity ...]
ManyToOne( x => x.Customer, mm => {
mm.Column( "CustomerID" );
mm.NotNullable( true );
} );
}
}
I guess this mapping is not correct because if I do something like
using ( var session = sessionFactory.OpenSession() ) {
var customer = new Customer() {
Name = $"Customer 1",
LegalName = $"Customer 1 LLC",
VATCode = "xxxxxxxxx",
Created = DateTime.UtcNow
};
customer.Sites.Add( new Site() {
Address = $"Address Customer 1",
City = $"City Customer 1",
Country = $"Country Customer 1",
Customer = customer
} );
session.Save( customer );
}
I get the following exception
Unhandled Exception: NHibernate.TransientObjectException: 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: Nhibernate.ConsoleApp.Entities.Site, Entity: [Site 0]
Any suggestion?
EDIT
Actually the problem was on a different area. it was because of the presence of two registered Listeners (IDeleteEventListener
and IUpdateEventListener
). Whenever I add those Update and delete does not work. However I was able to discover the problem thanks to Roman Artiukhin comment
Exception is telling that the root entity Customer
is holding the reference to other (Site
) entity which is transient entity. That is why, root entity cannot be persisted.
Scenario 1: No cascade options were configured.
In this case the child or referenced objects must be saved first.
using ( var session = sessionFactory.OpenSession() ) {
var customer = new Customer() {
Name = $"Customer 1",
LegalName = $"Customer 1 LLC",
VATCode = "xxxxxxxxx",
Created = DateTime.UtcNow
};
Site site = new Site() {
Address = $"Address Customer 1",
City = $"City Customer 1",
Country = $"Country Customer 1",
Customer = customer
}
customer.Sites.Add(site);
session.Save( customer );//<--Save the master
session.Save( site );//<--Save the site
...
...
...
session.Flush();//<--You are calling this somewhere in your code
}
This should work.
Scenario 2: Cascade options were not configured for all INPUT; UPDATE or DELETE operations.
In this case the configuration must be changed or child or referenced objects must be saved first.
You have already:
collection.Inverse( true );
DeleteOrphans | Persist
values for Cascade
through extension method Include
.so this is not a problem.
Scenario3: Related transient objects where instantiated and associated to the persitent object but no save operation is to be performed on those objects.
In this case the trainsient objects must be detached from the current session through the command:ISession.Evict(obj)
Instead of Evict
, you can call the Save
to attach those objects explicitly as mentioned above.
For more insight, have a look at documentation which explains various strategies.