I've tracked down the bug in the code. In Impl\SessionImpl.cs, SessionImpl.SaveOrUpdateCopy() ends up calling SessionImpl.DoCopy(), which in NHibernate 1.0.1 starts out like this (my comments are marked with
// ***
Code:
public object DoCopy( object obj, object id, IDictionary copiedAlready )
{
if( obj == null )
{
return null;
}
if( obj is INHibernateProxy )
{
LazyInitializer li = NHibernateProxyHelper.GetLazyInitializer( ( INHibernateProxy ) obj );
if( li.IsUninitialized )
{
return Load( li.PersistentClass, li.Identifier ); // EARLY EXIT!
}
else
{
// *** My object is not uninitialized,
// *** so it ends up here
obj = li.GetImplementation();
}
}
if( copiedAlready.Contains( obj ) )
{
return obj; // EARLY EXIT!
}
EntityEntry entry = GetEntry( obj );
if( entry != null )
{
if( id != null && entry.Id.Equals( id ) )
{
return obj; //EARLY EXIT!
}
// else copy from one persistent instance to another!
}
System.Type clazz = obj.GetType();
IClassPersister persister = GetClassPersister( clazz );
object result;
object target;
if( id == null && persister.IsUnsaved( obj ) )
{
copiedAlready[ obj ] = obj;
SaveWithGeneratedIdentifier( obj, Cascades.CascadingAction.ActionCopy, copiedAlready );
result = obj; // TODO: Handle its proxy (reassociate it, I suppose)
target = obj;
}
else
{
if( id == null )
{
id = persister.GetIdentifier( obj );
}
// *** My object gets to here ...
result = Get( clazz, id );
if( result == null )
{
copiedAlready[ obj ] = obj;
SaveWithGeneratedIdentifier( obj, Cascades.CascadingAction.ActionCopy, copiedAlready );
result = obj; // TODO: Could it have a proxy?
target = obj;
}
else
{
target = Unproxy( result );
...
The code reaches the call to Get(), which calls DoLoadByClass():
Code:
private object DoLoadByClass( System.Type clazz, object id, bool checkDeleted, bool allowProxyCreation )
{
if( log.IsDebugEnabled )
{
log.Debug( "loading " + MessageHelper.InfoString( clazz, id ) );
}
IClassPersister persister = GetClassPersister( clazz );
if( !persister.HasProxy )
{
// this class has no proxies (so do a shortcut)
return DoLoad( clazz, id, null, LockMode.None, checkDeleted );
}
else
{
Key key = new Key( id, persister );
object proxy = null;
if( GetEntity( key ) != null )
{
// return existing object or initialized proxy (unless deleted)
return ProxyFor(
persister,
key,
DoLoad( clazz, id, null, LockMode.None, checkDeleted )
);
}
else if( ( proxy = proxiesByKey[ key ] ) != null )
{
// return existing uninitizlied proxy
// *** The code gets here!!!
return NarrowProxy( proxy, persister, key, null );
The problem is that the code reaches the section returning a narrow (uninitialized) proxy. Back in DoCopy(), after returning from Get(), the following code is executed:
Code:
if( result == null )
{
copiedAlready[ obj ] = obj;
SaveWithGeneratedIdentifier( obj, Cascades.CascadingAction.ActionCopy, copiedAlready );
result = obj; // TODO: Could it have a proxy?
target = obj;
}
else
{
// *** The code reaches here,
// *** since Get() succeeded ...
target = Unproxy( result );
Finally, in Unproxy(), we have
Code:
private object Unproxy( object maybeProxy )
{
if( maybeProxy is INHibernateProxy )
{
INHibernateProxy proxy = ( INHibernateProxy ) maybeProxy;
LazyInitializer li = NHibernateProxyHelper.GetLazyInitializer( proxy );
if( li.IsUninitialized )
{
throw new PersistentObjectException( string.Format( "object was an uninitialized proxy for: {0}", li.PersistentClass.Name ) );
In conclusion, it looks like DoCopy() should needs to force initialization on the object returned by Get(), since the subsequent call to Unproxy() requires it to be initialized.
Please fix, this is a showstopper for us :(