예제 #1
0
        private object GetNextVersion(FlushEntityEvent @event)
        {
            // Convience method to retrieve an entities next version value
            EntityEntry      entry     = @event.EntityEntry;
            IEntityPersister persister = entry.Persister;

            if (persister.IsVersioned)
            {
                object[] values = @event.PropertyValues;

                if (entry.IsBeingReplicated)
                {
                    return(Versioning.GetVersion(values, persister));
                }
                else
                {
                    int[] dirtyProperties = @event.DirtyProperties;

                    bool isVersionIncrementRequired = IsVersionIncrementRequired(@event, entry, persister, dirtyProperties);

                    object nextVersion = isVersionIncrementRequired ?
                                         Versioning.Increment(entry.Version, persister.VersionType, @event.Session) :
                                         entry.Version;        //use the current version

                    Versioning.SetVersion(values, nextVersion, persister);

                    return(nextVersion);
                }
            }
            else
            {
                return(null);
            }
        }
예제 #2
0
        protected virtual async Task <bool> HandleInterceptionAsync(FlushEntityEvent @event, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            ISessionImplementor session   = @event.Session;
            EntityEntry         entry     = @event.EntityEntry;
            IEntityPersister    persister = entry.Persister;
            object entity = @event.Entity;

            //give the Interceptor a chance to modify property values
            object[] values      = @event.PropertyValues;
            bool     intercepted = InvokeInterceptor(session, entity, entry, values, persister);

            //now we might need to recalculate the dirtyProperties array
            if (intercepted && @event.DirtyCheckPossible && [email protected])
            {
                int[] dirtyProperties;
                if (@event.HasDatabaseSnapshot)
                {
                    dirtyProperties = await(persister.FindModifiedAsync(@event.DatabaseSnapshot, values, entity, session, cancellationToken)).ConfigureAwait(false);
                }
                else
                {
                    dirtyProperties = await(persister.FindDirtyAsync(values, entry.LoadedState, entity, session, cancellationToken)).ConfigureAwait(false);
                }
                @event.DirtyProperties = dirtyProperties;
            }

            return(intercepted);
        }
예제 #3
0
        /// <summary>
        /// Performs all necessary checking to determine if an entity needs an SQL update
        /// to synchronize its state to the database. Modifies the event by side-effect!
        /// Note: this method is quite slow, avoid calling if possible!
        /// </summary>
        protected Task <bool> IsUpdateNecessaryAsync(FlushEntityEvent @event, CancellationToken cancellationToken)
        {
            if (cancellationToken.IsCancellationRequested)
            {
                return(Task.FromCanceled <bool>(cancellationToken));
            }
            try
            {
                IEntityPersister persister = @event.EntityEntry.Persister;
                Status           status    = @event.EntityEntry.Status;

                if ([email protected])
                {
                    return(Task.FromResult <bool>(true));
                }
                else
                {
                    int[] dirtyProperties = @event.DirtyProperties;
                    if (dirtyProperties != null && dirtyProperties.Length != 0)
                    {
                        return(Task.FromResult <bool>(true));                       //TODO: suck into event class
                    }
                    else
                    {
                        return(HasDirtyCollectionsAsync(@event, persister, status, cancellationToken));
                    }
                }
            }
            catch (Exception ex)
            {
                return(Task.FromException <bool>(ex));
            }
        }
예제 #4
0
        // 1. detect any dirty entities
        // 2. schedule any entity updates
        // 3. search out any reachable collections
        private void FlushEntities(FlushEvent @event)
        {
            log.Debug("Flushing entities and processing referenced collections");

            // Among other things, updateReachables() will recursively load all
            // collections that are moving roles. This might cause entities to
            // be loaded.

            // So this needs to be safe from concurrent modification problems.
            // It is safe because of how IdentityMap implements entrySet()
            IEventSource source = @event.Session;

            ICollection list = IdentityMap.ConcurrentEntries(source.PersistenceContext.EntityEntries);

            foreach (DictionaryEntry me in list)
            {
                // Update the status of the object and if necessary, schedule an update
                EntityEntry entry  = (EntityEntry)me.Value;
                Status      status = entry.Status;

                if (status != Status.Loading && status != Status.Gone)
                {
                    FlushEntityEvent            entityEvent = new FlushEntityEvent(source, me.Key, entry);
                    IFlushEntityEventListener[] listeners   = source.Listeners.FlushEntityEventListeners;
                    foreach (IFlushEntityEventListener listener in listeners)
                    {
                        listener.OnFlushEntity(entityEvent);
                    }
                }
            }
            source.ActionQueue.SortActions();
        }
예제 #5
0
        private async Task <bool> IsUpdateNecessaryAsync(FlushEntityEvent @event, bool mightBeDirty, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            Status status = @event.EntityEntry.Status;

            if (mightBeDirty || status == Status.Deleted)
            {
                // compare to cached state (ignoring collections unless versioned)
                await(DirtyCheckAsync(@event, cancellationToken)).ConfigureAwait(false);
                if (await(IsUpdateNecessaryAsync(@event, cancellationToken)).ConfigureAwait(false))
                {
                    return(true);
                }
                else
                {
                    // TODO H3.2 Different behaviour
                    //FieldInterceptionHelper.clearDirty(@event.Entity);
                    return(false);
                }
            }
            else
            {
                return(await(HasDirtyCollectionsAsync(@event, @event.EntityEntry.Persister, status, cancellationToken)).ConfigureAwait(false));
            }
        }
