/// <summary> /// Traverses the nodes in a <see cref="NodeLayer"/>. /// </summary> private void Traverse(NodeLayer currentLayer, ResourceHook target, Action <IResourceHookContainer, INode> action) { if (!currentLayer.AnyEntities()) { return; } foreach (INode node in currentLayer) { var entityType = node.ResourceType; var hookContainer = _executorHelper.GetResourceHookContainer(entityType, target); if (hookContainer == null) { continue; } action(hookContainer, node); } Traverse(_traversalHelper.CreateNextLayer(currentLayer.ToList()), target, action); }
/// <summary> /// Fires the nested before hooks for entities in the current <paramref name="layer"/> /// </summary> /// <remarks> /// For example: consider the case when the owner of article1 (one-to-one) /// is being updated from owner_old to owner_new, where owner_new is currently already /// related to article2. Then, the following nested hooks need to be fired in the following order. /// First the BeforeUpdateRelationship should be for owner1, then the /// BeforeImplicitUpdateRelationship hook should be fired for /// owner2, and lastly the BeforeImplicitUpdateRelationship for article2.</remarks> private void FireNestedBeforeUpdateHooks(ResourcePipeline pipeline, NodeLayer layer) { foreach (INode node in layer) { var nestedHookContainer = _executorHelper.GetResourceHookContainer(node.ResourceType, ResourceHook.BeforeUpdateRelationship); IEnumerable uniqueEntities = node.UniqueEntities; RightType entityType = node.ResourceType; Dictionary <RelationshipAttribute, IEnumerable> currentEntitiesGrouped; Dictionary <RelationshipAttribute, IEnumerable> currentEntitiesGroupedInverse; // fire the BeforeUpdateRelationship hook for owner_new if (nestedHookContainer != null) { if (uniqueEntities.Cast <IIdentifiable>().Any()) { var relationships = node.RelationshipsToNextLayer.Select(p => p.Attribute).ToArray(); var dbValues = LoadDbValues(entityType, uniqueEntities, ResourceHook.BeforeUpdateRelationship, relationships); // these are the entities of the current node grouped by // RelationshipAttributes that occured in the previous layer // so it looks like { HasOneAttribute:owner => owner_new }. // Note that in the BeforeUpdateRelationship hook of Person, // we want want inverse relationship attribute: // we now have the one pointing from article -> person, ] // but we require the the one that points from person -> article currentEntitiesGrouped = node.RelationshipsFromPreviousLayer.GetRightEntities(); currentEntitiesGroupedInverse = ReplaceKeysWithInverseRelationships(currentEntitiesGrouped); var resourcesByRelationship = CreateRelationshipHelper(entityType, currentEntitiesGroupedInverse, dbValues); var allowedIds = CallHook(nestedHookContainer, ResourceHook.BeforeUpdateRelationship, new object[] { GetIds(uniqueEntities), resourcesByRelationship, pipeline }).Cast <string>(); var updated = GetAllowedEntities(uniqueEntities, allowedIds); node.UpdateUnique(updated); node.Reassign(); } } // Fire the BeforeImplicitUpdateRelationship hook for owner_old. // Note: if the pipeline is Post it means we just created article1, // which means we are sure that it isn't related to any other entities yet. if (pipeline != ResourcePipeline.Post) { // To fire a hook for owner_old, we need to first get a reference to it. // For this, we need to query the database for the HasOneAttribute:owner // relationship of article1, which is referred to as the // left side of the HasOneAttribute:owner relationship. var leftEntities = node.RelationshipsFromPreviousLayer.GetLeftEntities(); if (leftEntities.Any()) { // owner_old is loaded, which is an "implicitly affected entity" FireForAffectedImplicits(entityType, leftEntities, pipeline, uniqueEntities); } } // Fire the BeforeImplicitUpdateRelationship hook for article2 // For this, we need to query the database for the current owner // relationship value of owner_new. currentEntitiesGrouped = node.RelationshipsFromPreviousLayer.GetRightEntities(); if (currentEntitiesGrouped.Any()) { // rightEntities is grouped by relationships from previous // layer, ie { HasOneAttribute:owner => owner_new }. But // to load article2 onto owner_new, we need to have the // RelationshipAttribute from owner to article, which is the // inverse of HasOneAttribute:owner currentEntitiesGroupedInverse = ReplaceKeysWithInverseRelationships(currentEntitiesGrouped); // Note that currently in the JADNC implementation of hooks, // the root layer is ALWAYS homogenous, so we safely assume // that for every relationship to the previous layer, the // left type is the same. LeftType leftType = currentEntitiesGrouped.First().Key.LeftType; FireForAffectedImplicits(leftType, currentEntitiesGroupedInverse, pipeline); } } }