/// <summary>
 /// Prepares the save call using the given requested id.
 /// </summary>
 /// <param name="entity">The entity to be saved. </param>
 /// <param name="requestedId">The id to which to associate the entity. </param>
 /// <param name="entityName">The name of the entity being saved. </param>
 /// <param name="anything">Generally cascade-specific information. </param>
 /// <param name="source">The session which is the source of this save event. </param>
 /// <param name="cancellationToken">A cancellation token that can be used to cancel the work</param>
 /// <returns> The id used to save the entity. </returns>
 protected virtual Task <object> SaveWithRequestedIdAsync(object entity, object requestedId, string entityName, object anything, IEventSource source, CancellationToken cancellationToken)
 {
     if (cancellationToken.IsCancellationRequested)
     {
         return(Task.FromCanceled <object>(cancellationToken));
     }
     try
     {
         return(PerformSaveAsync(entity, requestedId, source.GetEntityPersister(entityName, entity), false, anything, source, true, cancellationToken));
     }
     catch (Exception ex)
     {
         return(Task.FromException <object>(ex));
     }
 }
        protected virtual void EntityIsPersistent(MergeEvent @event, IDictionary copyCache)
        {
            log.Debug("ignoring persistent instance");

            //TODO: check that entry.getIdentifier().equals(requestedId)
            object           entity    = @event.Entity;
            IEventSource     source    = @event.Session;
            IEntityPersister persister = source.GetEntityPersister(@event.EntityName, entity);

            copyCache[entity] = entity;             //before cascade!

            CascadeOnMerge(source, persister, entity, copyCache);
            CopyValues(persister, entity, entity, source, copyCache);

            @event.Result = entity;
        }