예제 #6
0
        private async Task <object> GetNextVersionAsync(FlushEntityEvent @event, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            // Convience method to retrieve an entities next version value
            EntityEntry      entry     = @event.EntityEntry;
            IEntityPersister persister = entry.Persister;

            if (persister.IsVersioned)
            {
                object[] values = @event.PropertyValues;

                if (entry.IsBeingReplicated)
                {
                    return(Versioning.GetVersion(values, persister));
                }
                else
                {
                    int[] dirtyProperties = @event.DirtyProperties;

                    bool isVersionIncrementRequired = IsVersionIncrementRequired(@event, entry, persister, dirtyProperties);

                    object nextVersion = isVersionIncrementRequired ?
                                         await(Versioning.IncrementAsync(entry.Version, persister.VersionType, @event.Session, cancellationToken)).ConfigureAwait(false) :
                                         entry.Version;        //use the current version

                    Versioning.SetVersion(values, nextVersion, persister);

                    return(nextVersion);
                }
            }
            else
            {
                return(null);
            }
        }
예제 #7
0
        protected virtual bool HandleInterception(FlushEntityEvent @event)
        {
            ISessionImplementor session   = @event.Session;
            EntityEntry         entry     = @event.EntityEntry;
            IEntityPersister    persister = entry.Persister;
            object entity = @event.Entity;

            //give the Interceptor a chance to modify property values
            object[] values      = @event.PropertyValues;
            bool     intercepted = InvokeInterceptor(session, entity, entry, values, persister);

            //now we might need to recalculate the dirtyProperties array
            if (intercepted && @event.DirtyCheckPossible && [email protected])
            {
                int[] dirtyProperties;
                if (@event.HasDatabaseSnapshot)
                {
                    dirtyProperties = persister.FindModified(@event.DatabaseSnapshot, values, entity, session);
                }
                else
                {
                    dirtyProperties = persister.FindDirty(values, entry.LoadedState, entity, session);
                }
                @event.DirtyProperties = dirtyProperties;
            }

            return(intercepted);
        }
예제 #8
0
        static IEnumerable <int> GetAdditionalDirtyProperties(FlushEntityEvent @event)
        {
            yield return(Array.IndexOf(@event.EntityEntry.Persister.PropertyNames,
                                       "UpdatedAt"));

            yield return(Array.IndexOf(@event.EntityEntry.Persister.PropertyNames,
                                       "UpdatedBy"));
        }
예제 #9
0
        public void OnFlushEntity(FlushEntityEvent @event)
        {
            var validator = @event.Session.SessionFactory.GetValidator();

            if (validator != null)
            {
                validator.Validate(@event.Entity);
            }
        }
예제 #10
0
        public void OnFlushEntity(FlushEntityEvent @event)
        {
            if (@event.EntityEntry.Status == Status.Deleted)
            {
                return;
            }

            SetModificationDateIfPossible(@event.Entity);
        }
 public void OnFlushEntity(FlushEntityEvent @event)
 {
     if (@event.Entity is Product)
     {
         if (ProductCreated != null)
         {
             ProductCreated(@event.Entity as Product);
         }
     }
 }
예제 #12
0
 protected override void DirtyCheck(FlushEntityEvent @event)
 {
     base.DirtyCheck(@event);
     if (@event.DirtyProperties != null &&
         @event.DirtyProperties.Any() &&
         @event.Entity is IAuditableEntity)
     {
         @event.DirtyProperties = @event.DirtyProperties.Concat(GetAdditionalDirtyProperties(@event)).ToArray();
     }
 }
예제 #13
0
 //https://stackoverflow.com/questions/5087888/ipreupdateeventlistener-and-dynamic-update-true
 protected override void DirtyCheck(FlushEntityEvent e)
 {
     base.DirtyCheck(e);
     if (e.DirtyProperties != null &&
         e.DirtyProperties.Any() &&
         e.Entity is IHaveAuditInformation)
     {
         e.DirtyProperties = e.DirtyProperties
                             .Concat(GetAdditionalDirtyProperties(e)).ToArray();
     }
 }
