2016-08-03 10:59:49 +00:00
//-----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
namespace System.Activities.Runtime
{
using System ;
using System.Collections.Generic ;
using System.Globalization ;
using System.Diagnostics.CodeAnalysis ;
using System.Runtime ;
using System.Runtime.Serialization ;
using System.Activities.DynamicUpdate ;
using System.Activities.Statements ;
using System.Collections.ObjectModel ;
[DataContract(Name = XD.Runtime.ActivityInstanceMap, Namespace = XD.Runtime.Namespace)]
class ActivityInstanceMap
{
// map from activities to (active) associated activity instances
IDictionary < Activity , InstanceList > instanceMapping ;
InstanceList [ ] rawDeserializedLists ;
IList < InstanceListNeedingUpdate > updateList ;
internal ActivityInstanceMap ( )
{
}
[ SuppressMessage ( FxCop . Category . Performance , FxCop . Rule . AvoidUncalledPrivateCode ,
Justification = "Called by serialization" ) ]
[DataMember(EmitDefaultValue = false)]
internal InstanceList [ ] SerializedInstanceLists
{
get
{
if ( this . instanceMapping = = null | | this . instanceMapping . Count = = 0 )
{
return this . rawDeserializedLists ;
}
else
{
InstanceList [ ] lists = new InstanceList [ this . instanceMapping . Count ] ;
int index = 0 ;
foreach ( KeyValuePair < Activity , InstanceList > entry in this . instanceMapping )
{
entry . Value . ActivityId = entry . Key . QualifiedId . AsByteArray ( ) ;
lists [ index ] = entry . Value ;
index + + ;
}
return lists ;
}
}
set
{
Fx . Assert ( value ! = null , "We don't serialize the default value." ) ;
this . rawDeserializedLists = value ;
}
}
IDictionary < Activity , InstanceList > InstanceMapping
{
get
{
if ( this . instanceMapping = = null )
{
this . instanceMapping = new Dictionary < Activity , InstanceList > ( ) ;
}
return this . instanceMapping ;
}
}
private static void AddBlockingActivity ( ref Collection < ActivityBlockingUpdate > updateErrors , DynamicUpdateMap . UpdatedActivity updatedActivity , QualifiedId originalId , string reason , string activityInstanceId )
{
if ( updatedActivity . NewActivity ! = null )
{
ActivityBlockingUpdate . AddBlockingActivity ( ref updateErrors , updatedActivity . NewActivity , originalId . ToString ( ) , reason , activityInstanceId ) ;
}
else
{
string updatedId = updatedActivity . MapEntry . IsRemoval ? null : updatedActivity . NewId . ToString ( ) ;
ActivityBlockingUpdate . AddBlockingActivity ( ref updateErrors , updatedId , originalId . ToString ( ) , reason , activityInstanceId ) ;
}
}
public void GetActivitiesBlockingUpdate ( DynamicUpdateMap updateMap , List < ActivityInstance > secondaryRootInstances , ref Collection < ActivityBlockingUpdate > updateErrors )
{
this . GetInstanceListsNeedingUpdate ( updateMap , null , secondaryRootInstances , ref updateErrors ) ;
}
// searching secondaryRootInstances list is necessary because instance in InstanceList doesn't have its Parent set until it's fixed up.
// so the only way to find out if an instance in InstanceList is a secondary root is to lookup in secondaryRootInstances list.
private static bool IsNonDefaultSecondaryRoot ( ActivityInstance instance , List < ActivityInstance > secondaryRootInstances )
{
if ( secondaryRootInstances ! = null & & secondaryRootInstances . Contains ( instance ) )
{
// Non-default secondary roots are CompensationParticipant type, and their environment will always have a non-null parent which is the environment owned by a CompensableActivity.
// A secondary root whose environment parent is null is the default secondary root, WorkflowCompensationBehavior.
if ( instance . IsEnvironmentOwner & & instance . Environment . Parent ! = null )
{
return true ;
}
}
return false ;
}
private static bool CanCompensationOrConfirmationHandlerReferenceAddedSymbols ( InstanceList instanceList , DynamicUpdateMap rootUpdateMap , IdSpace rootIdSpace , List < ActivityInstance > secondaryRootInstances , ref Collection < ActivityBlockingUpdate > updateErrors )
{
for ( int j = 0 ; j < instanceList . Count ; j + + )
{
ActivityInstance activityInstance = instanceList [ j ] as ActivityInstance ;
if ( activityInstance = = null | | ! IsNonDefaultSecondaryRoot ( activityInstance , secondaryRootInstances ) )
{
continue ;
}
// here, find out if the given non-default secondary root references an environment to which a symbol is to be added via DU.
// we start from a secondary root instead of starting from the enviroment with the already completed owner that was added symbols.
// It is becuase for the case of adding symbols to noSymbols activities, the environment doesn't even exist from which we can start looking for referencing secondary root.
int [ ] secondaryRootOriginalQID = new QualifiedId ( instanceList . ActivityId ) . AsIDArray ( ) ;
Fx . Assert ( secondaryRootOriginalQID ! = null & & secondaryRootOriginalQID . Length > 1 ,
"CompensationParticipant is always an implementation child of a CompensableActivity, therefore it's IdSpace must be at least one level deep." ) ;
int [ ] parentOfSecondaryRootOriginalQID = new int [ secondaryRootOriginalQID . Length - 1 ] ;
Array . Copy ( secondaryRootOriginalQID , parentOfSecondaryRootOriginalQID , secondaryRootOriginalQID . Length - 1 ) ;
List < int > currentQIDBuilder = new List < int > ( ) ;
for ( int i = 0 ; i < parentOfSecondaryRootOriginalQID . Length ; i + + )
{
//
// for each iteration of this for-loop,
// we are finding out if at every IdSpace level the map has any map entry whose activity has the CompensableActivity as an implementation decendant.
// The map may not exist for every IdSpace between the root and the CompensableActivity.
// If the matching map and the entry is found, then we find out if that matching entry's activity is a public decendant of any NoSymbols activity DU is to add variables or arguments to.
//
// This walk on the definition activity tree determines the hypothetical execution-time chain of instances and environments.
// The ultimate goal is to prevent adding variables or arguments to a NoSymbols activity which has already completed,
// but its decendant CompensableActivity's compensation or confirmation handlers in the future may need to reference the added variables or arguments.
currentQIDBuilder . Add ( parentOfSecondaryRootOriginalQID [ i ] ) ;
DynamicUpdateMap . UpdatedActivity updatedActivity = rootUpdateMap . GetUpdatedActivity ( new QualifiedId ( currentQIDBuilder . ToArray ( ) ) , rootIdSpace ) ;
if ( updatedActivity . MapEntry ! = null )
{
// the activity of this entry either has the CompensableActivity as an implementation decendant, or is the CompensableActivity itself.
// walk the same-IdSpace-parent chain of the entry,
// look for an entry whose EnvironmentUpdateMap.IsAdditionToNoSymbols is true.
DynamicUpdateMapEntry entry = updatedActivity . MapEntry ;
do
{
if ( ! entry . IsRemoval & & entry . HasEnvironmentUpdates & & entry . EnvironmentUpdateMap . IsAdditionToNoSymbols )
{
int [ ] noSymbolAddActivityIDArray = currentQIDBuilder . ToArray ( ) ;
noSymbolAddActivityIDArray [ noSymbolAddActivityIDArray . Length - 1 ] = entry . OldActivityId ;
QualifiedId noSymbolAddActivityQID = new QualifiedId ( noSymbolAddActivityIDArray ) ;
DynamicUpdateMap . UpdatedActivity noSymbolAddUpdatedActivity = rootUpdateMap . GetUpdatedActivity ( noSymbolAddActivityQID , rootIdSpace ) ;
AddBlockingActivity ( ref updateErrors , noSymbolAddUpdatedActivity , noSymbolAddActivityQID , SR . VariableOrArgumentAdditionToReferencedEnvironmentNoDUSupported , null ) ;
return true ;
}
entry = entry . Parent ;
} while ( entry ! = null ) ;
}
}
}
return false ;
}
private static bool IsInvalidEnvironmentUpdate ( InstanceList instanceList , DynamicUpdateMap . UpdatedActivity updatedActivity , ref Collection < ActivityBlockingUpdate > updateErrors )
{
if ( updatedActivity . MapEntry = = null | | ! updatedActivity . MapEntry . HasEnvironmentUpdates )
{
return false ;
}
for ( int j = 0 ; j < instanceList . Count ; j + + )
{
ActivityInstance activityInstance = instanceList [ j ] as ActivityInstance ;
if ( activityInstance ! = null )
{
string error = null ;
if ( activityInstance . SubState = = ActivityInstance . Substate . ResolvingVariables )
{
// if the entry has Environment update to do when the instance is in the middle of resolving variable, it is an error.
error = SR . CannotUpdateEnvironmentInTheMiddleOfResolvingVariables ;
}
else if ( activityInstance . SubState = = ActivityInstance . Substate . ResolvingArguments )
{
// if the entry has Environment update to do when the instance is in the middle of resolving arguments, it is an error.
error = SR . CannotUpdateEnvironmentInTheMiddleOfResolvingArguments ;
}
if ( error ! = null )
{
AddBlockingActivity ( ref updateErrors , updatedActivity , new QualifiedId ( instanceList . ActivityId ) , error , activityInstance . Id ) ;
return true ;
}
}
else
{
LocationEnvironment environment = instanceList [ j ] as LocationEnvironment ;
if ( environment ! = null )
{
//
// environment that is referenced by a secondary root
// Adding a variable or argument that requires expression scheduling to this instanceless environment is not allowed.
//
List < int > dummyIndexes ;
EnvironmentUpdateMap envMap = updatedActivity . MapEntry . EnvironmentUpdateMap ;
if ( ( envMap . HasVariableEntries & & TryGatherSchedulableExpressions ( envMap . VariableEntries , out dummyIndexes ) ) | |
( envMap . HasPrivateVariableEntries & & TryGatherSchedulableExpressions ( envMap . PrivateVariableEntries , out dummyIndexes ) ) | |
( envMap . HasArgumentEntries & & TryGatherSchedulableExpressions ( envMap . ArgumentEntries , out dummyIndexes ) ) )
{
AddBlockingActivity ( ref updateErrors , updatedActivity , new QualifiedId ( instanceList . ActivityId ) , SR . VariableOrArgumentAdditionToReferencedEnvironmentNoDUSupported , null ) ;
return true ;
}
}
}
}
return false ;
}
private static bool IsRemovalOrRTUpdateBlockedOrBlockedByUser ( DynamicUpdateMap . UpdatedActivity updatedActivity , QualifiedId oldQualifiedId , out string error )
{
error = null ;
if ( updatedActivity . MapEntry . IsRemoval )
{
//
error = SR . CannotRemoveExecutingActivityUpdateError ( oldQualifiedId , updatedActivity . MapEntry . DisplayName ) ;
}
else if ( updatedActivity . MapEntry . IsRuntimeUpdateBlocked )
{
error = updatedActivity . MapEntry . BlockReasonMessage ? ? UpdateBlockedReasonMessages . Get ( updatedActivity . MapEntry . BlockReason ) ;
}
else if ( updatedActivity . MapEntry . IsUpdateBlockedByUpdateAuthor )
{
error = SR . BlockedUpdateInsideActivityUpdateByUserError ;
}
return error ! = null ;
}
// targetDefinition argument is optional.
private IList < InstanceListNeedingUpdate > GetInstanceListsNeedingUpdate ( DynamicUpdateMap updateMap , Activity targetDefinition , List < ActivityInstance > secondaryRootInstances , ref Collection < ActivityBlockingUpdate > updateErrors )
{
IList < InstanceListNeedingUpdate > instanceListsToUpdate = new List < InstanceListNeedingUpdate > ( ) ;
if ( this . rawDeserializedLists = = null )
{
// This instance doesn't have any active instances (it is complete).
return instanceListsToUpdate ;
}
IdSpace rootIdSpace = null ;
if ( targetDefinition ! = null )
{
rootIdSpace = targetDefinition . MemberOf ;
}
for ( int i = 0 ; i < this . rawDeserializedLists . Length ; i + + )
{
InstanceList list = this . rawDeserializedLists [ i ] ;
QualifiedId oldQualifiedId = new QualifiedId ( list . ActivityId ) ;
if ( updateMap . IsImplementationAsRoot )
{
int [ ] oldIdArray = oldQualifiedId . AsIDArray ( ) ;
if ( oldIdArray . Length = = 1 & & oldIdArray [ 0 ] ! = 1 )
{
throw FxTrace . Exception . AsError ( new InstanceUpdateException ( SR . InvalidImplementationAsWorkflowRootForRuntimeState ) ) ;
}
}
string error ;
InstanceListNeedingUpdate update ;
DynamicUpdateMap . UpdatedActivity updatedActivity = updateMap . GetUpdatedActivity ( oldQualifiedId , rootIdSpace ) ;
if ( CanCompensationOrConfirmationHandlerReferenceAddedSymbols ( list , updateMap , rootIdSpace , secondaryRootInstances , ref updateErrors ) )
{
update = null ;
}
else if ( updatedActivity . MapEntry = = null )
{
if ( updatedActivity . IdChanged )
{
// this newQualifiedId is the new id for those InstanceLists whose IDs shifted by their parents' ID change
update = new InstanceListNeedingUpdate
{
InstanceList = list ,
NewId = updatedActivity . NewId
} ;
}
else
{
// nothing changed, no map, no mapEntry
update = new InstanceListNeedingUpdate
{
InstanceList = list ,
NewId = null ,
} ;
}
}
else if ( updatedActivity . MapEntry . IsParentRemovedOrBlocked )
{
update = null ;
}
else if ( IsRemovalOrRTUpdateBlockedOrBlockedByUser ( updatedActivity , oldQualifiedId , out error ) )
{
string instanceId = null ;
for ( int j = 0 ; j < list . Count ; j + + )
{
ActivityInstance activityInstance = list [ j ] as ActivityInstance ;
if ( activityInstance ! = null )
{
instanceId = activityInstance . Id ;
break ;
}
}
AddBlockingActivity ( ref updateErrors , updatedActivity , oldQualifiedId , error , instanceId ) ;
update = null ;
}
else if ( IsInvalidEnvironmentUpdate ( list , updatedActivity , ref updateErrors ) )
{
update = null ;
}
else
{
// no validation error for this InstanceList
// add it to the list of InstanceLists to be updated
update = new InstanceListNeedingUpdate
{
InstanceList = list ,
NewId = updatedActivity . NewId ,
UpdateMap = updatedActivity . Map ,
MapEntry = updatedActivity . MapEntry ,
NewActivity = updatedActivity . NewActivity
} ;
}
if ( update ! = null )
{
update . OriginalId = list . ActivityId ;
instanceListsToUpdate . Add ( update ) ;
}
}
return instanceListsToUpdate ;
}
public void UpdateRawInstance ( DynamicUpdateMap updateMap , Activity targetDefinition , List < ActivityInstance > secondaryRootInstances , ref Collection < ActivityBlockingUpdate > updateErrors )
{
this . updateList = GetInstanceListsNeedingUpdate ( updateMap , targetDefinition , secondaryRootInstances , ref updateErrors ) ;
if ( updateErrors ! = null & & updateErrors . Count > 0 )
{
// error found.
// there is no need to proceed to updating the instances
return ;
}
// if UpdateType is either MapEntryExists or ParentIdShiftOnly,
// update the ActivityIDs and update Environments
// also, update the ImplementationVersion.
foreach ( InstanceListNeedingUpdate update in this . updateList )
{
Fx . Assert ( update . InstanceList ! = null , "update.InstanceList must not be null." ) ;
if ( update . NothingChanged )
{
continue ;
}
Fx . Assert ( update . NewId ! = null , "update.NewId must not be null." ) ;
InstanceList instanceList = update . InstanceList ;
instanceList . ActivityId = update . NewId . AsByteArray ( ) ;
if ( update . ParentIdShiftOnly )
{
// this InstanceList must have been one of those whose IDs shifted by their parent's ID change,
// but no involvement in DU.
continue ;
}
bool implementationVersionUpdateNeeded = false ;
if ( update . MapEntry . ImplementationUpdateMap ! = null )
{
implementationVersionUpdateNeeded = true ;
}
if ( update . MapEntry . HasEnvironmentUpdates )
{
// update LocationEnvironemnt
Fx . Assert ( update . NewActivity ! = null , "TryGetUpdateMapEntryFromRootMap should have thrown if it couldn't map to an activity" ) ;
instanceList . UpdateEnvironments ( update . MapEntry . EnvironmentUpdateMap , update . NewActivity ) ;
}
for ( int i = 0 ; i < instanceList . Count ; i + + )
{
ActivityInstance activityInstance = instanceList [ i ] as ActivityInstance ;
if ( implementationVersionUpdateNeeded )
{
activityInstance . ImplementationVersion = update . NewActivity . ImplementationVersion ;
}
}
}
}
private static bool TryGatherSchedulableExpressions ( IList < EnvironmentUpdateMapEntry > entries , out List < int > addedLocationReferenceIndexes )
{
addedLocationReferenceIndexes = null ;
for ( int i = 0 ; i < entries . Count ; i + + )
{
EnvironmentUpdateMapEntry entry = entries [ i ] ;
if ( entry . IsAddition )
{
if ( addedLocationReferenceIndexes = = null )
{
addedLocationReferenceIndexes = new List < int > ( ) ;
}
addedLocationReferenceIndexes . Add ( entry . NewOffset ) ;
}
}
return addedLocationReferenceIndexes ! = null ;
}
// this is called after all instances have been loaded and fixedup
public void UpdateInstanceByActivityParticipation ( ActivityExecutor activityExecutor , DynamicUpdateMap rootMap , ref Collection < ActivityBlockingUpdate > updateErrors )
{
foreach ( InstanceListNeedingUpdate participant in this . updateList )
{
if ( participant . NothingChanged | | participant . ParentIdShiftOnly )
{
Fx . Assert ( participant . UpdateMap = = null & & participant . MapEntry = = null , "UpdateMap and MapEntry must be null if we are here." ) ;
// create a temporary NoChanges UpdateMap as well as a temporary no change MapEntry
// so that we can create a NativeActivityUpdateContext object in order to invoke UpdateInstance() on an activity which
// doesn't have a corresponding map and an map entry.
// The scenario enabled here is scheduling a newly added reference branch to a Parallel inside an activity's implementation.
participant . UpdateMap = DynamicUpdateMap . DummyMap ;
participant . MapEntry = DynamicUpdateMapEntry . DummyMapEntry ;
}
// now let activities participate in update
for ( int i = 0 ; i < participant . InstanceList . Count ; i + + )
{
ActivityInstance instance = participant . InstanceList [ i ] as ActivityInstance ;
if ( instance = = null )
{
continue ;
}
IInstanceUpdatable activity = instance . Activity as IInstanceUpdatable ;
if ( activity ! = null & & instance . SubState = = ActivityInstance . Substate . Executing )
{
NativeActivityUpdateContext updateContext = new NativeActivityUpdateContext ( this , activityExecutor , instance , participant . UpdateMap , participant . MapEntry , rootMap ) ;
try
{
activity . InternalUpdateInstance ( updateContext ) ;
if ( updateContext . IsUpdateDisallowed )
{
ActivityBlockingUpdate . AddBlockingActivity ( ref updateErrors , instance . Activity , new QualifiedId ( participant . OriginalId ) . ToString ( ) , updateContext . DisallowedReason , instance . Id ) ;
continue ;
}
}
catch ( Exception e )
{
if ( Fx . IsFatal ( e ) )
{
throw ;
}
throw FxTrace . Exception . AsError ( new InstanceUpdateException ( SR . NativeActivityUpdateInstanceThrewException ( e . Message ) , e ) ) ;
}
finally
{
updateContext . Dispose ( ) ;
}
}
}
}
// Schedule evaluation of newly added arguments and newly added variables.
// This needs to happen after all the invokations of UpdateInstance above, so that newly
// added arguments and newly added variables get evaluated before any newly added activities get executed.
// We iterate the list in reverse so that parents are always scheduled after (and thus
// execute before) their children, which may depend on the parents.
for ( int i = this . updateList . Count - 1 ; i > = 0 ; i - - )
{
InstanceListNeedingUpdate participant = this . updateList [ i ] ;
if ( ! participant . MapEntryExists )
{
// if the given InstanceList has no map entry,
// then there is no new LocationReferences to resolve.
continue ;
}
Fx . Assert ( participant . MapEntry ! = null , "MapEntry must be non-null here." ) ;
if ( ! participant . MapEntry . HasEnvironmentUpdates )
{
// if there is no environment updates for this MapEntry,
// then there is no new LocationReferences to resolve.
continue ;
}
for ( int j = 0 ; j < participant . InstanceList . Count ; j + + )
{
ActivityInstance instance = participant . InstanceList [ j ] as ActivityInstance ;
if ( instance = = null | | instance . SubState ! = ActivityInstance . Substate . Executing )
{
// if the given ActivityInstance is not in Substate.Executing,
// then, do not try to resolve new LocationReferences
continue ;
}
List < int > addedArgumentIndexes ;
List < int > addedVariableIndexes ;
List < int > addedPrivateVariableIndexes ;
EnvironmentUpdateMap envMap = participant . MapEntry . EnvironmentUpdateMap ;
if ( envMap . HasVariableEntries & & TryGatherSchedulableExpressions ( envMap . VariableEntries , out addedVariableIndexes ) )
{
// schedule added variable default expressions
instance . ResolveNewVariableDefaultsDuringDynamicUpdate ( activityExecutor , addedVariableIndexes , false ) ;
}
if ( envMap . HasPrivateVariableEntries & & TryGatherSchedulableExpressions ( envMap . PrivateVariableEntries , out addedPrivateVariableIndexes ) )
{
// schedule added private variable default expressions
// HasPrivateMemberChanged() check disallows addition of private variable default that offsets the private IdSpace,
// However, the added private variable default expression can be an imported activity, which has no affect on the private IdSpace.
// For such case, we want to be able to schedule the imported default expressions here.
instance . ResolveNewVariableDefaultsDuringDynamicUpdate ( activityExecutor , addedPrivateVariableIndexes , true ) ;
}
if ( envMap . HasArgumentEntries & & TryGatherSchedulableExpressions ( envMap . ArgumentEntries , out addedArgumentIndexes ) )
{
// schedule added arguments
instance . ResolveNewArgumentsDuringDynamicUpdate ( activityExecutor , addedArgumentIndexes ) ;
}
}
}
}
public void AddEntry ( IActivityReference reference , bool skipIfDuplicate )
{
Activity activity = reference . Activity ;
InstanceList mappedInstances ;
if ( this . InstanceMapping . TryGetValue ( activity , out mappedInstances ) )
{
mappedInstances . Add ( reference , skipIfDuplicate ) ;
}
else
{
this . InstanceMapping . Add ( activity , new InstanceList ( reference ) ) ;
}
}
public void AddEntry ( IActivityReference reference )
{
AddEntry ( reference , false ) ;
}
public void LoadActivityTree ( Activity rootActivity , ActivityInstance rootInstance , List < ActivityInstance > secondaryRootInstances , ActivityExecutor executor )
{
Fx . Assert ( this . rawDeserializedLists ! = null , "We should always have deserialized some lists." ) ;
this . instanceMapping = new Dictionary < Activity , InstanceList > ( this . rawDeserializedLists . Length ) ;
for ( int i = 0 ; i < this . rawDeserializedLists . Length ; i + + )
{
InstanceList list = this . rawDeserializedLists [ i ] ;
Activity activity ;
if ( ! QualifiedId . TryGetElementFromRoot ( rootActivity , list . ActivityId , out activity ) )
{
throw FxTrace . Exception . AsError ( new InvalidOperationException ( SR . ActivityInstanceFixupFailed ) ) ;
}
this . instanceMapping . Add ( activity , list ) ;
list . Load ( activity , this ) ;
}
// We need to null this out once we've recreated the dictionary to avoid
2017-08-21 15:34:15 +00:00
// having out of sync data
2016-08-03 10:59:49 +00:00
this . rawDeserializedLists = null ;
// then walk our instance list, fixup parent references, and perform basic validation
Func < ActivityInstance , ActivityExecutor , bool > processInstanceCallback = new Func < ActivityInstance , ActivityExecutor , bool > ( OnActivityInstanceLoaded ) ;
rootInstance . FixupInstance ( null , this , executor ) ;
ActivityUtilities . ProcessActivityInstanceTree ( rootInstance , executor , processInstanceCallback ) ;
if ( secondaryRootInstances ! = null )
{
foreach ( ActivityInstance instance in secondaryRootInstances )
{
instance . FixupInstance ( null , this , executor ) ;
ActivityUtilities . ProcessActivityInstanceTree ( instance , executor , processInstanceCallback ) ;
}
}
}
bool OnActivityInstanceLoaded ( ActivityInstance activityInstance , ActivityExecutor executor )
{
return activityInstance . TryFixupChildren ( this , executor ) ;
}
public bool RemoveEntry ( IActivityReference reference )
{
if ( this . instanceMapping = = null )
{
return false ;
}
Activity activity = reference . Activity ;
InstanceList mappedInstances ;
if ( ! this . InstanceMapping . TryGetValue ( activity , out mappedInstances ) )
{
return false ;
}
if ( mappedInstances . Count = = 1 )
{
this . InstanceMapping . Remove ( activity ) ;
}
else
{
mappedInstances . Remove ( reference ) ;
}
return true ;
}
[DataContract]
internal class InstanceList : HybridCollection < IActivityReference >
{
public InstanceList ( IActivityReference reference )
: base ( reference )
{
}
[ SuppressMessage ( FxCop . Category . Performance , FxCop . Rule . AvoidUncalledPrivateCode ,
Justification = "Called by serialization" ) ]
[DataMember]
public byte [ ] ActivityId
{
get ;
set ;
}
[OnSerializing]
[SuppressMessage(FxCop.Category.Usage, FxCop.Rule.ReviewUnusedParameters)]
[ SuppressMessage ( FxCop . Category . Usage , "CA2238:ImplementSerializationMethodsCorrectly" ,
Justification = "Needs to be internal for serialization in partial trust. We have set InternalsVisibleTo(System.Runtime.Serialization) to allow this." ) ]
internal void OnSerializing ( StreamingContext context )
{
base . Compress ( ) ;
}
public void Add ( IActivityReference reference , bool skipIfDuplicate )
{
Fx . Assert ( this . Count > = 1 , "instance list should never be empty when we call Add" ) ;
if ( skipIfDuplicate )
{
if ( base . SingleItem ! = null )
{
if ( base . SingleItem = = reference )
{
return ;
}
}
else
{
if ( base . MultipleItems . Contains ( reference ) )
{
return ;
}
}
}
Add ( reference ) ;
}
public void Load ( Activity activity , ActivityInstanceMap instanceMap )
{
Fx . Assert ( this . Count > = 1 , "instance list should never be empty on load" ) ;
if ( base . SingleItem ! = null )
{
base . SingleItem . Load ( activity , instanceMap ) ;
}
else
{
for ( int i = 0 ; i < base . MultipleItems . Count ; i + + )
{
base . MultipleItems [ i ] . Load ( activity , instanceMap ) ;
}
}
}
public void UpdateEnvironments ( EnvironmentUpdateMap map , Activity activity )
{
if ( base . SingleItem ! = null )
{
IActivityReferenceWithEnvironment reference = base . SingleItem as IActivityReferenceWithEnvironment ;
if ( reference ! = null )
{
reference . UpdateEnvironment ( map , activity ) ;
}
}
else
{
for ( int i = 0 ; i < base . MultipleItems . Count ; i + + )
{
IActivityReferenceWithEnvironment reference = base . MultipleItems [ i ] as IActivityReferenceWithEnvironment ;
if ( reference ! = null )
{
reference . UpdateEnvironment ( map , activity ) ;
}
}
}
}
}
public interface IActivityReference
{
Activity Activity { get ; }
void Load ( Activity activity , ActivityInstanceMap instanceMap ) ;
}
public interface IActivityReferenceWithEnvironment : IActivityReference
{
void UpdateEnvironment ( EnvironmentUpdateMap map , Activity activity ) ;
}
class InstanceListNeedingUpdate
{
// The list of IActivityReferences to be updated
public InstanceList InstanceList { get ; set ; }
public byte [ ] OriginalId { get ; set ; }
// The new ActivityId for these ActivityReferences.
public QualifiedId NewId { get ; set ; }
// The Map & MapEntry for this ActivityId, if there is one.
// Null if the activity's parent Id was updated, but not the activity itself,
// Or null if nothing changed.
public DynamicUpdateMap UpdateMap { get ; set ; }
public DynamicUpdateMapEntry MapEntry { get ; set ; }
// A pointer to this activity, in the new definition.
// Null if we don't have the definition loaded.
public Activity NewActivity { get ; set ; }
//
// the following three properties are mutual exlusive,
// meaning, one and only one of them evaluates to TRUE.
//
public bool NothingChanged
{
get
{
return this . MapEntry = = null & & this . NewId = = null ;
}
}
public bool MapEntryExists
{
get
{
return this . MapEntry ! = null ;
}
}
public bool ParentIdShiftOnly
{
get
{
return this . MapEntry = = null & & this . NewId ! = null ;
}
}
}
}
}