Пример #3
0
        protected virtual async Task EntityIsPersistentAsync(MergeEvent @event, IDictionary copyCache, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            log.Debug("ignoring persistent instance");

            //TODO: check that entry.getIdentifier().equals(requestedId)

            object           entity    = @event.Entity;
            IEventSource     source    = @event.Session;
            IEntityPersister persister = source.GetEntityPersister(@event.EntityName, entity);

            ((EventCache)copyCache).Add(entity, entity, true);             //before cascade!

            await(CascadeOnMergeAsync(source, persister, entity, copyCache, cancellationToken)).ConfigureAwait(false);
            await(CopyValuesAsync(persister, entity, entity, source, copyCache, cancellationToken)).ConfigureAwait(false);

            @event.Result = entity;
        }
        protected virtual void EntityIsTransient(MergeEvent @event, IDictionary copyCache)
        {
            log.Debug("merging transient instance");

            object       entity = @event.Entity;
            IEventSource source = @event.Session;

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

            object id = persister.HasIdentifierProperty ? persister.GetIdentifier(entity, source.EntityMode) : null;

            object copy = persister.Instantiate(id, source.EntityMode); // should this be Session.instantiate(Persister, ...)?

            copyCache[entity] = copy;                                   //before cascade!

            // cascade first, so that all unsaved objects get their
            // copy created before we actually copy
            //cascadeOnMerge(event, persister, entity, copyCache, Cascades.CASCADE_BEFORE_MERGE);
            base.CascadeBeforeSave(source, persister, entity, copyCache);
            CopyValues(persister, entity, copy, source, copyCache, ForeignKeyDirection.ForeignKeyFromParent);

            //this bit is only *really* absolutely necessary for handling
            //requestedId, but is also good if we merge multiple object
            //graphs, since it helps ensure uniqueness
            object requestedId = @event.RequestedId;

            if (requestedId == null)
            {
                SaveWithGeneratedId(copy, entityName, copyCache, source, false);
            }
            else
            {
                SaveWithRequestedId(copy, requestedId, entityName, copyCache, source);
            }

            // cascade first, so that all unsaved objects get their
            // copy created before we actually copy
            base.CascadeAfterSave(source, persister, entity, copyCache);
            CopyValues(persister, entity, copy, source, copyCache, ForeignKeyDirection.ForeignKeyToParent);

            @event.Result = copy;
        }
        protected virtual void EntityIsPersistent(PersistEvent @event, IDictionary createCache)
        {
            log.Debug("ignoring persistent instance");
            IEventSource source = @event.Session;

            //TODO: check that entry.getIdentifier().equals(requestedId)
            object           entity    = source.PersistenceContext.Unproxy(@event.Entity);
            IEntityPersister persister = source.GetEntityPersister(@event.EntityName, entity);

            object tempObject;

            tempObject          = createCache[entity];
            createCache[entity] = entity;
            if (tempObject == null)
            {
                //TODO: merge into one method!
                CascadeBeforeSave(source, persister, entity, createCache);
                CascadeAfterSave(source, persister, entity, createCache);
            }
        }
        protected object GetValue(Property property, object[] state)
        {
            var index = System.Array.IndexOf(Persister.PropertyNames, property.Name);

            if (index < 0)
            {
                return(null);
            }

            var value = state[index];

            // For entities, return the entity id.
            if (value != null && property.Type is EntityType entityType)
            {
                var entityName = entityType.GetAssociatedEntityName();
                var persister  = Session.GetEntityPersister(entityName, value);
                var id         = persister.GetIdentifier(value);
                value = id;
            }

            return(value);
        }
		/// <summary> 
		/// Prepares the save call using the given requested id. 
		/// </summary>
		/// <param name="entity">The entity to be saved. </param>
		/// <param name="requestedId">The id to which to associate the entity. </param>
		/// <param name="entityName">The name of the entity being saved. </param>
		/// <param name="anything">Generally cascade-specific information. </param>
		/// <param name="source">The session which is the source of this save event. </param>
		/// <returns> The id used to save the entity. </returns>
		protected virtual object SaveWithRequestedId(object entity, object requestedId, string entityName, object anything, IEventSource source)
		{
			return PerformSave(entity, requestedId, source.GetEntityPersister(entityName, entity), false, anything, source, true);
		}
		/// <summary> 
		/// Prepares the save call using a newly generated id. 
		/// </summary>
		/// <param name="entity">The entity to be saved </param>
		/// <param name="entityName">The entity-name for the entity to be saved </param>
		/// <param name="anything">Generally cascade-specific information. </param>
		/// <param name="source">The session which is the source of this save event. </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 SaveWithGeneratedId(object entity, string entityName, object anything, IEventSource source, bool requiresImmediateIdAccess)
		{
			IEntityPersister persister = source.GetEntityPersister(entityName, entity);
			object generatedId = persister.IdentifierGenerator.Generate(source, entity);
			if (generatedId == null)
			{
				throw new IdentifierGenerationException("null id generated for:" + entity.GetType());
			}
			else if (generatedId == IdentifierGeneratorFactory.ShortCircuitIndicator)
			{
				return source.GetIdentifier(entity);
			}
			else if (generatedId == IdentifierGeneratorFactory.PostInsertIndicator)
			{
				return PerformSave(entity, null, persister, true, anything, source, requiresImmediateIdAccess);
			}
			else
			{
				if (log.IsDebugEnabled)
				{
					log.Debug(string.Format("generated identifier: {0}, using strategy: {1}",
						persister.IdentifierType.ToLoggableString(generatedId, source.Factory),
						persister.IdentifierGenerator.GetType().FullName));
				}
				return PerformSave(entity, generatedId, persister, false, anything, source, true);
			}
		}
        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);
            }
        }
        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);
            }
        }
Пример #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));
                    }
                }
            }
        }
