protected virtual async Task EntityIsPersistentAsync(PersistEvent @event, IDictionary createCache, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); log.Debug("ignoring persistent instance"); IEventSource source = @event.Session; //TODO: check that entry.getIdentifier().equals(requestedId) object entity = source.PersistenceContext.Unproxy(@event.Entity); /* NH-2565: the UnProxy may return a "field interceptor proxy". When EntityName is null the session.GetEntityPersister will try to guess it. * Instead change a session's method I'll try to guess the EntityName here. * Because I'm using a session's method perhaps could be better if each session's method, which implementation forward to a method having the EntityName as parameter, * use the BestGuessEntityName directly instead do "70 turns" before call it. */ if (@event.EntityName == null) { @event.EntityName = source.BestGuessEntityName(entity); } IEntityPersister persister = source.GetEntityPersister(@event.EntityName, entity); object tempObject; tempObject = createCache[entity]; createCache[entity] = entity; if (tempObject == null) { //TODO: merge into one method! await(CascadeBeforeSaveAsync(source, persister, entity, createCache, cancellationToken)).ConfigureAwait(false); await(CascadeAfterSaveAsync(source, persister, entity, createCache, cancellationToken)).ConfigureAwait(false); } }
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)); } } } }
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(source.BestGuessEntityName(obj), 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); } }