예제 #14
0
        /// <summary> Perform a dirty check, and attach the results to the event</summary>
        protected virtual void DirtyCheck(FlushEntityEvent @event)
        {
            object entity = @event.Entity;

            object[]            values    = @event.PropertyValues;
            ISessionImplementor session   = @event.Session;
            EntityEntry         entry     = @event.EntityEntry;
            IEntityPersister    persister = entry.Persister;
            object id = entry.Id;

            object[] loadedState = entry.LoadedState;

            int[] dirtyProperties = session.Interceptor.FindDirty(entity, id, values, loadedState, persister.PropertyNames, persister.PropertyTypes);

            @event.DatabaseSnapshot = null;

            bool interceptorHandledDirtyCheck;
            bool cannotDirtyCheck;

            if (dirtyProperties == null)
            {
                // Interceptor returned null, so do the dirtycheck ourself, if possible
                interceptorHandledDirtyCheck = false;

                cannotDirtyCheck = loadedState == null;                 // object loaded by update()
                if (!cannotDirtyCheck)
                {
                    // dirty check against the usual snapshot of the entity
                    dirtyProperties = persister.FindDirty(values, loadedState, entity, session);
                }
                else
                {
                    // dirty check against the database snapshot, if possible/necessary
                    object[] databaseSnapshot = GetDatabaseSnapshot(session, persister, id);
                    if (databaseSnapshot != null)
                    {
                        dirtyProperties         = persister.FindModified(databaseSnapshot, values, entity, session);
                        cannotDirtyCheck        = false;
                        @event.DatabaseSnapshot = databaseSnapshot;
                    }
                }
            }
            else
            {
                // the Interceptor handled the dirty checking
                cannotDirtyCheck             = false;
                interceptorHandledDirtyCheck = true;
            }

            @event.DirtyProperties = dirtyProperties;
            @event.DirtyCheckHandledByInterceptor = interceptorHandledDirtyCheck;
            @event.DirtyCheckPossible             = !cannotDirtyCheck;
        }
예제 #15
0
        private bool IsVersionIncrementRequired(FlushEntityEvent @event, EntityEntry entry, IEntityPersister persister, int[] dirtyProperties)
        {
            // NH different behavior: because NH-1756 when PostInsertId is used with a generated version
            // the version is read inmediately after save and does not need to be incremented.
            // BTW, in general, a generated version does not need to be incremented by NH.

            bool isVersionIncrementRequired =
                entry.Status != Status.Deleted && !persister.IsVersionPropertyGenerated &&
                (dirtyProperties == null ||
                 Versioning.IsVersionIncrementRequired(dirtyProperties, @event.HasDirtyCollection, persister.PropertyVersionability));

            return(isVersionIncrementRequired);
        }
예제 #16
0
        protected Boolean HasDirtyProperties(FlushEntityEvent @event)
        {
            if ((@event.EntityEntry.RequiresDirtyCheck(@event.Entity) == false) || (@event.EntityEntry.ExistsInDatabase == false) || (@event.EntityEntry.LoadedState == null))
            {
                return(false);
            }

            Object[] currentState = @event.EntityEntry.Persister.GetPropertyValues(@event.Entity, @event.Session.EntityMode);
            Object[] loadedState  = @event.EntityEntry.LoadedState;

            return(@event.EntityEntry.Persister.EntityMetamodel.Properties
                   .Where((property, i) => (LazyPropertyInitializer.UnfetchedProperty.Equals(currentState[i]) == false) && (property.Type.IsDirty(loadedState[i], currentState[i], @event.Session) == true))
                   .Any());
        }
예제 #17
0
 private bool HasDirtyCollections(FlushEntityEvent @event, IEntityPersister persister, Status status)
 {
     if (IsCollectionDirtyCheckNecessary(persister, status))
     {
         DirtyCollectionSearchVisitor visitor = new DirtyCollectionSearchVisitor(@event.Session, persister.PropertyVersionability);
         visitor.ProcessEntityPropertyValues(@event.PropertyValues, persister.PropertyTypes);
         bool hasDirtyCollections = visitor.WasDirtyCollectionFound;
         @event.HasDirtyCollection = hasDirtyCollections;
         return(hasDirtyCollections);
     }
     else
     {
         return(false);
     }
 }
        public void OnFlushEntity(FlushEntityEvent @event)
        {
            object entity = @event.Entity;

            IAggregateState oldState = entity as IAggregateState;

            if (oldState == null)
            {
                return;
            }

            IAggregateState newState = oldState.AggregateRoot.GetState();

            @event.Session.Merge(newState);
        }
예제 #19
0
 private async Task <bool> HasDirtyCollectionsAsync(FlushEntityEvent @event, IEntityPersister persister, Status status, CancellationToken cancellationToken)
 {
     cancellationToken.ThrowIfCancellationRequested();
     if (IsCollectionDirtyCheckNecessary(persister, status))
     {
         DirtyCollectionSearchVisitor visitor = new DirtyCollectionSearchVisitor(@event.Session, persister.PropertyVersionability);
         await(visitor.ProcessEntityPropertyValuesAsync(@event.PropertyValues, persister.PropertyTypes, cancellationToken)).ConfigureAwait(false);
         bool hasDirtyCollections = visitor.WasDirtyCollectionFound;
         @event.HasDirtyCollection = hasDirtyCollections;
         return(hasDirtyCollections);
     }
     else
     {
         return(false);
     }
 }
예제 #20
0
        private bool HasDirtyProperties(FlushEntityEvent @event)
        {
            ISessionImplementor session = @event.Session;
            EntityEntry         entry   = @event.EntityEntry;
            var entity = @event.Entity;

            if (!entry.RequiresDirtyCheck(entity) || !entry.ExistsInDatabase || entry.LoadedState == null)
            {
                return(false);
            }
            IEntityPersister persister = entry.Persister;

            object[] currentState = persister.GetPropertyValues(entity, session.EntityMode);;
            object[] loadedState  = entry.LoadedState;

            return(persister.EntityMetamodel.Properties
                   .Where((property, i) => !LazyPropertyInitializer.UnfetchedProperty.Equals(currentState[i]) && property.Type.IsDirty(loadedState[i], currentState[i], session))
                   .Any());
        }