Пример #12
0
        protected virtual async Task EntityIsDetachedAsync(MergeEvent @event, IDictionary copyCache, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            log.Debug("merging detached instance");

            object       entity = @event.Entity;
            IEventSource source = @event.Session;

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

            object id = @event.RequestedId;

            if (id == null)
            {
                id = persister.GetIdentifier(entity);
            }
            else
            {
                // check that entity id = requestedId
                object entityId = persister.GetIdentifier(entity);
                if (!persister.IdentifierType.IsEqual(id, entityId, source.Factory))
                {
                    throw new HibernateException("merge requested with id not matching id of passed entity");
                }
            }

            string previousFetchProfile = source.FetchProfile;

            source.FetchProfile = "merge";

            //we must clone embedded composite identifiers, or
            //we will get back the same instance that we pass in
            object clonedIdentifier = persister.IdentifierType.DeepCopy(id, source.Factory);
            object result           = await(source.GetAsync(persister.EntityName, clonedIdentifier, cancellationToken)).ConfigureAwait(false);

            source.FetchProfile = previousFetchProfile;

            if (result == null)
            {
                //TODO: we should throw an exception if we really *know* for sure
                //      that this is a detached instance, rather than just assuming
                //throw new StaleObjectStateException(entityName, id);

                // we got here because we assumed that an instance
                // with an assigned id was detached, when it was
                // really persistent
                await(EntityIsTransientAsync(@event, copyCache, cancellationToken)).ConfigureAwait(false);
            }
            else
            {
                // NH different behavior : NH-1517
                if (InvokeUpdateLifecycle(entity, persister, source))
                {
                    return;
                }

                ((EventCache)copyCache).Add(entity, result, true);                 //before cascade!

                object target = source.PersistenceContext.Unproxy(result);
                if (target == entity)
                {
                    throw new AssertionFailure("entity was not detached");
                }
                else if (!(await(source.GetEntityNameAsync(target, cancellationToken)).ConfigureAwait(false)).Equals(entityName))
                {
                    throw new WrongClassException("class of the given object did not match class of persistent copy",
                                                  @event.RequestedId, persister.EntityName);
                }
                else if (IsVersionChanged(entity, source, persister, target))
                {
                    if (source.Factory.Statistics.IsStatisticsEnabled)
                    {
                        source.Factory.StatisticsImplementor.OptimisticFailure(entityName);
                    }
                    throw new StaleObjectStateException(persister.EntityName, id);
                }

                // cascade first, so that all unsaved objects get their
                // copy created before we actually copy
                await(CascadeOnMergeAsync(source, persister, entity, copyCache, cancellationToken)).ConfigureAwait(false);
                await(CopyValuesAsync(persister, entity, target, source, copyCache, cancellationToken)).ConfigureAwait(false);

                //copyValues works by reflection, so explicitly mark the entity instance dirty
                MarkInterceptorDirty(entity, target);

                @event.Result = result;
            }
        }
		private object MergeTransientEntity(object entity, string entityName, object requestedId, IEventSource source, IDictionary copyCache)
		{
			IEntityPersister persister = source.GetEntityPersister(entityName, entity);

			object id = persister.HasIdentifierProperty ? persister.GetIdentifier(entity, source.EntityMode) : null;
			object copy = null;
			
			if (copyCache.Contains(entity))
			{
				copy = copyCache[entity];
				persister.SetIdentifier(copy, id, source.EntityMode);
			}
			else
			{
				copy = source.Instantiate(persister, id);
				((EventCache)copyCache).Add(entity, copy, true); // before cascade!
			}

			// cascade first, so that all unsaved objects get their
			// copy created before we actually copy
			//cascadeOnMerge(event, persister, entity, copyCache, Cascades.CASCADE_BEFORE_MERGE);
			base.CascadeBeforeSave(source, persister, entity, copyCache);
			CopyValues(persister, entity, copy, source, copyCache, ForeignKeyDirection.ForeignKeyFromParent);

			try
			{
				// try saving; check for non-nullable properties that are null or transient entities before saving
				this.SaveTransientEntity(copy, entityName, requestedId, source, copyCache);
			}
			catch (PropertyValueException ex)
			{
				string propertyName = ex.PropertyName;
				object propertyFromCopy = persister.GetPropertyValue(copy, propertyName, source.EntityMode);
				object propertyFromEntity = persister.GetPropertyValue(entity, propertyName, source.EntityMode);
				IType propertyType = persister.GetPropertyType(propertyName);
				EntityEntry copyEntry = source.PersistenceContext.GetEntry(copy);

				if (propertyFromCopy == null || !propertyType.IsEntityType)
				{
					log.InfoFormat("property '{0}.{1}' is null or not an entity; {1} =[{2}]", copyEntry.EntityName, propertyName, propertyFromCopy);
					throw;
				}

				if (!copyCache.Contains(propertyFromEntity))
				{
					log.InfoFormat("property '{0}.{1}' from original entity is not in copyCache; {1} =[{2}]", copyEntry.EntityName, propertyName, propertyFromEntity);
					throw;
				}
				
				if (((EventCache)copyCache).IsOperatedOn(propertyFromEntity))
				{
					log.InfoFormat("property '{0}.{1}' from original entity is in copyCache and is in the process of being merged; {1} =[{2}]", copyEntry.EntityName, propertyName, propertyFromEntity);
				}
				else
				{
					log.InfoFormat("property '{0}.{1}' from original entity is in copyCache and is not in the process of being merged; {1} =[{2}]", copyEntry.EntityName, propertyName, propertyFromEntity);
				}
				
				// continue...; we'll find out if it ends up not getting saved later
			}
			
			// cascade first, so that all unsaved objects get their
			// copy created before we actually copy
			base.CascadeAfterSave(source, persister, entity, copyCache);
			CopyValues(persister, entity, copy, source, copyCache, ForeignKeyDirection.ForeignKeyToParent);

			return copy;
		}
