/// <summary>
        /// Prepares the save call by checking the session caches for a pre-existing
        /// entity and performing any lifecycle callbacks.
        /// </summary>
        /// <param name="entity">The entity to be saved. </param>
        /// <param name="id">The id by which to save the entity. </param>
        /// <param name="persister">The entity's persister instance. </param>
        /// <param name="useIdentityColumn">Is an identity column being used? </param>
        /// <param name="anything">Generally cascade-specific information. </param>
        /// <param name="source">The session from which the event originated. </param>
        /// <param name="requiresImmediateIdAccess">
        /// does the event context require
        /// access to the identifier immediately after execution of this method (if
        /// not, post-insert style id generators may be postponed if we are outside
        /// a transaction).
        /// </param>
        /// <returns>
        /// The id used to save the entity; may be null depending on the
        /// type of id generator used and the requiresImmediateIdAccess value
        /// </returns>
        protected virtual object PerformSave(object entity, object id, IEntityPersister persister, bool useIdentityColumn, object anything, IEventSource source, bool requiresImmediateIdAccess)
        {
            if (log.IsDebugEnabled())
            {
                log.Debug("saving {0}", MessageHelper.InfoString(persister, id, source.Factory));
            }

            EntityKey key;

            if (!useIdentityColumn)
            {
                key = source.GenerateEntityKey(id, persister);
                object old = source.PersistenceContext.GetEntity(key);
                if (old != null)
                {
                    if (source.PersistenceContext.GetEntry(old).Status == Status.Deleted)
                    {
                        source.ForceFlush(source.PersistenceContext.GetEntry(old));
                    }
                    else
                    {
                        throw new NonUniqueObjectException(id, persister.EntityName);
                    }
                }
                persister.SetIdentifier(entity, id);
            }
            else
            {
                key = null;
            }

            if (InvokeSaveLifecycle(entity, persister, source))
            {
                return(id);                //EARLY EXIT
            }
            return(PerformSaveOrReplicate(entity, key, persister, useIdentityColumn, anything, source, requiresImmediateIdAccess));
        }
