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);
}
}