예제 #21
0
        public void OnFlushEntity(FlushEntityEvent @event)
        {
            if ((@event.EntityEntry.Status == Status.Deleted) || (@event.EntityEntry.Status == Status.ReadOnly))
            {
                return;
            }

            IAuditable trackable = @event.Entity as IAuditable;

            if (trackable == null)
            {
                return;
            }

            if (this.HasDirtyProperties(@event) == true)
            {
                this.ExplicitUpdateCall(trackable);
            }
        }
예제 #22
0
        public void OnFlushEntity(FlushEntityEvent @event)
        {
            var entity      = @event.Entity;
            var entityEntry = @event.EntityEntry;

            if (entityEntry.Status == Status.Deleted)
            {
                return;
            }
            var trackable = entity as ITrackModificationDate;

            if (trackable == null)
            {
                return;
            }
            if (HasDirtyProperties(@event))
            {
                trackable.LastModified = CurrentDateTimeProvider();
            }
        }
예제 #23
0
    public void OnFlushEntity(FlushEntityEvent @event)
    {
        var entity      = @event.Entity;
        var entityEntry = @event.EntityEntry;

        if (entityEntry.Status == Status.Deleted)
        {
            //raise event
        }
        var trackable = entity as ITrackModificationDate;

        if (trackable == null)
        {
            return;
        }
        if (HasDirtyProperties(@event))
        {
            //raise event
        }
    }
예제 #24
0
        /// <summary>
        /// Flushes a single entity's state to the database, by scheduling an update action, if necessary
        /// </summary>
        public virtual void OnFlushEntity(FlushEntityEvent @event)
        {
            object           entity     = @event.Entity;
            EntityEntry      entry      = @event.EntityEntry;
            IEventSource     session    = @event.Session;
            IEntityPersister persister  = entry.Persister;
            Status           status     = entry.Status;
            EntityMode       entityMode = session.EntityMode;

            IType[] types = persister.PropertyTypes;

            bool mightBeDirty = entry.RequiresDirtyCheck(entity);

            object[] values = GetValues(entity, entry, entityMode, mightBeDirty, session);

            @event.PropertyValues = values;

            //TODO: avoid this for non-new instances where mightBeDirty==false
            bool substitute = WrapCollections(session, persister, types, values);

            if (IsUpdateNecessary(@event, mightBeDirty))
            {
                substitute = ScheduleUpdate(@event) || substitute;
            }

            if (status != Status.Deleted)
            {
                // now update the object .. has to be outside the main if block above (because of collections)
                if (substitute)
                {
                    persister.SetPropertyValues(entity, values, entityMode);
                }

                // Search for collections by reachability, updating their role.
                // We don't want to touch collections reachable from a deleted object
                if (persister.HasCollections)
                {
                    new FlushVisitor(session, entity).ProcessEntityPropertyValues(values, types);
                }
            }
        }
예제 #25
0
        /// <summary>
        /// Flushes a single entity's state to the database, by scheduling an update action, if necessary
        /// </summary>
        public virtual async Task OnFlushEntityAsync(FlushEntityEvent @event, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            object           entity    = @event.Entity;
            EntityEntry      entry     = @event.EntityEntry;
            IEventSource     session   = @event.Session;
            IEntityPersister persister = entry.Persister;
            Status           status    = entry.Status;

            IType[] types = persister.PropertyTypes;

            bool mightBeDirty = entry.RequiresDirtyCheck(entity);

            object[] values = await(GetValuesAsync(entity, entry, mightBeDirty, session, cancellationToken)).ConfigureAwait(false);

            @event.PropertyValues = values;

            //TODO: avoid this for non-new instances where mightBeDirty==false
            bool substitute = await(WrapCollectionsAsync(session, persister, types, values, cancellationToken)).ConfigureAwait(false);

            if (await(IsUpdateNecessaryAsync(@event, mightBeDirty, cancellationToken)).ConfigureAwait(false))
            {
                substitute = await(ScheduleUpdateAsync(@event, cancellationToken)).ConfigureAwait(false) || substitute;
            }

            if (status != Status.Deleted)
            {
                // now update the object .. has to be outside the main if block above (because of collections)
                if (substitute)
                {
                    persister.SetPropertyValues(entity, values);
                }

                // Search for collections by reachability, updating their role.
                // We don't want to touch collections reachable from a deleted object
                if (persister.HasCollections)
                {
                    await(new FlushVisitor(session, entity).ProcessEntityPropertyValuesAsync(values, types, cancellationToken)).ConfigureAwait(false);
                }
            }
        }