Example #2
0
        private async Task PerformReplicationAsync(object entity, object id, object version, IEntityPersister persister, ReplicationMode replicationMode, IEventSource source, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            if (log.IsDebugEnabled)
            {
                log.Debug("replicating changes to " + MessageHelper.InfoString(persister, id, source.Factory));
            }

            await(new OnReplicateVisitor(source, id, entity, true).ProcessAsync(entity, persister, cancellationToken)).ConfigureAwait(false);

            source.PersistenceContext.AddEntity(
                entity,
                persister.IsMutable ? Status.Loaded : Status.ReadOnly,
                null,
                source.GenerateEntityKey(id, persister),
                version,
                LockMode.None,
                true,
                persister,
                true,
                false);

            await(CascadeAfterReplicateAsync(entity, persister, replicationMode, source, cancellationToken)).ConfigureAwait(false);
        }
        /// <summary>
        /// Associates a given entity (either transient or associated with another session) to the given session.
        /// </summary>
        /// <param name="event">The event triggering the re-association </param>
        /// <param name="entity">The entity to be associated </param>
        /// <param name="id">The id of the entity. </param>
        /// <param name="persister">The entity's persister instance. </param>
        /// <param name="cancellationToken">A cancellation token that can be used to cancel the work</param>
        /// <returns> An EntityEntry representing the entity within this session. </returns>
        protected async Task <EntityEntry> ReassociateAsync(AbstractEvent @event, object entity, object id, IEntityPersister persister, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            if (log.IsDebugEnabled())
            {
                log.Debug("Reassociating transient instance: {0}", MessageHelper.InfoString(persister, id, @event.Session.Factory));
            }

            IEventSource source = @event.Session;
            EntityKey    key    = source.GenerateEntityKey(id, persister);

            source.PersistenceContext.CheckUniqueness(key, entity);

            //get a snapshot
            object[] values = persister.GetPropertyValues(entity);
            TypeHelper.DeepCopy(values, persister.PropertyTypes, persister.PropertyUpdateability, values, source);
            object version = Versioning.GetVersion(values, persister);

            EntityEntry newEntry = source.PersistenceContext.AddEntity(
                entity,
                persister.IsMutable ? Status.Loaded : Status.ReadOnly,
                values,
                key,
                version,
                LockMode.None,
                true,
                persister,
                false,
                true);

            await(new OnLockVisitor(source, id, entity).ProcessAsync(entity, persister, cancellationToken)).ConfigureAwait(false);

            persister.AfterReassociate(entity, source);

            return(newEntry);
        }
        /// <summary>
        /// Performs all the actual work needed to save an entity (well to get the save moved to
        /// the execution queue).
        /// </summary>
        /// <param name="entity">The entity to be saved </param>
        /// <param name="key">The id to be used for saving the entity (or null, in the case of identity columns) </param>
        /// <param name="persister">The entity's persister instance. </param>
        /// <param name="useIdentityColumn">Should an identity column be used for id generation? </param>
        /// <param name="anything">Generally cascade-specific information. </param>
        /// <param name="source">The session which is the source of the current event. </param>
        /// <param name="requiresImmediateIdAccess">
        /// Is access to the identifier required immediately
        /// after the completion of the save?  persist(), for example, does not require this...
        /// </param>
        /// <param name="cancellationToken">A cancellation token that can be used to cancel the work</param>
        /// <returns>
        /// The id used to save the entity; may be null depending on the
        /// type of id generator used and the requiresImmediateIdAccess value
        /// </returns>
        protected virtual async Task <object> PerformSaveOrReplicateAsync(object entity, EntityKey key, IEntityPersister persister, bool useIdentityColumn, object anything, IEventSource source, bool requiresImmediateIdAccess, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            Validate(entity, persister, source);

            object id = key == null ? null : key.Identifier;

            // NH Different behavior (shouldDelayIdentityInserts=false anyway)
            //bool inTxn = source.ConnectionManager.IsInActiveTransaction;
            //bool shouldDelayIdentityInserts = !inTxn && !requiresImmediateIdAccess;
            bool shouldDelayIdentityInserts = false;

            // Put a placeholder in entries, so we don't recurse back and try to save() the
            // same object again. QUESTION: should this be done before onSave() is called?
            // likewise, should it be done before onUpdate()?
            source.PersistenceContext.AddEntry(entity, Status.Saving, null, null, id, null, LockMode.Write, useIdentityColumn, persister, false, false);

            await(CascadeBeforeSaveAsync(source, persister, entity, anything, cancellationToken)).ConfigureAwait(false);

            // NH-962: This was originally done before many-to-one cascades.
            if (useIdentityColumn && !shouldDelayIdentityInserts)
            {
                log.Debug("executing insertions");
                await(source.ActionQueue.ExecuteInsertsAsync(cancellationToken)).ConfigureAwait(false);
            }

            object[] values = persister.GetPropertyValuesToInsert(entity, GetMergeMap(anything), source);
            IType[]  types  = persister.PropertyTypes;

            bool substitute = await(SubstituteValuesIfNecessaryAsync(entity, id, values, persister, source, cancellationToken)).ConfigureAwait(false);

            if (persister.HasCollections)
            {
                substitute = substitute || await(VisitCollectionsBeforeSaveAsync(entity, id, values, types, source, cancellationToken)).ConfigureAwait(false);
            }

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

            TypeHelper.DeepCopy(values, types, persister.PropertyUpdateability, values, source);

            await(new ForeignKeys.Nullifier(entity, false, useIdentityColumn, source).NullifyTransientReferencesAsync(values, types, cancellationToken)).ConfigureAwait(false);
            new Nullability(source).CheckNullability(values, persister, false);

            if (useIdentityColumn)
            {
                EntityIdentityInsertAction insert = new EntityIdentityInsertAction(values, entity, persister, source, shouldDelayIdentityInserts);
                if (!shouldDelayIdentityInserts)
                {
                    log.Debug("executing identity-insert immediately");
                    await(source.ActionQueue.ExecuteAsync(insert, cancellationToken)).ConfigureAwait(false);
                    id = insert.GeneratedId;
                    //now done in EntityIdentityInsertAction
                    //persister.setIdentifier( entity, id, source.getEntityMode() );
                    key = source.GenerateEntityKey(id, persister);
                    source.PersistenceContext.CheckUniqueness(key, entity);
                    //source.getBatcher().executeBatch(); //found another way to ensure that all batched joined inserts have been executed
                }
                else
                {
                    log.Debug("delaying identity-insert due to no transaction in progress");
                    source.ActionQueue.AddAction(insert);
                    key = insert.DelayedEntityKey;
                }
            }

            object version = Versioning.GetVersion(values, persister);

            source.PersistenceContext.AddEntity(
                entity,
                persister.IsMutable ? Status.Loaded : Status.ReadOnly,
                values, key,
                version,
                LockMode.Write,
                useIdentityColumn,
                persister,
                VersionIncrementDisabled,
                false);
            //source.getPersistenceContext().removeNonExist( new EntityKey( id, persister, source.getEntityMode() ) );

            if (!useIdentityColumn)
            {
                source.ActionQueue.AddAction(new EntityInsertAction(id, values, entity, version, persister, source));
            }

            await(CascadeAfterSaveAsync(source, persister, entity, anything, cancellationToken)).ConfigureAwait(false);

            MarkInterceptorDirty(entity, persister, source);

            return(id);
        }
        protected virtual void PerformUpdate(SaveOrUpdateEvent @event, object entity, IEntityPersister persister)
        {
            if (!persister.IsMutable)
            {
                log.Debug("immutable instance passed to PerformUpdate(), locking");
            }

            if (log.IsDebugEnabled)
            {
                log.Debug("updating " + MessageHelper.InfoString(persister, @event.RequestedId, @event.Session.Factory));
            }

            IEventSource source = @event.Session;

            EntityKey key = source.GenerateEntityKey(@event.RequestedId, persister);

            source.PersistenceContext.CheckUniqueness(key, entity);

            if (InvokeUpdateLifecycle(entity, persister, source))
            {
                Reassociate(@event, @event.Entity, @event.RequestedId, persister);
                return;
            }

            // this is a transient object with existing persistent state not loaded by the session
            new OnUpdateVisitor(source, @event.RequestedId, entity).Process(entity, persister);

            //TODO: put this stuff back in to read snapshot from
            //      the second-level cache (needs some extra work)

            /*Object[] cachedState = null;
             *
             * if ( persister.hasCache() ) {
             * CacheEntry entry = (CacheEntry) persister.getCache()
             * .get( event.getRequestedId(), source.getTimestamp() );
             * cachedState = entry==null ?
             * null :
             * entry.getState(); //TODO: half-assemble this stuff
             * }*/

            source.PersistenceContext.AddEntity(
                entity,
                persister.IsMutable ? Status.Loaded : Status.ReadOnly,
                null,
                key,
                persister.GetVersion(entity),
                LockMode.None,
                true,
                persister,
                false,
                true);

            //persister.AfterReassociate(entity, source); TODO H3.2 not ported

            if (log.IsDebugEnabled)
            {
                log.Debug("updating " + MessageHelper.InfoString(persister, @event.RequestedId, source.Factory));
            }

            CascadeOnUpdate(@event, persister, entity);
        }
		/// <summary> 
		/// Performs all the actual work needed to save an entity (well to get the save moved to
		/// the execution queue). 
		/// </summary>
		/// <param name="entity">The entity to be saved </param>
		/// <param name="key">The id to be used for saving the entity (or null, in the case of identity columns) </param>
		/// <param name="persister">The entity's persister instance. </param>
		/// <param name="useIdentityColumn">Should an identity column be used for id generation? </param>
		/// <param name="anything">Generally cascade-specific information. </param>
		/// <param name="source">The session which is the source of the current event. </param>
		/// <param name="requiresImmediateIdAccess">
		/// Is access to the identifier required immediately
		/// after the completion of the save?  persist(), for example, does not require this... 
		/// </param>
		/// <returns> 
		/// The id used to save the entity; may be null depending on the
		/// type of id generator used and the requiresImmediateIdAccess value
		/// </returns>
		protected virtual object PerformSaveOrReplicate(object entity, EntityKey key, IEntityPersister persister, bool useIdentityColumn, object anything, IEventSource source, bool requiresImmediateIdAccess)
		{
			Validate(entity, persister, source);

			object id = key == null ? null : key.Identifier;

			// NH Different behavior (shouldDelayIdentityInserts=false anyway)
			//bool inTxn = source.ConnectionManager.IsInActiveTransaction;
			//bool shouldDelayIdentityInserts = !inTxn && !requiresImmediateIdAccess;
			bool shouldDelayIdentityInserts = false;

			// Put a placeholder in entries, so we don't recurse back and try to save() the
			// same object again. QUESTION: should this be done before onSave() is called?
			// likewise, should it be done before onUpdate()?
			source.PersistenceContext.AddEntry(entity, Status.Saving, null, null, id, null, LockMode.Write, useIdentityColumn, persister, false, false);

			CascadeBeforeSave(source, persister, entity, anything);

			// NH-962: This was originally done before many-to-one cascades.
			if (useIdentityColumn && !shouldDelayIdentityInserts)
			{
				log.Debug("executing insertions");
				source.ActionQueue.ExecuteInserts();
			}

			object[] values = persister.GetPropertyValuesToInsert(entity, GetMergeMap(anything), source);
			IType[] types = persister.PropertyTypes;

			bool substitute = SubstituteValuesIfNecessary(entity, id, values, persister, source);

			if (persister.HasCollections)
			{
				substitute = substitute || VisitCollectionsBeforeSave(entity, id, values, types, source);
			}

			if (substitute)
			{
				persister.SetPropertyValues(entity, values, source.EntityMode);
			}

			TypeHelper.DeepCopy(values, types, persister.PropertyUpdateability, values, source);

			new ForeignKeys.Nullifier(entity, false, useIdentityColumn, source).NullifyTransientReferences(values, types);
			new Nullability(source).CheckNullability(values, persister, false);

			if (useIdentityColumn)
			{
				EntityIdentityInsertAction insert = new EntityIdentityInsertAction(values, entity, persister, source, shouldDelayIdentityInserts);
				if (!shouldDelayIdentityInserts)
				{
					log.Debug("executing identity-insert immediately");
					source.ActionQueue.Execute(insert);
					id = insert.GeneratedId;
					//now done in EntityIdentityInsertAction
					//persister.setIdentifier( entity, id, source.getEntityMode() );
					key = source.GenerateEntityKey(id, persister);
					source.PersistenceContext.CheckUniqueness(key, entity);
					//source.getBatcher().executeBatch(); //found another way to ensure that all batched joined inserts have been executed
				}
				else
				{
					log.Debug("delaying identity-insert due to no transaction in progress");
					source.ActionQueue.AddAction(insert);
					key = insert.DelayedEntityKey;
				}
			}

			object version = Versioning.GetVersion(values, persister);
			source.PersistenceContext.AddEntity(
				entity, 
				persister.IsMutable ? Status.Loaded : Status.ReadOnly,
				values, key, 
				version, 
				LockMode.Write, 
				useIdentityColumn, 
				persister, 
				VersionIncrementDisabled, 
				false);
			//source.getPersistenceContext().removeNonExist( new EntityKey( id, persister, source.getEntityMode() ) );

			if (!useIdentityColumn)
			{
				source.ActionQueue.AddAction(new EntityInsertAction(id, values, entity, version, persister, source));
			}

			CascadeAfterSave(source, persister, entity, anything);

			MarkInterceptorDirty(entity, persister, source);

			return id;
		}
		/// <summary> 
		/// Prepares the save call by checking the session caches for a pre-existing
		/// entity and performing any lifecycle callbacks. 
		/// </summary>
		/// <param name="entity">The entity to be saved. </param>
		/// <param name="id">The id by which to save the entity. </param>
		/// <param name="persister">The entity's persister instance. </param>
		/// <param name="useIdentityColumn">Is an identity column being used? </param>
		/// <param name="anything">Generally cascade-specific information. </param>
		/// <param name="source">The session from which the event originated. </param>
		/// <param name="requiresImmediateIdAccess">
		/// does the event context require
		/// access to the identifier immediately after execution of this method (if
		/// not, post-insert style id generators may be postponed if we are outside
		/// a transaction). 
		/// </param>
		/// <returns> 
		/// The id used to save the entity; may be null depending on the
		/// type of id generator used and the requiresImmediateIdAccess value
		/// </returns>
		protected virtual object PerformSave(object entity, object id, IEntityPersister persister, bool useIdentityColumn, object anything, IEventSource source, bool requiresImmediateIdAccess)
		{
			if (log.IsDebugEnabled)
			{
				log.Debug("saving " + MessageHelper.InfoString(persister, id, source.Factory));
			}

			EntityKey key;
			if (!useIdentityColumn)
			{
				key = source.GenerateEntityKey(id, persister);
				object old = source.PersistenceContext.GetEntity(key);
				if (old != null)
				{
					if (source.PersistenceContext.GetEntry(old).Status == Status.Deleted)
					{
						source.ForceFlush(source.PersistenceContext.GetEntry(old));
					}
					else
					{
						throw new NonUniqueObjectException(id, persister.EntityName);
					}
				}
				persister.SetIdentifier(entity, id, source.EntityMode);
			}
			else
			{
				key = null;
			}

			if (InvokeSaveLifecycle(entity, persister, source))
			{
				return id; //EARLY EXIT
			}
			return PerformSaveOrReplicate(entity, key, persister, useIdentityColumn, anything, source, requiresImmediateIdAccess);
		}
        public virtual async Task OnReplicateAsync(ReplicateEvent @event, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            IEventSource source = @event.Session;

            if (source.PersistenceContext.ReassociateIfUninitializedProxy(@event.Entity))
            {
                log.Debug("uninitialized proxy passed to replicate()");
                return;
            }

            object entity = await(source.PersistenceContext.UnproxyAndReassociateAsync(@event.Entity, cancellationToken)).ConfigureAwait(false);

            if (source.PersistenceContext.IsEntryFor(entity))
            {
                log.Debug("ignoring persistent instance passed to replicate()");
                //hum ... should we cascade anyway? throw an exception? fine like it is?
                return;
            }

            IEntityPersister persister = source.GetEntityPersister(@event.EntityName, entity);

            // get the id from the object

            /*if ( persister.isUnsaved(entity, source) ) {
             * throw new TransientObjectException("transient instance passed to replicate()");
             * }*/
            object id = persister.GetIdentifier(entity);

            if (id == null)
            {
                throw new TransientObjectException("instance with null id passed to replicate()");
            }

            ReplicationMode replicationMode = @event.ReplicationMode;
            object          oldVersion;

            if (replicationMode == ReplicationMode.Exception)
            {
                //always do an INSERT, and let it fail by constraint violation
                oldVersion = null;
            }
            else
            {
                //what is the version on the database?
                oldVersion = await(persister.GetCurrentVersionAsync(id, source, cancellationToken)).ConfigureAwait(false);
            }

            if (oldVersion != null)
            {
                if (log.IsDebugEnabled())
                {
                    log.Debug("found existing row for {0}", MessageHelper.InfoString(persister, id, source.Factory));
                }

                // HHH-2378
                object realOldVersion = persister.IsVersioned ? oldVersion : null;

                bool canReplicate =
                    replicationMode.ShouldOverwriteCurrentVersion(entity, realOldVersion,
                                                                  persister.GetVersion(entity),
                                                                  persister.VersionType);

                if (canReplicate)
                {
                    //will result in a SQL UPDATE:
                    await(PerformReplicationAsync(entity, id, realOldVersion, persister, replicationMode, source, cancellationToken)).ConfigureAwait(false);
                }
                else
                {
                    //else do nothing (don't even reassociate object!)
                    log.Debug("no need to replicate");
                }

                //TODO: would it be better to do a refresh from db?
            }
            else
            {
                // no existing row - do an insert
                if (log.IsDebugEnabled())
                {
                    log.Debug("no existing row, replicating new instance {0}", MessageHelper.InfoString(persister, id, source.Factory));
                }

                bool      regenerate = persister.IsIdentifierAssignedByInsert;            // prefer re-generation of identity!
                EntityKey key        = regenerate ? null : source.GenerateEntityKey(id, persister);

                await(PerformSaveOrReplicateAsync(entity, key, persister, regenerate, replicationMode, source, true, cancellationToken)).ConfigureAwait(false);
            }
        }
		private void PerformReplication(object entity, object id, object version, IEntityPersister persister, ReplicationMode replicationMode, IEventSource source)
		{
			if (log.IsDebugEnabled)
			{
				log.Debug("replicating changes to " + MessageHelper.InfoString(persister, id, source.Factory));
			}

			new OnReplicateVisitor(source, id, entity, true).Process(entity, persister);

			source.PersistenceContext.AddEntity(
				entity, 
				persister.IsMutable ? Status.Loaded : Status.ReadOnly,
				null,
				source.GenerateEntityKey(id, persister),
				version, 
				LockMode.None, 
				true, 
				persister,
				true, 
				false);

			CascadeAfterReplicate(entity, persister, replicationMode, source);
		}
        public virtual void OnDelete(DeleteEvent @event, ISet <object> transientEntities)
        {
            IEventSource        source             = @event.Session;
            IPersistenceContext persistenceContext = source.PersistenceContext;
            object entity = persistenceContext.UnproxyAndReassociate(@event.Entity);

            EntityEntry      entityEntry = persistenceContext.GetEntry(entity);
            IEntityPersister persister;
            object           id;
            object           version;

            if (entityEntry == null)
            {
                log.Debug("entity was not persistent in delete processing");

                persister = source.GetEntityPersister(@event.EntityName, entity);

                if (ForeignKeys.IsTransientSlow(persister.EntityName, entity, source))
                {
                    DeleteTransientEntity(source, entity, @event.CascadeDeleteEnabled, persister, transientEntities);
                    // EARLY EXIT!!!
                    return;
                }
                else
                {
                    PerformDetachedEntityDeletionCheck(@event);
                }

                id = persister.GetIdentifier(entity);

                if (id == null)
                {
                    throw new TransientObjectException("the detached instance passed to delete() had a null identifier");
                }

                EntityKey key = source.GenerateEntityKey(id, persister);

                persistenceContext.CheckUniqueness(key, entity);

                new OnUpdateVisitor(source, id, entity).Process(entity, persister);

                version = persister.GetVersion(entity);

                entityEntry = persistenceContext.AddEntity(
                    entity,
                    persister.IsMutable ? Status.Loaded : Status.ReadOnly,
                    persister.GetPropertyValues(entity),
                    key,
                    version,
                    LockMode.None,
                    true,
                    persister,
                    false);
            }
            else
            {
                log.Debug("deleting a persistent instance");

                if (entityEntry.Status == Status.Deleted || entityEntry.Status == Status.Gone)
                {
                    log.Debug("object was already deleted");
                    return;
                }
                persister = entityEntry.Persister;
                id        = entityEntry.Id;
                version   = entityEntry.Version;
            }

            if (InvokeDeleteLifecycle(source, entity, persister))
            {
                return;
            }

            DeleteEntity(source, entity, entityEntry, @event.CascadeDeleteEnabled, persister, transientEntities);

            if (source.Factory.Settings.IsIdentifierRollbackEnabled)
            {
                persister.ResetIdentifier(entity, id, version);
            }
        }
