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 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); }
/// <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)); } }
// 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 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)); } }
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); } }
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); }
static IEnumerable <int> GetAdditionalDirtyProperties(FlushEntityEvent @event) { yield return(Array.IndexOf(@event.EntityEntry.Persister.PropertyNames, "UpdatedAt")); yield return(Array.IndexOf(@event.EntityEntry.Persister.PropertyNames, "UpdatedBy")); }
public void OnFlushEntity(FlushEntityEvent @event) { var validator = @event.Session.SessionFactory.GetValidator(); if (validator != null) { validator.Validate(@event.Entity); } }
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); } } }
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(); } }
//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(); } }
/// <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 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); }
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()); }
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); }
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); } }
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()); }
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); } }
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(); } }
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 } }
/// <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); } } }
/// <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); } } }
/// <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); } } }
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; }
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; }
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; }
/// <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; }
public void OnFlushEntity(FlushEntityEvent @event) { log.Debug("OnFlushEntity :" + @event); }