/// <summary> Cascade an action to the child or children</summary> private Task CascadePropertyAsync(object parent, object child, IType type, CascadeStyle style, string propertyName, object anything, bool isCascadeDeleteEnabled, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { return(Task.FromCanceled <object>(cancellationToken)); } try { if (child != null) { if (type.IsAssociationType) { IAssociationType associationType = (IAssociationType)type; if (CascadeAssociationNow(associationType)) { return(CascadeAssociationAsync(parent, child, type, style, anything, isCascadeDeleteEnabled, cancellationToken)); } } else if (type.IsComponentType) { return(CascadeComponentAsync(parent, child, (IAbstractComponentType)type, propertyName, anything, cancellationToken)); } } else { // potentially we need to handle orphan deletes for one-to-ones here... if (type.IsEntityType && ((EntityType)type).IsLogicalOneToOne()) { // We have a physical or logical one-to-one and from previous checks we know we // have a null value. See if the attribute cascade settings and action-type require // orphan checking if (style.HasOrphanDelete && action.DeleteOrphans) { // value is orphaned if loaded state for this property shows not null // because it is currently null. EntityEntry entry = eventSource.PersistenceContext.GetEntry(parent); if (entry != null && entry.Status != Status.Saving) { EntityType entityType = (EntityType)type; object loadedValue; if (!componentPathStack.Any()) { // association defined on entity loadedValue = entry.GetLoadedValue(propertyName); } else { // association defined on component // todo : this is currently unsupported because of the fact that // we do not know the loaded state of this value properly // and doing so would be very difficult given how components and // entities are loaded (and how 'loaded state' is put into the // EntityEntry). Solutions here are to either: // 1) properly account for components as a 2-phase load construct // 2) just assume the association was just now orphaned and // issue the orphan delete. This would require a special // set of SQL statements though since we do not know the // orphaned value, something a delete with a subquery to // match the owner. loadedValue = null; } if (loadedValue != null) { return(eventSource.DeleteAsync(entry.Persister.EntityName, loadedValue, false, null, cancellationToken)); } } } } } return(Task.CompletedTask); } catch (System.Exception ex) { return(Task.FromException <object>(ex)); } }
/// <summary> Cascade an action to the child or children</summary> private void CascadeProperty(object parent, object child, IType type, CascadeStyle style, string propertyName, object anything, bool isCascadeDeleteEnabled) { if (child != null) { if (type.IsAssociationType) { IAssociationType associationType = (IAssociationType)type; if (CascadeAssociationNow(associationType)) { CascadeAssociation(parent, child, type, style, anything, isCascadeDeleteEnabled); } } else if (type.IsComponentType) { CascadeComponent(parent, child, (IAbstractComponentType)type, propertyName, anything); } } else { // potentially we need to handle orphan deletes for one-to-ones here... if (type.IsEntityType && ((EntityType)type).IsLogicalOneToOne()) { // We have a physical or logical one-to-one and from previous checks we know we // have a null value. See if the attribute cascade settings and action-type require // orphan checking if (style.HasOrphanDelete && action.DeleteOrphans) { // value is orphaned if loaded state for this property shows not null // because it is currently null. EntityEntry entry = eventSource.PersistenceContext.GetEntry(parent); if (entry != null && entry.Status != Status.Saving) { object loadedValue; if (componentPathStack.Count == 0) { // association defined on entity loadedValue = entry.GetLoadedValue(propertyName); // Check this is not a null carrying proxy. The no-proxy load is currently handled by // putting a proxy (!) flagged for unwrapping (even for non-constrained one-to-one, // which association may be null) in the loadedState of the parent. The unwrap flag // causes it to support having a null implementation, instead of throwing an entity // not found error. loadedValue = eventSource.PersistenceContext.UnproxyAndReassociate(loadedValue); } else { // association defined on component // todo : this is currently unsupported because of the fact that // we do not know the loaded state of this value properly // and doing so would be very difficult given how components and // entities are loaded (and how 'loaded state' is put into the // EntityEntry). Solutions here are to either: // 1) properly account for components as a 2-phase load construct // 2) just assume the association was just now orphaned and // issue the orphan delete. This would require a special // set of SQL statements though since we do not know the // orphaned value, something a delete with a subquery to // match the owner. loadedValue = null; } if (loadedValue != null) { eventSource.Delete(entry.Persister.EntityName, loadedValue, false, null); } } } } } }