Example #11
0
        public virtual async Task OnMergeAsync(MergeEvent @event, IDictionary copiedAlready, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            EventCache   copyCache = (EventCache)copiedAlready;
            IEventSource source    = @event.Session;
            object       original  = @event.Original;

            if (original != null)
            {
                object entity;
                if (original.IsProxy())
                {
                    ILazyInitializer li = ((INHibernateProxy)original).HibernateLazyInitializer;
                    if (li.IsUninitialized)
                    {
                        log.Debug("ignoring uninitialized proxy");
                        @event.Result = await(source.LoadAsync(li.EntityName, li.Identifier, cancellationToken)).ConfigureAwait(false);
                        return;                         //EARLY EXIT!
                    }
                    else
                    {
                        entity = await(li.GetImplementationAsync(cancellationToken)).ConfigureAwait(false);
                    }
                }
                else
                {
                    entity = original;
                }

                if (copyCache.Contains(entity) && copyCache.IsOperatedOn(entity))
                {
                    log.Debug("already in merge process");
                    @event.Result = entity;
                }
                else
                {
                    if (copyCache.Contains(entity))
                    {
                        log.Info("already in copyCache; setting in merge process");
                        copyCache.SetOperatedOn(entity, true);
                    }

                    @event.Entity = entity;
                    EntityState entityState = EntityState.Undefined;
                    if (ReferenceEquals(null, @event.EntityName))
                    {
                        @event.EntityName = source.BestGuessEntityName(entity);
                    }

                    // Check the persistence context for an entry relating to this
                    // entity to be merged...
                    EntityEntry entry = source.PersistenceContext.GetEntry(entity);
                    if (entry == null)
                    {
                        IEntityPersister persister = source.GetEntityPersister(@event.EntityName, entity);
                        object           id        = persister.GetIdentifier(entity);
                        if (id != null)
                        {
                            EntityKey key           = source.GenerateEntityKey(id, persister);
                            object    managedEntity = source.PersistenceContext.GetEntity(key);
                            entry = source.PersistenceContext.GetEntry(managedEntity);
                            if (entry != null)
                            {
                                // we have specialized case of a detached entity from the
                                // perspective of the merge operation.  Specifically, we
                                // have an incoming entity instance which has a corresponding
                                // entry in the current persistence context, but registered
                                // under a different entity instance
                                entityState = EntityState.Detached;
                            }
                        }
                    }

                    if (entityState == EntityState.Undefined)
                    {
                        entityState = await(GetEntityStateAsync(entity, @event.EntityName, entry, source, cancellationToken)).ConfigureAwait(false);
                    }

                    switch (entityState)
                    {
                    case EntityState.Persistent:
                        await(EntityIsPersistentAsync(@event, copyCache, cancellationToken)).ConfigureAwait(false);
                        break;

                    case EntityState.Transient:
                        await(EntityIsTransientAsync(@event, copyCache, cancellationToken)).ConfigureAwait(false);
                        break;

                    case EntityState.Detached:
                        await(EntityIsDetachedAsync(@event, copyCache, cancellationToken)).ConfigureAwait(false);
                        break;

                    default:
                        throw new ObjectDeletedException("deleted instance passed to merge", null, GetLoggableName(@event.EntityName, entity));
                    }
                }
            }
        }