예제 #26
0
        /// <summary>
        /// Performs all necessary checking to determine if an entity needs an SQL update
        /// to synchronize its state to the database. Modifies the event by side-effect!
        /// Note: this method is quite slow, avoid calling if possible!
        /// </summary>
        protected bool IsUpdateNecessary(FlushEntityEvent @event)
        {
            IEntityPersister persister = @event.EntityEntry.Persister;
            Status           status    = @event.EntityEntry.Status;

            if ([email protected])
            {
                return(true);
            }
            else
            {
                int[] dirtyProperties = @event.DirtyProperties;
                if (dirtyProperties != null && dirtyProperties.Length != 0)
                {
                    return(true);                    //TODO: suck into event class
                }
                else
                {
                    return(HasDirtyCollections(@event, persister, status));
                }
            }
        }
		/// <summary>
		/// Flushes a single entity's state to the database, by scheduling an update action, if necessary
		/// </summary>
		public virtual void OnFlushEntity(FlushEntityEvent @event)
		{
			object entity = @event.Entity;
			EntityEntry entry = @event.EntityEntry;
			IEventSource session = @event.Session;
			IEntityPersister persister = entry.Persister;
			Status status = entry.Status;
			EntityMode entityMode = session.EntityMode;
			IType[] types = persister.PropertyTypes;

			bool mightBeDirty = entry.RequiresDirtyCheck(entity);

			object[] values = GetValues(entity, entry, entityMode, mightBeDirty, session);

			@event.PropertyValues = values;

			//TODO: avoid this for non-new instances where mightBeDirty==false
			bool substitute = WrapCollections(session, persister, types, values);

			if (IsUpdateNecessary(@event, mightBeDirty))
			{
				substitute = ScheduleUpdate(@event) || substitute;
			}

			if (status != Status.Deleted)
			{
				// now update the object .. has to be outside the main if block above (because of collections)
				if (substitute)
					persister.SetPropertyValues(entity, values, entityMode);

				// Search for collections by reachability, updating their role.
				// We don't want to touch collections reachable from a deleted object
				if (persister.HasCollections)
				{
					new FlushVisitor(session, entity).ProcessEntityPropertyValues(values, types);
				}
			}
		}
예제 #28
0
        private bool IsUpdateNecessary(FlushEntityEvent @event, bool mightBeDirty)
        {
            Status status = @event.EntityEntry.Status;

            if (mightBeDirty || status == Status.Deleted)
            {
                // compare to cached state (ignoring collections unless versioned)
                DirtyCheck(@event);
                if (IsUpdateNecessary(@event))
                {
                    return(true);
                }
                else
                {
                    // TODO H3.2 Different behaviour
                    //FieldInterceptionHelper.clearDirty(@event.Entity);
                    return(false);
                }
            }
            else
            {
                return(HasDirtyCollections(@event, @event.EntityEntry.Persister, status));
            }
        }
		/// <summary>
		/// Performs all necessary checking to determine if an entity needs an SQL update
		/// to synchronize its state to the database. Modifies the event by side-effect!
		/// Note: this method is quite slow, avoid calling if possible!
		/// </summary>
		protected bool IsUpdateNecessary(FlushEntityEvent @event)
		{
			IEntityPersister persister = @event.EntityEntry.Persister;
			Status status = @event.EntityEntry.Status;

			if ([email protected])
			{
				return true;
			}
			else
			{

				int[] dirtyProperties = @event.DirtyProperties;
				if (dirtyProperties != null && dirtyProperties.Length != 0)
				{
					return true; //TODO: suck into event class
				}
				else
				{
					return HasDirtyCollections(@event, persister, status);
				}
			}
		}
		// 1. detect any dirty entities
		// 2. schedule any entity updates
		// 3. search out any reachable collections
		private void FlushEntities(FlushEvent @event)
		{
			log.Debug("Flushing entities and processing referenced collections");

			// Among other things, updateReachables() will recursively load all
			// collections that are moving roles. This might cause entities to
			// be loaded.

			// So this needs to be safe from concurrent modification problems.
			// It is safe because of how IdentityMap implements entrySet()
			IEventSource source = @event.Session;

			ICollection list = IdentityMap.ConcurrentEntries(source.PersistenceContext.EntityEntries);
			foreach (DictionaryEntry me in list)
			{
				// Update the status of the object and if necessary, schedule an update
				EntityEntry entry = (EntityEntry)me.Value;
				Status status = entry.Status;

				if (status != Status.Loading && status != Status.Gone)
				{
					FlushEntityEvent entityEvent = new FlushEntityEvent(source, me.Key, entry);
					IFlushEntityEventListener[] listeners = source.Listeners.FlushEntityEventListeners;
					foreach (IFlushEntityEventListener listener in listeners)
						listener.OnFlushEntity(entityEvent);
				}
			}
			source.ActionQueue.SortActions();
		}
		private bool IsUpdateNecessary(FlushEntityEvent @event, bool mightBeDirty)
		{
			Status status = @event.EntityEntry.Status;
			if (mightBeDirty || status == Status.Deleted)
			{
				// compare to cached state (ignoring collections unless versioned)
				DirtyCheck(@event);
				if (IsUpdateNecessary(@event))
				{
					return true;
				}
				else
				{
					// TODO H3.2 Different behaviour
					//FieldInterceptionHelper.clearDirty(@event.Entity);
					return false;
				}
			}
			else
			{
				return HasDirtyCollections(@event, @event.EntityEntry.Persister, status);
			}
		}
		private bool ScheduleUpdate(FlushEntityEvent @event)
		{
			EntityEntry entry = @event.EntityEntry;
			IEventSource session = @event.Session;
			EntityMode entityMode = session.EntityMode;
			object entity = @event.Entity;
			Status status = entry.Status;
			IEntityPersister persister = entry.Persister;
			object[] values = @event.PropertyValues;

			if (log.IsDebugEnabled)
			{
				if (status == Status.Deleted)
				{
					log.Debug("Updating deleted entity: " + MessageHelper.InfoString(persister, entry.Id, session.Factory));
				}
				else
				{
					log.Debug("Updating entity: " + MessageHelper.InfoString(persister, entry.Id, session.Factory));
				}
			}

			bool intercepted;

			if (!entry.IsBeingReplicated)
			{
				// give the Interceptor a chance to process property values, if the properties
				// were modified by the Interceptor, we need to set them back to the object
				intercepted = HandleInterception(@event);
			}
			else
			{
				intercepted = false;
			}

			Validate(entity, persister, status, entityMode);

			// increment the version number (if necessary)
			object nextVersion = GetNextVersion(@event);

			// if it was dirtied by a collection only
			int[] dirtyProperties = @event.DirtyProperties;
			if (@event.DirtyCheckPossible && dirtyProperties == null)
			{
				if (!intercepted && [email protected])
				{
					throw new AssertionFailure("dirty, but no dirty properties");
				}
				dirtyProperties = ArrayHelper.EmptyIntArray;
			}

			// check nullability but do not perform command execute
			// we'll use scheduled updates for that.
			new Nullability(session).CheckNullability(values, persister, true);

			// schedule the update
			// note that we intentionally do _not_ pass in currentPersistentState!
			session.ActionQueue.AddAction(
				new EntityUpdateAction(entry.Id, values, dirtyProperties,
				@event.HasDirtyCollection, entry.LoadedState, entry.Version,
				nextVersion, entity, persister, session));

			return intercepted;
		}