Пример #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);
            }
        }
        public virtual void OnMerge(MergeEvent @event, IDictionary copyCache)
        {
            IEventSource source   = @event.Session;
            object       original = @event.Original;

            if (original != null)
            {
                object entity;
                if (original is INHibernateProxy)
                {
                    ILazyInitializer li = ((INHibernateProxy)original).HibernateLazyInitializer;
                    if (li.IsUninitialized)
                    {
                        log.Debug("ignoring uninitialized proxy");
                        @event.Result = source.Load(li.PersistentClass, li.Identifier);
                        return;                         //EARLY EXIT!
                    }
                    else
                    {
                        entity = li.GetImplementation();
                    }
                }
                else
                {
                    entity = original;
                }

                if (copyCache.Contains(entity))
                {
                    log.Debug("already merged");
                    @event.Result = entity;
                }
                else
                {
                    @event.Entity = entity;
                    EntityState entityState = EntityState.Undefined;

                    // 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, source.EntityMode);
                        if (id != null)
                        {
                            EntityKey key           = new EntityKey(id, persister, source.EntityMode);
                            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 = GetEntityState(entity, @event.EntityName, entry, source);
                    }

                    switch (entityState)
                    {
                    case EntityState.Persistent:
                        EntityIsPersistent(@event, copyCache);
                        break;

                    case EntityState.Transient:
                        EntityIsTransient(@event, copyCache);
                        break;

                    case EntityState.Detached:
                        EntityIsDetached(@event, copyCache);
                        break;

                    default:
                        throw new ObjectDeletedException("deleted instance passed to merge", null, GetLoggableName(@event.EntityName, entity));
                    }
                }
            }
        }
Пример #16
0
 /// <summary>
 /// Prepares the save call using the given requested id.
 /// </summary>
 /// <param name="entity">The entity to be saved. </param>
 /// <param name="requestedId">The id to which to associate the entity. </param>
 /// <param name="entityName">The name of the entity being saved. </param>
 /// <param name="anything">Generally cascade-specific information. </param>
 /// <param name="source">The session which is the source of this save event. </param>
 /// <returns> The id used to save the entity. </returns>
 protected virtual object SaveWithRequestedId(object entity, object requestedId, string entityName, object anything, IEventSource source)
 {
     return(PerformSave(entity, requestedId, source.GetEntityPersister(entityName, entity), false, anything, source, true));
 }