Example #12
0
        private async Task <object> AssembleCacheEntryAsync(CacheEntry entry, object id, IEntityPersister persister, LoadEvent @event, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            object       optionalObject        = @event.InstanceToLoad;
            IEventSource session               = @event.Session;
            ISessionFactoryImplementor factory = session.Factory;

            if (log.IsDebugEnabled())
            {
                log.Debug("assembling entity from second-level cache: {0}", MessageHelper.InfoString(persister, id, factory));
            }

            IEntityPersister subclassPersister = factory.GetEntityPersister(entry.Subclass);
            object           result            = optionalObject ?? session.Instantiate(subclassPersister, id);

            // make it circular-reference safe
            EntityKey entityKey = session.GenerateEntityKey(id, subclassPersister);

            TwoPhaseLoad.AddUninitializedCachedEntity(entityKey, result, subclassPersister, LockMode.None, entry.Version, session);

            IType[]  types  = subclassPersister.PropertyTypes;
            object[] values = await(entry.AssembleAsync(result, id, subclassPersister, session.Interceptor, session, cancellationToken)).ConfigureAwait(false);              // intializes result by side-effect
            TypeHelper.DeepCopy(values, types, subclassPersister.PropertyUpdateability, values, session);

            object version = Versioning.GetVersion(values, subclassPersister);

            if (log.IsDebugEnabled())
            {
                log.Debug("Cached Version: {0}", version);
            }

            IPersistenceContext persistenceContext = session.PersistenceContext;
            bool isReadOnly = session.DefaultReadOnly;

            if (persister.IsMutable)
            {
                object proxy = persistenceContext.GetProxy(entityKey);
                if (proxy != null)
                {
                    // this is already a proxy for this impl
                    // only set the status to read-only if the proxy is read-only
                    isReadOnly = ((INHibernateProxy)proxy).HibernateLazyInitializer.ReadOnly;
                }
            }
            else
            {
                isReadOnly = true;
            }

            persistenceContext.AddEntry(
                result,
                isReadOnly ? Status.ReadOnly : Status.Loaded,
                values,
                null,
                id,
                version,
                LockMode.None,
                true,
                subclassPersister,
                false);

            subclassPersister.AfterInitialize(result, session);
            await(persistenceContext.InitializeNonLazyCollectionsAsync(cancellationToken)).ConfigureAwait(false);
            // upgrade the lock if necessary:
            //lock(result, lockMode);

            //PostLoad is needed for EJB3
            //TODO: reuse the PostLoadEvent...
            PostLoadEvent postLoadEvent = new PostLoadEvent(session);

            postLoadEvent.Entity    = result;
            postLoadEvent.Id        = id;
            postLoadEvent.Persister = persister;

            IPostLoadEventListener[] listeners = session.Listeners.PostLoadEventListeners;
            for (int i = 0; i < listeners.Length; i++)
            {
                listeners[i].OnPostLoad(postLoadEvent);
            }
            return(result);
        }
		private static bool ExistsInDatabase(object entity, IEventSource source, IEntityPersister persister)
		{
			EntityEntry entry = source.PersistenceContext.GetEntry(entity);
			if (entry == null)
			{
				object id = persister.GetIdentifier(entity, source.EntityMode);
				if (id != null)
				{
					EntityKey key = source.GenerateEntityKey(id, persister);
					object managedEntity = source.PersistenceContext.GetEntity(key);
					entry = source.PersistenceContext.GetEntry(managedEntity);
				}
			}

			if (entry == null)
			{
				// perhaps this should be an exception since it is only ever used
				// in the above method?
				return false;
			}
			else
			{
				return entry.ExistsInDatabase;
			}
		}