예제 #33
0
        private bool ScheduleUpdate(FlushEntityEvent @event)
        {
            EntityEntry      entry     = @event.EntityEntry;
            IEventSource     session   = @event.Session;
            object           entity    = @event.Entity;
            Status           status    = entry.Status;
            IEntityPersister persister = entry.Persister;

            object[] values = @event.PropertyValues;

            if (log.IsDebugEnabled)
            {
                if (status == Status.Deleted)
                {
                    if (!persister.IsMutable)
                    {
                        log.Debug("Updating immutable, deleted entity: " + MessageHelper.InfoString(persister, entry.Id, session.Factory));
                    }
                    else if (!entry.IsModifiableEntity())
                    {
                        log.Debug("Updating non-modifiable, deleted entity: " + MessageHelper.InfoString(persister, entry.Id, session.Factory));
                    }
                    else
                    {
                        log.Debug("Updating deleted entity: " + MessageHelper.InfoString(persister, entry.Id, session.Factory));
                    }
                }
                else
                {
                    log.Debug("Updating entity: " + MessageHelper.InfoString(persister, entry.Id, session.Factory));
                }
            }

            bool intercepted;

            if (!entry.IsBeingReplicated)
            {
                // give the Interceptor a chance to process property values, if the properties
                // were modified by the Interceptor, we need to set them back to the object
                intercepted = HandleInterception(@event);
            }
            else
            {
                intercepted = false;
            }

            Validate(entity, persister, status);

            // increment the version number (if necessary)
            object nextVersion = GetNextVersion(@event);

            // if it was dirtied by a collection only
            int[] dirtyProperties = @event.DirtyProperties;
            if (@event.DirtyCheckPossible && dirtyProperties == null)
            {
                if (!intercepted && [email protected])
                {
                    throw new AssertionFailure("dirty, but no dirty properties");
                }
                dirtyProperties = ArrayHelper.EmptyIntArray;
            }

            // check nullability but do not perform command execute
            // we'll use scheduled updates for that.
            new Nullability(session).CheckNullability(values, persister, true);

            // schedule the update
            // note that we intentionally do _not_ pass in currentPersistentState!
            session.ActionQueue.AddAction(
                new EntityUpdateAction(
                    entry.Id,
                    values,
                    dirtyProperties,
                    @event.HasDirtyCollection,
                    status == Status.Deleted && !entry.IsModifiableEntity() ? persister.GetPropertyValues(entity) : entry.LoadedState,
                    entry.Version,
                    nextVersion,
                    entity,
                    persister,
                    session));

            return(intercepted);
        }
		/// <summary> Perform a dirty check, and attach the results to the event</summary>
		protected virtual void DirtyCheck(FlushEntityEvent @event)
		{
			object entity = @event.Entity;
			object[] values = @event.PropertyValues;
			ISessionImplementor session = @event.Session;
			EntityEntry entry = @event.EntityEntry;
			IEntityPersister persister = entry.Persister;
			object id = entry.Id;
			object[] loadedState = entry.LoadedState;

			int[] dirtyProperties = session.Interceptor.FindDirty(entity, id, values, loadedState, persister.PropertyNames, persister.PropertyTypes);

			@event.DatabaseSnapshot = null;

			bool interceptorHandledDirtyCheck;
			bool cannotDirtyCheck;

			if (dirtyProperties == null)
			{
				// Interceptor returned null, so do the dirtycheck ourself, if possible
				interceptorHandledDirtyCheck = false;

				cannotDirtyCheck = loadedState == null; // object loaded by update()
				if (!cannotDirtyCheck)
				{
					// dirty check against the usual snapshot of the entity
					dirtyProperties = persister.FindDirty(values, loadedState, entity, session);
				}
				else if (entry.Status == Status.Deleted && [email protected]())
				{
					// A non-modifiable (e.g., read-only or immutable) entity needs to be have
					// references to transient entities set to null before being deleted. No other
					// fields should be updated.
					if (values != entry.DeletedState ) 
					{
						throw new InvalidOperationException("Entity has status Status.Deleted but values != entry.DeletedState");
					}
					// Even if loadedState == null, we can dirty-check by comparing currentState and
					// entry.getDeletedState() because the only fields to be updated are those that
					// refer to transient entities that are being set to null.
					// - currentState contains the entity's current property values.
					// - entry.getDeletedState() contains the entity's current property values with
					//   references to transient entities set to null.
					// - dirtyProperties will only contain properties that refer to transient entities
					object[] currentState = persister.GetPropertyValues(@event.Entity, @event.Session.EntityMode);
					dirtyProperties = persister.FindDirty(entry.DeletedState, currentState, entity, session);
					cannotDirtyCheck = false;
				}
				else
				{
					// dirty check against the database snapshot, if possible/necessary
					object[] databaseSnapshot = GetDatabaseSnapshot(session, persister, id);
					if (databaseSnapshot != null)
					{
						dirtyProperties = persister.FindModified(databaseSnapshot, values, entity, session);
						cannotDirtyCheck = false;
						@event.DatabaseSnapshot = databaseSnapshot;
					}
				}
			}
			else
			{
				// the Interceptor handled the dirty checking
				cannotDirtyCheck = false;
				interceptorHandledDirtyCheck = true;
			}

			@event.DirtyProperties = dirtyProperties;
			@event.DirtyCheckHandledByInterceptor = interceptorHandledDirtyCheck;
			@event.DirtyCheckPossible = !cannotDirtyCheck;
		}
		/// <summary> Perform a dirty check, and attach the results to the event</summary>
		protected virtual void DirtyCheck(FlushEntityEvent @event)
		{
			object entity = @event.Entity;
			object[] values = @event.PropertyValues;
			ISessionImplementor session = @event.Session;
			EntityEntry entry = @event.EntityEntry;
			IEntityPersister persister = entry.Persister;
			object id = entry.Id;
			object[] loadedState = entry.LoadedState;

			int[] dirtyProperties = session.Interceptor.FindDirty(entity, id, values, loadedState, persister.PropertyNames, persister.PropertyTypes);

			@event.DatabaseSnapshot = null;

			bool interceptorHandledDirtyCheck;
			bool cannotDirtyCheck;

			if (dirtyProperties == null)
			{
				// Interceptor returned null, so do the dirtycheck ourself, if possible
				interceptorHandledDirtyCheck = false;

				cannotDirtyCheck = loadedState == null; // object loaded by update()
				if (!cannotDirtyCheck)
				{
					// dirty check against the usual snapshot of the entity
					dirtyProperties = persister.FindDirty(values, loadedState, entity, session);
				}
				else
				{
					// dirty check against the database snapshot, if possible/necessary
					object[] databaseSnapshot = GetDatabaseSnapshot(session, persister, id);
					if (databaseSnapshot != null)
					{
						dirtyProperties = persister.FindModified(databaseSnapshot, values, entity, session);
						cannotDirtyCheck = false;
						@event.DatabaseSnapshot = databaseSnapshot;
					}
				}
			}
			else
			{
				// the Interceptor handled the dirty checking
				cannotDirtyCheck = false;
				interceptorHandledDirtyCheck = true;
			}

			@event.DirtyProperties = dirtyProperties;
			@event.DirtyCheckHandledByInterceptor = interceptorHandledDirtyCheck;
			@event.DirtyCheckPossible = !cannotDirtyCheck;
		}
		private bool HasDirtyCollections(FlushEntityEvent @event, IEntityPersister persister, Status status)
		{
			if (IsCollectionDirtyCheckNecessary(persister, status))
			{
				DirtyCollectionSearchVisitor visitor = new DirtyCollectionSearchVisitor(@event.Session, persister.PropertyVersionability);
				visitor.ProcessEntityPropertyValues(@event.PropertyValues, persister.PropertyTypes);
				bool hasDirtyCollections = visitor.WasDirtyCollectionFound;
				@event.HasDirtyCollection = hasDirtyCollections;
				return hasDirtyCollections;
			}
			else
			{
				return false;
			}
		}
		private bool IsVersionIncrementRequired(FlushEntityEvent @event, EntityEntry entry, IEntityPersister persister, int[] dirtyProperties)
		{
			bool isVersionIncrementRequired = 
				entry.Status != Status.Deleted && 
				(dirtyProperties == null || 
					Versioning.IsVersionIncrementRequired(dirtyProperties, @event.HasDirtyCollection, persister.PropertyVersionability));
			return isVersionIncrementRequired;
		}
		private bool IsVersionIncrementRequired(FlushEntityEvent @event, EntityEntry entry, IEntityPersister persister, int[] dirtyProperties)
		{
			// NH different behavior: because NH-1756 when PostInsertId is used with a generated version
			// the version is read inmediately after save and does not need to be incremented.
			// BTW, in general, a generated version does not need to be incremented by NH.

			bool isVersionIncrementRequired =
				entry.Status != Status.Deleted && !persister.IsVersionPropertyGenerated &&
				(dirtyProperties == null ||
					Versioning.IsVersionIncrementRequired(dirtyProperties, @event.HasDirtyCollection, persister.PropertyVersionability));
			return isVersionIncrementRequired;
		}
		private object GetNextVersion(FlushEntityEvent @event)
		{
			// Convience method to retrieve an entities next version value
			EntityEntry entry = @event.EntityEntry;
			IEntityPersister persister = entry.Persister;
			if (persister.IsVersioned)
			{
				object[] values = @event.PropertyValues;

				if (entry.IsBeingReplicated)
				{
					return Versioning.GetVersion(values, persister);
				}
				else
				{
					int[] dirtyProperties = @event.DirtyProperties;

					bool isVersionIncrementRequired = IsVersionIncrementRequired(@event, entry, persister, dirtyProperties);

					object nextVersion = isVersionIncrementRequired ?
						Versioning.Increment(entry.Version, persister.VersionType, @event.Session) :
						entry.Version; //use the current version

					Versioning.SetVersion(values, nextVersion, persister);

					return nextVersion;
				}
			}
			else
			{
				return null;
			}
		}
		protected virtual bool HandleInterception(FlushEntityEvent @event)
		{
			ISessionImplementor session = @event.Session;
			EntityEntry entry = @event.EntityEntry;
			IEntityPersister persister = entry.Persister;
			object entity = @event.Entity;

			//give the Interceptor a chance to modify property values
			object[] values = @event.PropertyValues;
			bool intercepted = InvokeInterceptor(session, entity, entry, values, persister);

			//now we might need to recalculate the dirtyProperties array
			if (intercepted && @event.DirtyCheckPossible && [email protected])
			{
				int[] dirtyProperties;
				if (@event.HasDatabaseSnapshot)
				{
					dirtyProperties = persister.FindModified(@event.DatabaseSnapshot, values, entity, session);
				}
				else
				{
					dirtyProperties = persister.FindDirty(values, entry.LoadedState, entity, session);
				}
				@event.DirtyProperties = dirtyProperties;
			}

			return intercepted;
		}
