protected virtual void OnCreateDynamicUpdateMap(NativeActivityUpdateMapMetadata metadata, Activity originalActivity) { // default UpdateMapMetadata.AllowUpdateInsideThisActivity is TRUE if (!metadata.IsUpdateExplicitlyAllowedOrDisallowed && !DoPublicChildrenMatch(metadata, this, originalActivity)) { metadata.DisallowUpdateInsideThisActivity(SR.PublicChildrenChangeBlockDU); } }
protected override void OnCreateDynamicUpdateMap(NativeActivityUpdateMapMetadata metadata, Activity originalActivity) { PickBranchBody originalBranchBody = (PickBranchBody)originalActivity; if ((originalBranchBody.Action != null && metadata.GetMatch(this.Trigger) == originalBranchBody.Action) || (this.Action != null && metadata.GetMatch(this.Action) == originalBranchBody.Trigger)) { metadata.DisallowUpdateInsideThisActivity(SR.PickBranchTriggerActionSwapped); return; } metadata.AllowUpdateInsideThisActivity(); }
protected override void OnCreateDynamicUpdateMap(NativeActivityUpdateMapMetadata metadata, Activity originalActivity) { Flowchart originalFlowchart = (Flowchart)originalActivity; Dictionary <Activity, int> originalActivities = new Dictionary <Activity, int>(); foreach (FlowNode node in originalFlowchart.reachableNodes) { if (node.ChildActivity == null) { continue; } if (metadata.IsReferenceToImportedChild(node.ChildActivity)) { // We can't save original values for referenced children. Also, we can't // reliably combine implementation changes with changes to referenced children. // For now, we just disable this scenario altogether; if we want to support it, // we'll need deeper runtime support. metadata.DisallowUpdateInsideThisActivity(SR.FlowchartContainsReferences); return; } if (originalActivities.ContainsKey(node.ChildActivity)) { metadata.DisallowUpdateInsideThisActivity(SR.MultipleFlowNodesSharingSameChildBlockDU); return; } originalActivities[node.ChildActivity] = node.Index; } HashSet <Activity> updatedActivities = new HashSet <Activity>(); foreach (FlowNode node in this.reachableNodes) { if (node.ChildActivity != null) { if (metadata.IsReferenceToImportedChild(node.ChildActivity)) { metadata.DisallowUpdateInsideThisActivity(SR.FlowchartContainsReferences); return; } if (updatedActivities.Contains(node.ChildActivity)) { metadata.DisallowUpdateInsideThisActivity(SR.MultipleFlowNodesSharingSameChildBlockDU); return; } else { updatedActivities.Add(node.ChildActivity); } Activity originalChild = metadata.GetMatch(node.ChildActivity); int originalIndex; if (originalChild != null && originalActivities.TryGetValue(originalChild, out originalIndex)) { if (originalFlowchart.reachableNodes[originalIndex].GetType() != node.GetType()) { metadata.DisallowUpdateInsideThisActivity(SR.CannotMoveChildAcrossDifferentFlowNodeTypes); return; } if (originalIndex != node.Index) { metadata.SaveOriginalValue(node.ChildActivity, originalIndex); } } } } }
protected override void OnCreateDynamicUpdateMap(NativeActivityUpdateMapMetadata metadata, Activity originalActivity) { InternalState originalInternalState = (InternalState)originalActivity; // NOTE: State.Entry/Exit are allowed to be removed, because it doesn't change the execution semantics of SM // if this removed activity was executing, WF runtime would disallow the update. Activity entryActivityMatch = metadata.GetMatch(this.Entry); Activity exitActivityMatch = metadata.GetMatch(this.Exit); if ((null != entryActivityMatch && !object.ReferenceEquals(entryActivityMatch, originalInternalState.Entry)) || (null != exitActivityMatch && !object.ReferenceEquals(exitActivityMatch, originalInternalState.Exit))) { // original State.Entry/Exit is replaced with another child activities with InternalState // new State.Entry/Exit is moved from another child activities within InternalState. metadata.DisallowUpdateInsideThisActivity(SR.MovingActivitiesInStateBlockDU); return; } int originalTriggerInUpdatedDefinition = 0; foreach (InternalTransition originalTransition in originalInternalState.internalTransitions) { if (metadata.IsReferenceToImportedChild(originalTransition.Trigger)) { metadata.DisallowUpdateInsideThisActivity(SR.TriggerOrConditionIsReferenced); return; } if (!originalTransition.IsUnconditional) { // new Trigger activity foreach (TransitionData transitionData in originalTransition.TransitionDataList) { if (metadata.IsReferenceToImportedChild(transitionData.Condition)) { metadata.DisallowUpdateInsideThisActivity(SR.TriggerOrConditionIsReferenced); return; } } } } foreach (InternalTransition updatedTransition in this.internalTransitions) { if (metadata.IsReferenceToImportedChild(updatedTransition.Trigger)) { // if the trigger is referenced, it might have another save values already. metadata.DisallowUpdateInsideThisActivity(SR.TriggerOrConditionIsReferenced); return; } Activity triggerMatch = metadata.GetMatch(updatedTransition.Trigger); if (null != triggerMatch) { InternalTransition originalTransition; if (originalInternalState.triggerInternalTransitionMapping.TryGetValue(triggerMatch, out originalTransition)) { originalTriggerInUpdatedDefinition++; if (originalTransition.IsUnconditional) { string errorMessage; bool canTransitionBeUpdated = ValidateDUInUnconditionalTransition(metadata, updatedTransition, originalTransition, out errorMessage); if (!canTransitionBeUpdated) { metadata.DisallowUpdateInsideThisActivity(errorMessage); return; } } else { if (updatedTransition.IsUnconditional) { // cannot change the transition from condition to unconditional. metadata.DisallowUpdateInsideThisActivity(SR.ChangeConditionalTransitionToUnconditionalBlockDU); return; } else { string errorMessage; bool canTransitionBeUpdated = ValidateDUInConditionTransition(metadata, updatedTransition, originalTransition, out errorMessage); if (!canTransitionBeUpdated) { metadata.DisallowUpdateInsideThisActivity(errorMessage); return; } } } } else { // the trigger is an child activity moved from elsewhere within the state metadata.DisallowUpdateInsideThisActivity(SR.MovingActivitiesInStateBlockDU); return; } } else { // new Trigger activity foreach (TransitionData transitionData in updatedTransition.TransitionDataList) { if ((null != transitionData.Condition && null != metadata.GetMatch(transitionData.Condition)) || (null != transitionData.Action && null != metadata.GetMatch(transitionData.Action))) { // if a new transition is added, it is expected that the Condition/Action // are newly created. metadata.DisallowUpdateInsideThisActivity(SR.ChangingTriggerOrUseOriginalConditionActionBlockDU); return; } } } } if (originalTriggerInUpdatedDefinition != originalInternalState.internalTransitions.Count) { // NOTE: in general, if the transition is removed when there are pending triggers, // runtime would be able to detect the missing child activities. However, in cases, // where the transition is happening already (in between completion of Transition.Action // callback but before InternalState is completed), the workflow definition can be unloaded // and updated. The InternalState is unable to trace the original transition that set the // destination state index. In that case, the update would fail at UpdateInstance. // To simplify the model, it is more convenient to disallow removing existing transitions // from an executing InternalState. The only extra restriction it brings, is that it disables // update even if the InternalState is uploaded at State.Entry. This scenario, however, is uncommon. metadata.DisallowUpdateInsideThisActivity(SR.RemovingTransitionsBlockDU); } }