/// <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); }
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; }