예제 #41
0
        /// <summary> Perform a dirty check, and attach the results to the event</summary>
        protected virtual void DirtyCheck(FlushEntityEvent @event)
        {
            object entity = @event.Entity;

            object[]            values    = @event.PropertyValues;
            ISessionImplementor session   = @event.Session;
            EntityEntry         entry     = @event.EntityEntry;
            IEntityPersister    persister = entry.Persister;
            object id = entry.Id;

            object[] loadedState = entry.LoadedState;

            int[] dirtyProperties = session.Interceptor.FindDirty(entity, id, values, loadedState, persister.PropertyNames, persister.PropertyTypes);

            @event.DatabaseSnapshot = null;

            bool interceptorHandledDirtyCheck;
            bool cannotDirtyCheck;

            if (dirtyProperties == null)
            {
                // Interceptor returned null, so do the dirtycheck ourself, if possible
                interceptorHandledDirtyCheck = false;

                cannotDirtyCheck = loadedState == null;                 // object loaded by update()
                if (!cannotDirtyCheck)
                {
                    // dirty check against the usual snapshot of the entity
                    dirtyProperties = persister.FindDirty(values, loadedState, entity, session);
                }
                else if (entry.Status == Status.Deleted && [email protected]())
                {
                    // A non-modifiable (e.g., read-only or immutable) entity needs to be have
                    // references to transient entities set to null before being deleted. No other
                    // fields should be updated.
                    if (values != entry.DeletedState)
                    {
                        throw new InvalidOperationException("Entity has status Status.Deleted but values != entry.DeletedState");
                    }
                    // Even if loadedState == null, we can dirty-check by comparing currentState and
                    // entry.getDeletedState() because the only fields to be updated are those that
                    // refer to transient entities that are being set to null.
                    // - currentState contains the entity's current property values.
                    // - entry.getDeletedState() contains the entity's current property values with
                    //   references to transient entities set to null.
                    // - dirtyProperties will only contain properties that refer to transient entities
                    object[] currentState = persister.GetPropertyValues(@event.Entity);
                    dirtyProperties  = persister.FindDirty(entry.DeletedState, currentState, entity, session);
                    cannotDirtyCheck = false;
                }
                else
                {
                    // dirty check against the database snapshot, if possible/necessary
                    object[] databaseSnapshot = GetDatabaseSnapshot(session, persister, id);
                    if (databaseSnapshot != null)
                    {
                        dirtyProperties         = persister.FindModified(databaseSnapshot, values, entity, session);
                        cannotDirtyCheck        = false;
                        @event.DatabaseSnapshot = databaseSnapshot;
                    }
                }
            }
            else
            {
                // the Interceptor handled the dirty checking
                cannotDirtyCheck             = false;
                interceptorHandledDirtyCheck = true;
            }

            @event.DirtyProperties = dirtyProperties;
            @event.DirtyCheckHandledByInterceptor = interceptorHandledDirtyCheck;
            @event.DirtyCheckPossible             = !cannotDirtyCheck;
        }
예제 #42
0
 public void OnFlushEntity(FlushEntityEvent @event)
 {
     log.Debug("OnFlushEntity :" + @event);
 }