Пример #17
0
        private async Task <object> MergeTransientEntityAsync(object entity, string entityName, object requestedId, IEventSource source, IDictionary copyCache, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            IEntityPersister persister = source.GetEntityPersister(entityName, entity);

            object id   = persister.HasIdentifierProperty ? persister.GetIdentifier(entity) : null;
            object copy = null;

            if (copyCache.Contains(entity))
            {
                copy = copyCache[entity];
                persister.SetIdentifier(copy, id);
            }
            else
            {
                copy = source.Instantiate(persister, id);
                ((EventCache)copyCache).Add(entity, copy, true);                 // before cascade!
            }

            // cascade first, so that all unsaved objects get their
            // copy created before we actually copy
            //cascadeOnMerge(event, persister, entity, copyCache, Cascades.CASCADE_BEFORE_MERGE);
            await(base.CascadeBeforeSaveAsync(source, persister, entity, copyCache, cancellationToken)).ConfigureAwait(false);
            await(CopyValuesAsync(persister, entity, copy, source, copyCache, ForeignKeyDirection.ForeignKeyFromParent, cancellationToken)).ConfigureAwait(false);

            try
            {
                // try saving; check for non-nullable properties that are null or transient entities before saving
                await(this.SaveTransientEntityAsync(copy, entityName, requestedId, source, copyCache, cancellationToken)).ConfigureAwait(false);
            }
            catch (PropertyValueException ex)
            {
                string      propertyName       = ex.PropertyName;
                object      propertyFromCopy   = persister.GetPropertyValue(copy, propertyName);
                object      propertyFromEntity = persister.GetPropertyValue(entity, propertyName);
                IType       propertyType       = persister.GetPropertyType(propertyName);
                EntityEntry copyEntry          = source.PersistenceContext.GetEntry(copy);

                if (propertyFromCopy == null || !propertyType.IsEntityType)
                {
                    log.Info("property '{0}.{1}' is null or not an entity; {1} =[{2}]", copyEntry.EntityName, propertyName, propertyFromCopy);
                    throw;
                }

                if (!copyCache.Contains(propertyFromEntity))
                {
                    log.Info("property '{0}.{1}' from original entity is not in copyCache; {1} =[{2}]", copyEntry.EntityName, propertyName, propertyFromEntity);
                    throw;
                }

                if (((EventCache)copyCache).IsOperatedOn(propertyFromEntity))
                {
                    log.Info(ex, "property '{0}.{1}' from original entity is in copyCache and is in the process of being merged; {1} =[{2}]", copyEntry.EntityName, propertyName, propertyFromEntity);
                }
                else
                {
                    log.Info(ex, "property '{0}.{1}' from original entity is in copyCache and is not in the process of being merged; {1} =[{2}]", copyEntry.EntityName, propertyName, propertyFromEntity);
                }

                // continue...; we'll find out if it ends up not getting saved later
            }

            // cascade first, so that all unsaved objects get their
            // copy created before we actually copy
            await(base.CascadeAfterSaveAsync(source, persister, entity, copyCache, cancellationToken)).ConfigureAwait(false);
            await(CopyValuesAsync(persister, entity, copy, source, copyCache, ForeignKeyDirection.ForeignKeyToParent, cancellationToken)).ConfigureAwait(false);

            return(copy);
        }
    public virtual void OnRefresh(RefreshEvent @event, IDictionary refreshedAlready)
    {
        IEventSource source = @event.Session;

        if (source.PersistenceContext.ReassociateIfUninitializedProxy(@event.Entity))
        {
            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, source.EntityMode);
            if (log.IsDebugEnabled)
            {
                log.Debug("refreshing transient " + MessageHelper.InfoString(persister, id, source.Factory));
            }
            EntityKey key = new EntityKey(id, persister, source.EntityMode);
            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 " + 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 = new EntityKey(id, persister, source.EntityMode);
            source.PersistenceContext.RemoveEntity(key);
            if (persister.HasCollections)
            {
                new EvictVisitor(source).Process(obj, persister);
            }
        }
        if (persister.HasCache)
        {
            CacheKey ck = new CacheKey(id, persister.IdentifierType, persister.RootEntityName, source.EntityMode, source.Factory);
            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);
        // NH-3253: Forcing simple load to prevent the redundante instance
        //on session:
        //Ocurre when:
        // -there is a 'one-to-many' 'Parent-Child' relationship;
        // -AND the 'Parent' 'many-to-one' association is ' fetch="select" ';
        // -AND 'Child' is using a 'composite-id' and 'key-many-to-one' to the
        //'Parent'.
        object result;

        if (this.IsReferencedByCompositeId(persister))
        {
            result = persister.Load(id, obj, @event.LockMode, source);
        }
        string previousFetchProfile = source.FetchProfile;

        source.FetchProfile = "refresh";
        result = persister.Load(id, obj, @event.LockMode, source);
        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.IsTransient(persister.EntityName, obj, result == null, @event.Session))
        {
            UnresolvableObjectException.ThrowIfNull(result, id, persister.EntityName);
        }
    }