Example #14
0
        public virtual void OnRefresh(RefreshEvent @event, IDictionary refreshedAlready)
        {
            IEventSource source = @event.Session;

            bool isTransient = !source.Contains(@event.Entity);

            if (source.PersistenceContext.ReassociateIfUninitializedProxy(@event.Entity))
            {
                if (isTransient)
                {
                    source.SetReadOnly(@event.Entity, source.DefaultReadOnly);
                }
                return;
            }

            object obj = source.PersistenceContext.UnproxyAndReassociate(@event.Entity);

            if (refreshedAlready.Contains(obj))
            {
                log.Debug("already refreshed");
                return;
            }

            EntityEntry      e = source.PersistenceContext.GetEntry(obj);
            IEntityPersister persister;
            object           id;

            if (e == null)
            {
                persister = source.GetEntityPersister(null, obj);                 //refresh() does not pass an entityName
                id        = persister.GetIdentifier(obj);
                if (log.IsDebugEnabled())
                {
                    log.Debug("refreshing transient {0}", MessageHelper.InfoString(persister, id, source.Factory));
                }
                EntityKey key = source.GenerateEntityKey(id, persister);
                if (source.PersistenceContext.GetEntry(key) != null)
                {
                    throw new PersistentObjectException("attempted to refresh transient instance when persistent instance was already associated with the Session: " +
                                                        MessageHelper.InfoString(persister, id, source.Factory));
                }
            }
            else
            {
                if (log.IsDebugEnabled())
                {
                    log.Debug("refreshing {0}", MessageHelper.InfoString(e.Persister, e.Id, source.Factory));
                }
                if (!e.ExistsInDatabase)
                {
                    throw new HibernateException("this instance does not yet exist as a row in the database");
                }

                persister = e.Persister;
                id        = e.Id;
            }

            // cascade the refresh prior to refreshing this entity
            refreshedAlready[obj] = obj;
            new Cascade(CascadingAction.Refresh, CascadePoint.BeforeRefresh, source).CascadeOn(persister, obj, refreshedAlready);

            if (e != null)
            {
                EntityKey key = source.GenerateEntityKey(id, persister);
                source.PersistenceContext.RemoveEntity(key);
                if (persister.HasCollections)
                {
                    new EvictVisitor(source).Process(obj, persister);
                }
            }

            if (persister.HasCache)
            {
                CacheKey ck = source.GenerateCacheKey(id, persister.IdentifierType, persister.RootEntityName);
                persister.Cache.Remove(ck);
            }

            EvictCachedCollections(persister, id, source.Factory);

            // NH Different behavior : NH-1601
            // At this point the entity need the real refresh, all elementes of collections are Refreshed,
            // the collection state was evicted, but the PersistentCollection (in the entity state)
            // is associated with a possible previous session.
            new WrapVisitor(source).Process(obj, persister);

            string previousFetchProfile = source.FetchProfile;

            source.FetchProfile = "refresh";
            object result = persister.Load(id, obj, @event.LockMode, source);

            if (result != null)
            {
                if (!persister.IsMutable)
                {
                    source.SetReadOnly(result, true);
                }
                else
                {
                    source.SetReadOnly(result, e == null ? source.DefaultReadOnly : !e.IsModifiableEntity());
                }
            }
            source.FetchProfile = previousFetchProfile;

            // NH Different behavior : we are ignoring transient entities without throw any kind of exception
            // because a transient entity is "self refreshed"
            if (!ForeignKeys.IsTransientFast(persister.EntityName, obj, @event.Session).GetValueOrDefault(result == null))
            {
                UnresolvableObjectException.ThrowIfNull(result, id, persister.EntityName);
            }
        }
		/// <summary> 
		/// Perform the entity deletion.  Well, as with most operations, does not
		/// really perform it; just schedules an action/execution with the
		/// <see cref="ActionQueue"/> for execution during flush. 
		/// </summary>
		/// <param name="session">The originating session </param>
		/// <param name="entity">The entity to delete </param>
		/// <param name="entityEntry">The entity's entry in the <see cref="ISession"/> </param>
		/// <param name="isCascadeDeleteEnabled">Is delete cascading enabled? </param>
		/// <param name="persister">The entity persister. </param>
		/// <param name="transientEntities">A cache of already deleted entities. </param>
		protected virtual void DeleteEntity(IEventSource session, object entity, EntityEntry entityEntry, bool isCascadeDeleteEnabled, IEntityPersister persister, ISet<object> transientEntities)
		{
			if (log.IsDebugEnabled)
			{
				log.Debug("deleting " + MessageHelper.InfoString(persister, entityEntry.Id, session.Factory));
			}

			IPersistenceContext persistenceContext = session.PersistenceContext;

			IType[] propTypes = persister.PropertyTypes;
			object version = entityEntry.Version;

			object[] currentState;
			if (entityEntry.LoadedState == null)
			{
				//ie. the entity came in from update()
				currentState = persister.GetPropertyValues(entity, session.EntityMode);
			}
			else
			{
				currentState = entityEntry.LoadedState;
			}

			object[] deletedState = CreateDeletedState(persister, currentState, session);
			entityEntry.DeletedState = deletedState;

			session.Interceptor.OnDelete(entity, entityEntry.Id, deletedState, persister.PropertyNames, propTypes);

			// before any callbacks, etc, so subdeletions see that this deletion happened first
			persistenceContext.SetEntryStatus(entityEntry, Status.Deleted);
			EntityKey key = session.GenerateEntityKey(entityEntry.Id, persister);

			CascadeBeforeDelete(session, persister, entity, entityEntry, transientEntities);

			new ForeignKeys.Nullifier(entity, true, false, session).NullifyTransientReferences(entityEntry.DeletedState, propTypes);
			new Nullability(session).CheckNullability(entityEntry.DeletedState, persister, true);
			persistenceContext.NullifiableEntityKeys.Add(key);

			// Ensures that containing deletions happen before sub-deletions
			session.ActionQueue.AddAction(new EntityDeleteAction(entityEntry.Id, deletedState, version, entity, persister, isCascadeDeleteEnabled, session));

			CascadeAfterDelete(session, persister, entity, transientEntities);

			// the entry will be removed after the flush, and will no longer
			// override the stale snapshot
			// This is now handled by removeEntity() in EntityDeleteAction
			//persistenceContext.removeDatabaseSnapshot(key);
		}