private void DoUpdateMutable( object obj, object id, IClassPersister persister )
		{
			if( log.IsDebugEnabled )
			{
				log.Debug( "updating " + MessageHelper.InfoString( persister, id ) );
			}

			Key key = new Key( id, persister );
			CheckUniqueness( key, obj );

			if( persister.ImplementsLifecycle )
			{
				log.Debug( "calling onUpdate()" );
				if( ( ( ILifecycle ) obj ).OnUpdate( this ) == LifecycleVeto.Veto )
				{
					log.Debug( "update vetoed by onUpdate()" );
					Reassociate( obj, id, persister );
					return;
				}
			}

			// this is a transient object with existing persistent state not loaded by the session

			new OnUpdateVisitor( this, id ).Process( obj, persister );

			AddEntity( key, obj );
			AddEntry( obj, Status.Loaded, null, id, persister.GetVersion( obj ), LockMode.None, true, persister, false );
		}
		/// <summary>
		/// The entity instance is not in the session cache
		/// </summary>
		/// <param name="dr"></param>
		/// <param name="i"></param>
		/// <param name="persister"></param>
		/// <param name="suffix"></param>
		/// <param name="key"></param>
		/// <param name="lockMode"></param>
		/// <param name="optionalObjectKey"></param>
		/// <param name="optionalObject"></param>
		/// <param name="hydratedObjects"></param>
		/// <param name="session"></param>
		/// <returns></returns>
		private object InstanceNotYetLoaded( IDataReader dr, int i, ILoadable persister, string suffix, Key key, LockMode lockMode, Key optionalObjectKey, object optionalObject, IList hydratedObjects, ISessionImplementor session )
		{
			object obj;

			System.Type instanceClass = GetInstanceClass( dr, i, persister, key.Identifier, session );

			if( optionalObjectKey != null && key.Equals( optionalObjectKey ) )
			{
				// its the given optional object
				obj = optionalObject;
			}
			else
			{
				obj = session.Instantiate( instanceClass, key.Identifier );
			}

			// need to hydrate it

			// grab its state from the DataReader and keep it in the Session
			// (but don't yet initialize the object itself)
			// note that we acquired LockMode.READ even if it was not requested
			LockMode acquiredLockMode = lockMode == LockMode.None ? LockMode.Read : lockMode;
			LoadFromResultSet( dr, i, obj, key, suffix, acquiredLockMode, persister, session );

			// materialize associations (and initialize the object) later
			hydratedObjects.Add( obj );

			return obj;
		}
		/// <summary>
		/// Invokes the method if this is something that the LazyInitializer can handle
		/// without the underlying proxied object being instantiated.
		/// </summary>
		/// <param name="method">The name of the method/property to Invoke.</param>
		/// <param name="args">The arguments to pass the method/property.</param>
		/// <returns>
		/// The result of the Invoke if the underlying proxied object is not needed.  If the 
		/// underlying proxied object is needed then it returns the result <see cref="InvokeImplementation"/>
		/// which indicates that the Proxy will need to forward to the real implementation.
		/// </returns>
		public virtual object Invoke( MethodBase method, object[ ] args, object proxy )
		{
			string methodName = method.Name;
			int paramCount = method.GetParameters().Length;

			if( paramCount == 0 )
			{
				if( !_overridesEquals && methodName == "GetHashCode" )
				{
					return IdentityHashCodeProvider.GetHashCode( proxy );
				}
				else if( method.Equals( _getIdentifierMethod ) )
				{
					return _id;
				}
				else if( methodName == "Finalize" )
				{
					return null;
				}
			}
			else if( paramCount == 1 )
			{
				if( !_overridesEquals && methodName == "Equals" )
				{
					return args[0] == proxy;
				}
				else if( method.Equals( _setIdentifierMethod ) )
				{
					Initialize();
					_id = args[ 0 ];
					return InvokeImplementation;
				}
			}
			else if( paramCount == 2)
			{
				// if the Proxy Engine delegates the call of GetObjectData to the Initializer
				// then we need to handle it.  Castle.DynamicProxy takes care of serializing
				// proxies for us, but other providers might not.
				if( methodName == "GetObjectData" )
				{
					SerializationInfo info = ( SerializationInfo ) args[ 0 ];
					StreamingContext context = ( StreamingContext ) args[ 1 ]; // not used !?!

					if( _target == null & _session != null )
					{
						Key key = new Key( _id, _session.Factory.GetPersister( _persistentClass ) );
						_target = _session.GetEntity( key );
					}

					// let the specific LazyInitializer write its requirements for deserialization 
					// into the stream.
					AddSerializationInfo( info, context );

					// don't need a return value for proxy.
					return null;
				}
			}

			return InvokeImplementation;
		}
		private IList DoQuery(
			ISessionImplementor session,
			QueryParameters queryParameters,
			object optionalObject,
			object optionalId,
			object[ ] optionalCollectionKeys,
			bool returnProxies
			)
		{
			RowSelection selection = queryParameters.RowSelection;
			int maxRows = HasMaxRows( selection ) ?
				selection.MaxRows :
				int.MaxValue;

			ILoadable[ ] persisters = Persisters;
			int cols = persisters.Length;

			ArrayList hydratedObjects = cols > 0 ? new ArrayList() : null;
			IList results = new ArrayList();

			IDbCommand st = PrepareQueryCommand(
				ApplyLocks( SqlString, queryParameters.LockModes, session.Factory.Dialect ),
				queryParameters, false, session );

			IDataReader rs = GetResultSet( st, selection, session );

			try
			{
				if( optionalCollectionKeys != null )
				{
					HandleEmptyCollections( optionalCollectionKeys, rs, session );
				}

				Key[ ] keys = new Key[cols]; // we can reuse it each time

				if( log.IsDebugEnabled )
				{
					log.Debug( "processing result set" );
				}

				int count;
				for( count = 0; count < maxRows && rs.Read(); count++ )
				{
					object result = GetRowFromResultSet(
						rs,
						session,
						queryParameters,
						hydratedObjects,
						optionalObject,
						optionalId,
						keys,
						returnProxies );
					results.Add( result );
				}

				if( log.IsDebugEnabled )
				{
					log.Debug( string.Format( "done processing result set ({0} rows)", count ) );
				}
			}
			catch( Exception sqle )
			{
				ADOExceptionReporter.LogExceptions( sqle );
				throw;
			}
			finally
			{
				session.Batcher.CloseQueryCommand( st, rs );
			}

			InitializeEntitiesAndCollections( hydratedObjects, rs, session );

			return results; // GetResultList( results );
		}
		/// <summary>
		/// Resolve any ids for currently loaded objects, duplications within the <c>IDataReader</c>,
		/// etc. Instanciate empty objects to be initialized from the <c>IDataReader</c>. Return an
		/// array of objects (a row of results) and an array of booleans (by side-effect) that determine
		/// wheter the corresponding object should be initialized
		/// </summary>
		/// <param name="rs"></param>
		/// <param name="persisters"></param>
		/// <param name="suffixes"></param>
		/// <param name="keys"></param>
		/// <param name="optionalObject"></param>
		/// <param name="optionalObjectKey"></param>
		/// <param name="session"></param>
		/// <param name="hydratedObjects"></param>
		/// <param name="lockModes"></param>
		/// <returns></returns>
		private object[ ] GetRow(
			IDataReader rs,
			ILoadable[ ] persisters,
			string[ ] suffixes,
			Key[ ] keys,
			object optionalObject,
			Key optionalObjectKey,
			LockMode[ ] lockModes,
			IList hydratedObjects,
			ISessionImplementor session )
		{
			int cols = persisters.Length;

			if( log.IsDebugEnabled )
			{
				log.Debug( "result row: " + StringHelper.ToString( keys ) );
			}

			object[ ] rowResults = new object[cols];

			for( int i = 0; i < cols; i++ )
			{
				object obj = null;
				Key key = keys[ i ];

				if( keys[ i ] == null )
				{
					// do nothing
				}
				else
				{
					//If the object is already loaded, return the loaded one
					obj = session.GetEntity( key );
					if( obj != null )
					{
						//its already loaded so dont need to hydrate it
						InstanceAlreadyLoaded( rs, i, persisters[ i ], suffixes[ i ], key, obj, lockModes[ i ], session );
					}
					else
					{
						obj = InstanceNotYetLoaded( rs, i, persisters[ i ], suffixes[ i ], key, lockModes[ i ], optionalObjectKey, optionalObject, hydratedObjects, session );
					}
				}

				rowResults[ i ] = obj;
			}
			return rowResults;
		}
		private object DoSave(
			object obj,
			object id,
			IClassPersister persister,
			bool useIdentityColumn,
			Cascades.CascadingAction cascadeAction,
			object anything )
		{
			if( log.IsDebugEnabled )
			{
				log.Debug( "saving " + MessageHelper.InfoString( persister, id ) );
			}

			Key key;
			if( useIdentityColumn )
			{
				// if the id is generated by the database, we assign the key later
				key = null;
			}
			else
			{
				key = new Key( id, persister );

				object old = GetEntity( key );
				if( old != null )
				{
					EntityEntry e = GetEntry( old );
					if( e.Status == Status.Deleted )
					{
						ForceFlush( e );
					}
					else
					{
						throw new NonUniqueObjectException( id, persister.MappedClass );
					}
				}

				persister.SetIdentifier( obj, id );
			}

			// Sub-insertions should occur before containing insertsion so
			// Try to do the callback not
			if( persister.ImplementsLifecycle )
			{
				log.Debug( "calling OnSave()" );
				if( ( ( ILifecycle ) obj ).OnSave( this ) == LifecycleVeto.Veto )
				{
					log.Debug( "insertion vetoed by OnSave()" );
					return id;
				}
			}

			return DoSave( obj, key, persister, false, useIdentityColumn, cascadeAction, anything );
		}
		public void CreateWithWrongTypeOfId()
		{
			IClassPersister persister = new TestingClassPersister();
			Key key = new Key(1L, persister);
		}
		/// <summary>
		/// Create a "temporary" entry for a newly instantiated entity. The entity is 
		/// uninitialized, but we need the mapping from id to instance in order to guarantee 
		/// uniqueness.
		/// </summary>
		/// <param name="key"></param>
		/// <param name="obj"></param>
		/// <param name="lockMode"></param>
		public void AddUninitializedEntity( Key key, object obj, LockMode lockMode )
		{
			AddEntity( key, obj );
			AddEntry( obj, Status.Loading, null, key.Identifier, null, lockMode, true, null, false ); //temporary
		}
		/// <summary>
		/// Load the data for the object with the specified id into a newly created
		/// object. A new key will be assigned to the object. If the class supports
		/// lazy initialization, return a proxy instead, leaving the real work for
		/// later. This should return an existing proxy where appropriate.
		/// </summary>
		/// <param name="clazz">The <see cref="System.Type"/> of the object to load.</param>
		/// <param name="id">The identifier of the object in the database.</param>
		/// <param name="checkDeleted">
		/// A boolean indicating if NHiberate should check if the object has or has not been deleted.
		/// </param>
		/// <param name="allowProxyCreation">A boolean indicating if it is allowed to return a Proxy instead of an instance of the <see cref="System.Type"/>.</param>
		/// <returns>
		/// An loaded instance of the object or a proxy of the object is proxies are allowed.
		/// </returns>
		/// <remarks>
		/// If the parameter <c>checkDeleted</c> is <c>false</c> it is possible to return an object that has 
		/// been deleted by the user in this <see cref="ISession"/>.  If the parameter <c>checkDeleted</c> is
		/// <c>true</c> and the object has been deleted then an <see cref="ObjectDeletedException"/> will be
		/// thrown.
		/// </remarks>
		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
					return NarrowProxy( proxy, persister, key, null );
				}
				else if( allowProxyCreation )
				{
					// return new uninitailzed proxy
					proxy = persister.CreateProxy( id, this );
					if( persister.IsBatchLoadable )
					{
						batchLoadableEntityKeys[ key ] = Marker;
					}
					proxiesByKey[ key ] = proxy;
					return proxy;
				}
				else
				{
					// return a newly loaded object
					return DoLoad( clazz, id, null, LockMode.None, checkDeleted );
				}
			}
		}
		/// <summary>
		/// If the existing proxy is insufficiently "narrow" (derived), instantiate a 
		/// new proxy and overwrite the registration of the old one. This breaks == and 
		/// occurs only for "class" proxies rather than "interface" proxies.
		/// </summary>
		/// <param name="proxy"></param>
		/// <param name="persister"></param>
		/// <param name="key"></param>
		/// <param name="obj"></param>
		/// <returns></returns>
		public object NarrowProxy( object proxy, IClassPersister persister, Key key, object obj )
		{
			if( !persister.ConcreteProxyClass.IsAssignableFrom( proxy.GetType() ) )
			{
				if( log.IsWarnEnabled )
				{
					log.Warn(
						"Narrowing proxy to " + persister.ConcreteProxyClass + " - this operation breaks =="
						);
				}

				if( obj != null )
				{
					proxiesByKey.Remove( key );
					return obj;
				}
				else
				{
					proxy = persister.CreateProxy( key.Identifier, this );
					proxiesByKey[ key ] = proxy;
					return proxy;
				}
			}
			else
			{
				return proxy;
			}
		}
		/// <summary>
		/// Grab the existing proxy for an instance, if one exists.
		/// (otherwise return the instance)
		/// </summary>
		/// <param name="persister"></param>
		/// <param name="key"></param>
		/// <param name="impl"></param>
		/// <returns></returns>
		public object ProxyFor( IClassPersister persister, Key key, object impl )
		{
			if( !persister.HasProxy || key == null )
			{
				return impl;
			}

			object proxy = proxiesByKey[ key ];
			if( proxy != null )
			{
				return NarrowProxy( proxy, persister, key, impl );
			}
			else
			{
				return impl;
			}
		}
		private EntityEntry Reassociate( object obj, object id, IClassPersister persister )
		{
			if( log.IsDebugEnabled )
			{
				log.Debug( "reassociating transient instance: " + MessageHelper.InfoString( persister, id ) );
			}
			Key key = new Key( id, persister );
			CheckUniqueness( key, obj );
			AddEntity( key, obj );
			Object[ ] values = persister.GetPropertyValues( obj );
			TypeFactory.DeepCopy( values, persister.PropertyTypes, persister.PropertyUpdateability, values );
			object version = Versioning.GetVersion( values, persister );
			EntityEntry newEntry = AddEntry( obj, Status.Loaded, values, id, version, LockMode.None, true, persister, false );
			new OnLockVisitor( this, id ).Process( obj, persister );
			return newEntry;
		}
		private void CheckUniqueness( Key key, object obj )
		{
			object entity = GetEntity( key );
			if( entity == obj )
			{
				throw new AssertionFailure( "object already associated in DoSave()" );
			}
			if( entity != null )
			{
				throw new NonUniqueObjectException( key.Identifier, key.MappedClass );
			}
		}
		/// <summary>
		/// Used only by Replicate
		/// </summary>
		/// <param name="obj"></param>
		/// <param name="id"></param>
		/// <param name="version"></param>
		/// <param name="replicationMode"></param>
		/// <param name="persister"></param>
		private void DoReplicate( object obj, object id, object version, ReplicationMode replicationMode, IClassPersister persister )
		{
			if( log.IsDebugEnabled )
			{
				log.Debug( "replicating changes to " + MessageHelper.InfoString( persister, id ) );
			}

			new OnReplicateVisitor( this, id ).Process( obj, persister );
			Key key = new Key( id, persister );
			AddEntity( key, obj );
			AddEntry( obj, Status.Loaded, null, id, version, LockMode.None, true, persister, true );

			cascading++;
			try
			{
				// do cascade
				Cascades.Cascade( this, persister, obj, Cascades.CascadingAction.ActionReplicate, CascadePoint.CascadeOnUpdate, replicationMode );
			}
			finally
			{
				cascading--;
			}
		}
		/// <summary>
		/// 
		/// </summary>
		/// <param name="key"></param>
		/// <returns></returns>
		public object GetEntity( Key key )
		{
			return entitiesByKey[ key ];
		}
		/// <summary>
		/// Actually do all the hard work of loading up an object
		/// </summary>
		/// <param name="theClass"></param>
		/// <param name="id"></param>
		/// <param name="optionalObject"></param>
		/// <param name="lockMode"></param>
		/// <param name="checkDeleted"></param>
		/// <returns></returns>
		/// <remarks>
		/// 1. see if it is already loaded
		/// 2. see if it is cached
		/// 3. actually go to the database
		/// </remarks>
		private object DoLoad( System.Type theClass, object id, object optionalObject, LockMode lockMode, bool checkDeleted )
		{
			//DONT need to flush before a load by id, because ids are constant

			if( log.IsDebugEnabled )
			{
				log.Debug( "attempting to resolve " + MessageHelper.InfoString( theClass, id ) );
			}

			IClassPersister persister = GetClassPersister( theClass );
			Key key = new Key( id, persister );

			if( optionalObject != null )
			{
				persister.SetIdentifier( optionalObject, id );
			}

			// LOOK FOR LOADED OBJECT 
			// Look for Status.Loaded object
			object old = GetEntity( key );
			if( old != null )
			{
				//if this object was already loaded
				EntityEntry oldEntry = GetEntry( old );
				Status status = oldEntry.Status;
				if( checkDeleted && ( status == Status.Deleted || status == Status.Gone ) )
				{
					throw new ObjectDeletedException( "The object with that id was deleted", id, theClass );
				}
				UpgradeLock( old, oldEntry, lockMode );
				if( log.IsDebugEnabled )
				{
					log.Debug( "resolved object in session cache " + MessageHelper.InfoString( persister, id ) );
				}
				return old;

			}
			else
			{
				// check to see if we know already that it does not exist:
				if( nonExists.Contains( key ) )
				{
					log.Debug( "entity does not exist" );
					return null;
				}

				// LOOK IN CACHE
				CacheEntry entry = persister.HasCache && lockMode.LessThan( LockMode.Read ) ?
					( CacheEntry ) persister.Cache.Get( id, Timestamp ) :
					null;

				if( entry != null )
				{
					return AssembleCacheEntry( entry, id, persister, optionalObject );
				}
				else
				{
					//GO TO DATABASE
					if( log.IsDebugEnabled )
					{
						log.Debug( "object not resolved in any cache " + MessageHelper.InfoString( persister, id ) );
					}
					object result = persister.Load( id, optionalObject, lockMode, this );
					if( result == null )
					{
						// remember it doesn't exist, in case of next time
						AddNonExist( key );
					}
					return result;
				}
			}
		}
		private object RemoveEntity( Key key )
		{
			object retVal = entitiesByKey[ key ];
			entitiesByKey.Remove( key );
			return retVal;
		}
		public void Refresh( object obj, LockMode lockMode )
		{
			CheckIsOpen();

			if( obj == null )
			{
				throw new ArgumentNullException( "obj", "attempted to refresh null" );
			}

			if( ReassociateIfUninitializedProxy( obj ) )
			{
				return;
			}

			object theObj = UnproxyAndReassociate( obj );
			EntityEntry e = RemoveEntry( theObj );
			IClassPersister persister;
			object id;

			if( e == null )
			{
				persister = GetPersister( theObj );
				id = persister.GetIdentifier( theObj );
				if( log.IsDebugEnabled )
				{
					log.Debug( "refreshing transient " + MessageHelper.InfoString( persister, id ) );
				}

				if( GetEntry( new Key( id, persister ) ) != null )
				{
					throw new PersistentObjectException(
						"attempted to refresh transient instance when persistent instance was already associated with the Session: " +
						MessageHelper.InfoString( persister, id ) );
				}
			}
			else
			{
				if( log.IsDebugEnabled )
				{
					log.Debug( "refreshing " + MessageHelper.InfoString( e.Persister, e.Id ) );
				}

				if( !e.ExistsInDatabase )
				{
					throw new HibernateException( "this instance does not yet exist as a row in the database" );
				}

				persister = e.Persister;
				id = e.Id;
				Key key = new Key( id, persister );
				RemoveEntity( key );
				if( persister.HasCollections )
				{
					new EvictVisitor( this ).Process( obj, persister );
				}
			}

			if( persister.HasCache )
			{
				persister.Cache.Remove( id );
			}
			
			EvictCachedCollections( persister, id );
			object result = persister.Load( id, theObj, lockMode, this );
			UnresolvableObjectException.ThrowIfNull( result, id, persister.MappedClass );
		}
		private object DoSave(
			object theObj,
			Key key,
			IClassPersister persister,
			bool replicate,
			bool useIdentityColumn,
			Cascades.CascadingAction cascadeAction,
			object anything )
		{
			if( persister.ImplementsValidatable )
			{
				( ( IValidatable ) theObj ).Validate();
			}

			object id;
			if( useIdentityColumn )
			{
				id = null;
				ExecuteInserts();
			}
			else
			{
				id = key.Identifier;
			}

			// Put a placeholder in entries, so we don't recurse back to try and Save() the
			// same object again. QUESTION: Should this be done before OnSave() is called?
			// likewise, should it be done before OnUpdate()?
			AddEntry( theObj, Status.Saving, null, id, null, LockMode.Write, useIdentityColumn, persister, false ); // okay if id is null here

			// cascade-save to many-to-one BEFORE the parent is saved
			cascading++;
			try
			{
				Cascades.Cascade( this, persister, theObj, cascadeAction, CascadePoint.CascadeBeforeInsertAfterDelete, anything );
			}
			finally
			{
				cascading--;
			}

			object[ ] values = persister.GetPropertyValues( theObj );
			IType[ ] types = persister.PropertyTypes;

			bool substitute = false;
			if( !replicate )
			{
				substitute = interceptor.OnSave( theObj, id, values, persister.PropertyNames, types );

				// Keep the existing version number in the case of replicate!
				if( persister.IsVersioned )
				{
					// IsUnsavedVersion bit below is NHibernate-specific
					substitute = Versioning.SeedVersion(
						values, persister.VersionProperty, persister.VersionType, persister.IsUnsavedVersion( values )
						) || substitute;
				}
			}

			if( persister.HasCollections )
			{
				// TODO: make OnReplicateVisitor extend WrapVisitor
				if( replicate )
				{
					OnReplicateVisitor visitor = new OnReplicateVisitor( this, id );
					visitor.ProcessValues( values, types );
				}
				WrapVisitor visitor2 = new WrapVisitor( this );
				// substitutes into values by side-effect
				visitor2.ProcessValues( values, types );
				substitute = substitute || visitor2.IsSubstitutionRequired;
			}

			if( substitute )
			{
				persister.SetPropertyValues( theObj, values );
			}

			TypeFactory.DeepCopy( values, types, persister.PropertyUpdateability, values );
			NullifyTransientReferences( values, types, useIdentityColumn, theObj );
			CheckNullability( values, persister, false );

			if( useIdentityColumn )
			{
				ScheduledIdentityInsertion insert = new ScheduledIdentityInsertion( values, theObj, persister, this );
				Execute( insert );
				id = insert.GeneratedId;
				persister.SetIdentifier( theObj, id );
				key = new Key( id, persister );
				CheckUniqueness( key, theObj );
			}

			object version = Versioning.GetVersion( values, persister );
			AddEntity( key, theObj );
			AddEntry( theObj, Status.Loaded, values, id, version, LockMode.Write, useIdentityColumn, persister, replicate );
			nonExists.Remove( key );

			if( !useIdentityColumn )
			{
				insertions.Add( new ScheduledInsertion( id, values, theObj, version, persister, this ) );
			}

			// cascade-save to collections AFTER the collection owner was saved
			cascading++;
			try
			{
				Cascades.Cascade( this, persister, theObj, cascadeAction, CascadePoint.CascadeAfterInsertBeforeDelete, anything );
			}
			finally
			{
				cascading--;
			}

			return id;
		}
		public void PostDelete( object obj )
		{
			EntityEntry entry = RemoveEntry( obj );
			if( entry == null )
			{
				throw new AssertionFailure( "possible nonthreadsafe access to session" );
			}
			entry.Status = Status.Gone;
			entry.ExistsInDatabase = false;
			Key key = new Key( entry.Id, entry.Persister );
			RemoveEntity( key );
			proxiesByKey.Remove( key );
		}
		private object GetRowFromResultSet(
			IDataReader resultSet,
			ISessionImplementor session,
			QueryParameters queryParameters,
			IList hydratedObjects,
			object optionalObject,
			object optionalId,
			Key[ ] keys,
			bool returnProxies )
		{
			ILoadable[ ] persisters = Persisters;
			int cols = persisters.Length;
			ICollectionPersister collectionPersister = CollectionPersister;
			int collectionOwner = CollectionOwner;
			string[ ] suffixes = Suffixes;
			LockMode[ ] lockModeArray = GetLockModes( queryParameters.LockModes );
			int[ ] owners = Owners;

			// this is a CollectionInitializer and we are loading up a single collection
			bool hasCollections = collectionPersister != null;
			// this is a query and we are loading multiple instances of the same collection role
			bool hasCollectionOwners = hasCollections && collectionOwner >= 0;

			Key optionalObjectKey;

			if( optionalObject != null )
			{
				optionalObjectKey = new Key( optionalId, session.GetPersister( optionalObject ) );
			}
			else
			{
				optionalObjectKey = null;
			}

			for( int i = 0; i < cols; i++ )
			{
				keys[ i ] = GetKeyFromResultSet(
					i,
					Persisters[ i ],
					( i == cols - 1 ) ? optionalId : null,
					resultSet,
					session );
				//TODO: the i==cols-1 bit depends upon subclass implementation (very bad)
			}

			if( owners != null )
			{
				RegisterNonExists( keys, owners, persisters, session );
			}

			// this call is side-effecty
			object[ ] row = GetRow(
				resultSet,
				persisters,
				suffixes,
				keys,
				optionalObject,
				optionalObjectKey,
				lockModeArray,
				hydratedObjects,
				session );

			if( returnProxies )
			{
				for( int i = 0; i < cols; i++ )
				{
					// now get an existing proxy for each row element (if there is one)
					row[ i ] = session.ProxyFor( persisters[ i ], keys[ i ], row[ i ] );
				}
			}

			if( hasCollections )
			{
				//if null, owner will be retrieved from session
				object owner = hasCollectionOwners ? row[ collectionOwner ] : null;
				object key = owner != null
					? keys[ collectionOwner ].Identifier : null;
				ReadCollectionElement( owner, key, resultSet, session );
			}

			return GetResultColumnOrRow( row, resultSet, session );
		}
		/// <summary>
		/// record the fact that this collection was dereferenced
		/// </summary>
		/// <param name="coll"></param>
		private void UpdateUnreachableCollection( PersistentCollection coll )
		{
			CollectionEntry entry = GetCollectionEntry( coll );

			if( log.IsDebugEnabled && entry.loadedPersister != null )
			{
				log.Debug( "collection dereferenced: " + MessageHelper.InfoString( entry.loadedPersister, entry.loadedKey ) );
			}

			// do a check
			if( entry.loadedPersister != null && entry.loadedPersister.HasOrphanDelete )
			{
				Key key = new Key(
					entry.loadedKey,
					GetClassPersister( entry.loadedPersister.OwnerClass ) );

				object owner = GetEntity( key );

				if( owner == null )
				{
					throw new AssertionFailure( "owner not associated with session" );
				}

				EntityEntry e = GetEntry( owner );

				// only collections belonging to deleted entities are allowed to be dereferenced in 
				// the case of orphan delete
				if( e != null && e.Status != Status.Deleted && e.Status != Status.Gone )
				{
					throw new HibernateException( "You may not dereference an collection with cascade=\"all-delete-orphan\"" );
				}
			}

			// do the work
			entry.currentPersister = null;
			entry.currentKey = null;

			PrepareCollectionForUpdate( coll, entry );
		}
		private void RegisterNonExists(
			Key[ ] keys,
			int[ ] owners,
			ILoadable[ ] persisters,
			ISessionImplementor session
			)
		{
			for( int i = 0; i < keys.Length; i++ )
			{
				int owner = owners[ i ];
				if( owner > -1 )
				{
					Key ownerKey = keys[ owner ];
					if( keys[ i ] == null && ownerKey != null )
					{
						session.AddNonExist( new Key( ownerKey.Identifier, persisters[ i ] ) );
					}
				}
			}
		}
		/// <summary>
		/// remove any hard references to the entity that are held by the infrastructure
		/// (references held by application or other persistant instances are okay)
		/// </summary>
		/// <param name="obj"></param>
		public void Evict( object obj )
		{
			CheckIsOpen();

			if( obj is INHibernateProxy )
			{
				LazyInitializer li = NHibernateProxyHelper.GetLazyInitializer( ( INHibernateProxy ) obj );
				object id = li.Identifier;
				IClassPersister persister = GetClassPersister( li.PersistentClass );
				Key key = new Key( id, persister );
				proxiesByKey.Remove( key );
				if( !li.IsUninitialized )
				{
					object entity = RemoveEntity( key );
					if( entity != null )
					{
						EntityEntry e = RemoveEntry( entity );
						DoEvict( e.Persister, entity );
					}
				}
			}
			else
			{
				EntityEntry e = RemoveEntry( obj );
				if( e != null )
				{
					RemoveEntity( new Key( e.Id, e.Persister ) );
					DoEvict( e.Persister, obj );
				}
			}
		}
		/// <summary>
		/// The entity instance is already in the session cache
		/// </summary>
		private void InstanceAlreadyLoaded(
			IDataReader rs,
			int i,
			ILoadable persister,
			string suffix,
			Key key,
			object obj,
			LockMode lockMode,
			ISessionImplementor session )
		{
			if( !persister.MappedClass.IsAssignableFrom( obj.GetType() ) )
			{
				throw new WrongClassException( "loading object was of wrong class", key.Identifier, persister.MappedClass );
			}

			if( LockMode.None != lockMode && UpgradeLocks() )
			{
				// we don't need to worry about existing version being uninitialized
				// because this block isn't called by a re-entrant load (re-entrant
				// load _always_ have lock mode NONE
				if( persister.IsVersioned && session.GetLockMode( obj ).LessThan( lockMode ) )
				{
					// we only check the version when _upgrading_ lock modes
					CheckVersion( i, persister, key.Identifier, session.GetVersion( obj ), rs, session );
					// we need to upgrade the lock mode to the mode requested
					session.SetLockMode( obj, lockMode );
				}
			}
		}
		public void AddNonExist( Key key )
		{
			nonExists.Add( key );
		}
		/// <summary>
		/// Hydrate the state of an object from the SQL <c>IDataReader</c>, into
		/// an array of "hydrated" values (do not resolve associations yet),
		/// and pass the hydrated state to the session.
		/// </summary>
		/// <param name="rs"></param>
		/// <param name="i"></param>
		/// <param name="obj"></param>
		/// <param name="key"></param>
		/// <param name="suffix"></param>
		/// <param name="lockMode"></param>
		/// <param name="rootPersister"></param>
		/// <param name="session"></param>
		private void LoadFromResultSet( IDataReader rs, int i, object obj, Key key, string suffix, LockMode lockMode, ILoadable rootPersister, ISessionImplementor session )
		{
			if( log.IsDebugEnabled )
			{
				log.Debug( "Initializing object from DataReader: " + key );
			}

			// add temp entry so that the next step is circular-reference
			// safe - only needed because some types don't take proper
			// advantage of two-phase-load (esp. components)
			session.AddUninitializedEntity( key, obj, lockMode );

			// Get the persister for the _subclass_
			ILoadable persister = ( ILoadable ) session.GetPersister( obj );

			// This is not very nice (and quite slow):
			string[ ][ ] cols = persister == rootPersister ?
				suffixedPropertyColumns[ i ] :
				GetSuffixedPropertyAliases( persister, suffix );

			object id = key.Identifier;
			object[ ] values = Hydrate( rs, id, obj, persister, session, cols );
			session.PostHydrate( persister, id, values, obj, lockMode );
		}
		private void AddEntity( Key key, object obj )
		{
			entitiesByKey[ key ] = obj;
			if( key.IsBatchLoadable )
			{
				batchLoadableEntityKeys.Remove( key );
			}
		}
		/// <summary>
		/// Return the Underlying Persistent Object in a given <see cref="ISession"/>, or null.
		/// </summary>
		/// <param name="s">The Session to get the object from.</param>
		/// <returns>The Persistent Object this proxy is Proxying, or <c>null</c>.</returns>
		public object GetImplementation( ISessionImplementor s )
		{
			Key key = new Key( Identifier, s.Factory.GetPersister( PersistentClass ) );
			return s.GetEntity( key );
		}
		private void DoDelete( object obj, EntityEntry entry, IClassPersister persister )
		{
			if( log.IsDebugEnabled )
			{
				log.Debug( "deleting " + MessageHelper.InfoString( persister, entry.Id ) );
			}

			IType[ ] propTypes = persister.PropertyTypes;

			object version = entry.Version;

			object[ ] loadedState;
			if( entry.LoadedState == null )
			{
				//ie the object came in from Update()
				loadedState = persister.GetPropertyValues( obj );
			}
			else
			{
				loadedState = entry.LoadedState;
			}
			entry.DeletedState = new object[loadedState.Length];
			TypeFactory.DeepCopy( loadedState, propTypes, persister.PropertyUpdateability, entry.DeletedState );

			interceptor.OnDelete( obj, entry.Id, entry.DeletedState, persister.PropertyNames, propTypes );

			entry.Status = Status.Deleted; // before cascade and Lifecycle callback, so we are circular-reference safe
			Key key = new Key( entry.Id, persister );

			IList deletionsByOnDelete = null;
			ISet nullifiablesAfterOnDelete = null;

			// do Lifecycle callback before cascades, since this can veto
			if( persister.ImplementsLifecycle )
			{
				ISet oldNullifiables = ( ISet ) nullifiables.Clone();
				ArrayList oldDeletions = ( ArrayList ) deletions.Clone();

				nullifiables.Add( key ); //the deletion of the parent is actually executed BEFORE any deletion from onDelete()

				try
				{
					log.Debug( "calling onDelete()" );
					if( ( ( ILifecycle ) obj ).OnDelete( this ) == LifecycleVeto.Veto )
					{
						//rollback deletion
						entry.Status = Status.Loaded;
						entry.DeletedState = null;
						nullifiables = oldNullifiables;
						log.Debug( "deletion vetoed by OnDelete()" );
						return; // don't let it cascade
					}
				}
				catch( Exception )
				{
					//rollback deletion
					entry.Status = Status.Loaded;
					entry.DeletedState = null;
					nullifiables = oldNullifiables;
					throw;
				}

				//note, the following assumes that onDelete() didn't cause the session to be flushed! 
				// TODO: add a better check that it doesn't
				if( oldDeletions.Count > deletions.Count )
				{
					throw new HibernateException( "session was flushed during onDelete()" );
				}
				deletionsByOnDelete = Sublist( deletions, oldDeletions.Count, deletions.Count );
				deletions = oldDeletions;
				nullifiablesAfterOnDelete = nullifiables;
				nullifiables = oldNullifiables;
			}

			cascading++;
			try
			{
				// cascade-delete to collections "BEFORE" the collection owner is deleted
				Cascades.Cascade( this, persister, obj, Cascades.CascadingAction.ActionDelete, CascadePoint.CascadeAfterInsertBeforeDelete, null );
			}
			finally
			{
				cascading--;
			}

			NullifyTransientReferences( entry.DeletedState, propTypes, false, obj );
			// NH: commented out, seems illogical to check for nulls here
			//CheckNullability( entry.DeletedState, persister, true );
			nullifiables.Add( key );

			ScheduledDeletion delete = new ScheduledDeletion( entry.Id, version, obj, persister, this );
			deletions.Add( delete ); // Ensures that containing deletions happen before sub-deletions

			if( persister.ImplementsLifecycle )
			{
				// after nullify, because we don't want to nullify references to subdeletions
				nullifiables.AddAll( nullifiablesAfterOnDelete );
				// after deletions.add(), to respect foreign key constraints
				deletions.AddRange( deletionsByOnDelete );
			}

			cascading++;
			try
			{
				// cascade-delete to many-to-one AFTER the parent was deleted
				Cascades.Cascade( this, persister, obj, Cascades.CascadingAction.ActionDelete, CascadePoint.CascadeBeforeInsertAfterDelete );
			}
			finally
			{
				cascading--;
			}
		}