Пример #1
0
        /// <summary>
        /// Determine which merged entities in the copyCache are transient.
        /// </summary>
        /// <param name="event"></param>
        /// <param name="copyCache"></param>
        /// <param name="cancellationToken">A cancellation token that can be used to cancel the work</param>
        /// <returns></returns>
        /// <remarks>Should this method be on the EventCache class?</remarks>
        protected async Task <EventCache> GetTransientCopyCacheAsync(MergeEvent @event, EventCache copyCache, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            EventCache transientCopyCache = new EventCache();

            foreach (object entity in copyCache.Keys)
            {
                object entityCopy = copyCache[entity];

                if (entityCopy.IsProxy())
                {
                    entityCopy = await(((INHibernateProxy)entityCopy).HibernateLazyInitializer.GetImplementationAsync(cancellationToken)).ConfigureAwait(false);
                }

                // NH-specific: Disregard entities that implement ILifecycle and manage their own state - they
                // don't have an EntityEntry, and we can't determine if they are transient or not
                if (entityCopy is ILifecycle)
                {
                    continue;
                }

                EntityEntry copyEntry = @event.Session.PersistenceContext.GetEntry(entityCopy);

                if (copyEntry == null)
                {
                    // entity name will not be available for non-POJO entities
                    // TODO: cache the entity name somewhere so that it is available to this exception
                    log.Info(
                        "transient instance could not be processed by merge: {0} [{1}]",
                        @event.Session.GuessEntityName(entityCopy),
                        entity);

                    // merge did not cascade to this entity; it's in copyCache because a
                    // different entity has a non-nullable reference to it;
                    // this entity should not be put in transientCopyCache, because it was
                    // not included in the merge;

                    throw new TransientObjectException(
                              "object is an unsaved transient instance - save the transient instance before merging: " + @event.Session.GuessEntityName(entityCopy));
                }
                else if (copyEntry.Status == Status.Saving)
                {
                    transientCopyCache.Add(entity, entityCopy, copyCache.IsOperatedOn(entity));
                }
                else if (copyEntry.Status != Status.Loaded && copyEntry.Status != Status.ReadOnly)
                {
                    throw new AssertionFailure(
                              String.Format(
                                  "Merged entity does not have status set to MANAGED or READ_ONLY; {0} status = {1}",
                                  entityCopy,
                                  copyEntry.Status));
                }
            }
            return(transientCopyCache);
        }
Пример #2
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));
                    }
                }
            }
        }
		/// <summary>
		/// Determine which merged entities in the copyCache are transient.
		/// </summary>
		/// <param name="event"></param>
		/// <param name="copyCache"></param>
		/// <returns></returns>
		/// <remarks>Should this method be on the EventCache class?</remarks>
		protected EventCache GetTransientCopyCache(MergeEvent @event, EventCache copyCache)
		{
			EventCache transientCopyCache = new EventCache();

			foreach(object entity in copyCache.Keys)
			{
				object entityCopy = copyCache[entity];
				
				if (entityCopy.IsProxy())
					entityCopy = ((INHibernateProxy)entityCopy).HibernateLazyInitializer.GetImplementation();
				
				// NH-specific: Disregard entities that implement ILifecycle and manage their own state - they 
				// don't have an EntityEntry, and we can't determine if they are transient or not
				if (entityCopy is ILifecycle)
					continue;
			
				EntityEntry copyEntry = @event.Session.PersistenceContext.GetEntry(entityCopy);

				if (copyEntry == null)
				{
					// entity name will not be available for non-POJO entities
					// TODO: cache the entity name somewhere so that it is available to this exception
					log.InfoFormat(
						"transient instance could not be processed by merge: {0} [{1}]",
						@event.Session.GuessEntityName(entityCopy),
						entity);
					
					// merge did not cascade to this entity; it's in copyCache because a
					// different entity has a non-nullable reference to it;
					// this entity should not be put in transientCopyCache, because it was
					// not included in the merge;
					
					throw new TransientObjectException(
						"object is an unsaved transient instance - save the transient instance before merging: " + @event.Session.GuessEntityName(entityCopy));
				}
				else if (copyEntry.Status == Status.Saving)
				{
					transientCopyCache.Add(entity, entityCopy, copyCache.IsOperatedOn(entity));
				}
				else if (copyEntry.Status != Status.Loaded && copyEntry.Status != Status.ReadOnly)
				{
					throw new AssertionFailure(
						String.Format(
							"Merged entity does not have status set to MANAGED or READ_ONLY; {0} status = {1}",
							entityCopy,
							copyEntry.Status));
				}
			}
			return transientCopyCache;
		}