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;
		}
		private void FlushEntity( object obj, EntityEntry entry )
		{
			IClassPersister persister = entry.Persister;
			Status status = entry.Status;
			CheckId( obj, persister, entry.Id );

			object[ ] values;
			if( status == Status.Deleted )
			{
				//grab its state saved at deletion
				values = entry.DeletedState;
			}
			else
			{
				//grab its current state
				values = persister.GetPropertyValues( obj );
			}
			IType[ ] types = persister.PropertyTypes;

			bool substitute = false;

			if( persister.HasCollections )
			{
				// wrap up any new collections directly referenced by the object
				// or its components

				// NOTE: we need to do the wrap here even if its not "dirty",
				// because nested collections need wrapping but changes to
				// _them_ don't dirty the container. Also, for versioned
				// data, we need to wrap before calling searchForDirtyCollections

				WrapVisitor visitor = new WrapVisitor( this );
				// substitutes into values by side-effect
				visitor.ProcessValues( values, types );
				substitute = visitor.IsSubstitutionRequired;
			}

			bool cannotDirtyCheck;
			bool interceptorHandledDirtyCheck;
			bool dirtyCheckDoneBySelect = false;
			object[ ] currentPersistentState = null;

			int[ ] dirtyProperties = interceptor.FindDirty( obj, entry.Id, values, entry.LoadedState, persister.PropertyNames, types );

			if( dirtyProperties == null )
			{
				// interceptor returned null, so do the dirtycheck ourself, if possible
				interceptorHandledDirtyCheck = false;
				cannotDirtyCheck = entry.LoadedState == null; // object loaded by update()
				if( !cannotDirtyCheck )
				{
					dirtyProperties = persister.FindDirty( values, entry.LoadedState, obj, this );
				}
				else
				{
					currentPersistentState = persister.GetCurrentPersistentState( entry.Id, entry.Version, this );
					if( currentPersistentState != null )
					{
						dirtyProperties = persister.FindModified( currentPersistentState, values, obj, this );
						cannotDirtyCheck = false;
						dirtyCheckDoneBySelect = true;
					}
				}
			}
			else
			{
				// the interceptor handled the dirty checking
				cannotDirtyCheck = false;
				interceptorHandledDirtyCheck = true;
			}

			// compare to cached state (ignoring nested collections)
			if( IsUpdateNecessary( persister, cannotDirtyCheck, status, dirtyProperties, values, types ) )
			{
				// its dirty!

				if( log.IsDebugEnabled )
				{
					if( status == Status.Deleted )
					{
						log.Debug( "Updating deleted entity: " + MessageHelper.InfoString( persister, entry.Id ) );
					}
					else
					{
						log.Debug( "Updating entity: " + MessageHelper.InfoString( persister, entry.Id ) );
					}
				}

				if( !entry.IsBeingReplicated )
				{
					// give the Interceptor a chance to modify property values
					bool intercepted = interceptor.OnFlushDirty(
						obj, entry.Id, values, entry.LoadedState, persister.PropertyNames, types );

					//now we might need to recalculate the dirtyProperties array
					if( intercepted && !cannotDirtyCheck && !interceptorHandledDirtyCheck )
					{
						if( dirtyCheckDoneBySelect )
						{
							dirtyProperties = persister.FindModified( currentPersistentState, values, obj, this );
						}
						else
						{
							dirtyProperties = persister.FindDirty( values, entry.LoadedState, obj, this );
						}
					}
					// if the properties were modified by the Interceptor, we need to set them back to the object
					substitute = substitute || intercepted;
				}

				// validate() instances of Validatable
				if( status == Status.Loaded && persister.ImplementsValidatable )
				{
					( ( IValidatable ) obj ).Validate();
				}

				//increment the version number (if necessary)
				object nextVersion = GetNextVersion( persister, values, entry );

				// get the updated snapshot by cloning current state
				object[ ] updatedState = null;
				if( status == Status.Loaded )
				{
					updatedState = new object[values.Length];
					TypeFactory.DeepCopy( values, types, persister.PropertyUpdateability, updatedState );
				}

				// if it was dirtied by a collection only
				if( !cannotDirtyCheck && dirtyProperties == null )
				{
					dirtyProperties = ArrayHelper.EmptyIntArray;
				}

				CheckNullability( values, persister, true );

				//note that we intentionally did _not_ pass in currentPersistentState!
				updates.Add(
					new ScheduledUpdate( entry.Id, values, dirtyProperties, entry.LoadedState, entry.Version, nextVersion, obj, updatedState, persister, this )
					);
			}

			if( status == Status.Deleted )
			{
				//entry.status = Status.Gone;
			}
			else
			{
				// now update the object... has to be outside the main if block above (because of collections)
				if( substitute )
				{
					persister.SetPropertyValues( obj, values );
				}

				// search for collections by reachability, updating their role.
				// we don't want to touch collections reachable from a deleted object.
				if( persister.HasCollections )
				{
					new FlushVisitor( this, obj ).ProcessValues( values, types );
				}
			}
		}