1 //-----------------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //-----------------------------------------------------------------------------
5 namespace System
.Activities
.Runtime
8 using System
.Collections
.Generic
;
9 using System
.Globalization
;
10 using System
.Diagnostics
.CodeAnalysis
;
12 using System
.Runtime
.Serialization
;
13 using System
.Activities
.DynamicUpdate
;
14 using System
.Activities
.Statements
;
15 using System
.Collections
.ObjectModel
;
17 [DataContract(Name
= XD
.Runtime
.ActivityInstanceMap
, Namespace
= XD
.Runtime
.Namespace
)]
18 class ActivityInstanceMap
20 // map from activities to (active) associated activity instances
21 IDictionary
<Activity
, InstanceList
> instanceMapping
;
22 InstanceList
[] rawDeserializedLists
;
24 IList
<InstanceListNeedingUpdate
> updateList
;
26 internal ActivityInstanceMap()
30 [SuppressMessage(FxCop
.Category
.Performance
, FxCop
.Rule
.AvoidUncalledPrivateCode
,
31 Justification
= "Called by serialization")]
32 [DataMember(EmitDefaultValue
= false)]
33 internal InstanceList
[] SerializedInstanceLists
37 if (this.instanceMapping
== null || this.instanceMapping
.Count
== 0)
39 return this.rawDeserializedLists
;
43 InstanceList
[] lists
= new InstanceList
[this.instanceMapping
.Count
];
45 foreach (KeyValuePair
<Activity
, InstanceList
> entry
in this.instanceMapping
)
47 entry
.Value
.ActivityId
= entry
.Key
.QualifiedId
.AsByteArray();
48 lists
[index
] = entry
.Value
;
57 Fx
.Assert(value != null, "We don't serialize the default value.");
59 this.rawDeserializedLists
= value;
63 IDictionary
<Activity
, InstanceList
> InstanceMapping
67 if (this.instanceMapping
== null)
69 this.instanceMapping
= new Dictionary
<Activity
, InstanceList
>();
72 return this.instanceMapping
;
76 private static void AddBlockingActivity(ref Collection
<ActivityBlockingUpdate
> updateErrors
, DynamicUpdateMap
.UpdatedActivity updatedActivity
, QualifiedId originalId
, string reason
, string activityInstanceId
)
78 if (updatedActivity
.NewActivity
!= null)
80 ActivityBlockingUpdate
.AddBlockingActivity(ref updateErrors
, updatedActivity
.NewActivity
, originalId
.ToString(), reason
, activityInstanceId
);
84 string updatedId
= updatedActivity
.MapEntry
.IsRemoval
? null : updatedActivity
.NewId
.ToString();
85 ActivityBlockingUpdate
.AddBlockingActivity(ref updateErrors
, updatedId
, originalId
.ToString(), reason
, activityInstanceId
);
89 public void GetActivitiesBlockingUpdate(DynamicUpdateMap updateMap
, List
<ActivityInstance
> secondaryRootInstances
, ref Collection
<ActivityBlockingUpdate
> updateErrors
)
91 this.GetInstanceListsNeedingUpdate(updateMap
, null, secondaryRootInstances
, ref updateErrors
);
94 // searching secondaryRootInstances list is necessary because instance in InstanceList doesn't have its Parent set until it's fixed up.
95 // so the only way to find out if an instance in InstanceList is a secondary root is to lookup in secondaryRootInstances list.
96 private static bool IsNonDefaultSecondaryRoot(ActivityInstance instance
, List
<ActivityInstance
> secondaryRootInstances
)
98 if (secondaryRootInstances
!= null && secondaryRootInstances
.Contains(instance
))
100 // 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.
101 // A secondary root whose environment parent is null is the default secondary root, WorkflowCompensationBehavior.
102 if (instance
.IsEnvironmentOwner
&& instance
.Environment
.Parent
!= null)
111 private static bool CanCompensationOrConfirmationHandlerReferenceAddedSymbols(InstanceList instanceList
, DynamicUpdateMap rootUpdateMap
, IdSpace rootIdSpace
, List
<ActivityInstance
> secondaryRootInstances
, ref Collection
<ActivityBlockingUpdate
> updateErrors
)
113 for (int j
= 0; j
< instanceList
.Count
; j
++)
115 ActivityInstance activityInstance
= instanceList
[j
] as ActivityInstance
;
116 if (activityInstance
== null || !IsNonDefaultSecondaryRoot(activityInstance
, secondaryRootInstances
))
121 // here, find out if the given non-default secondary root references an environment to which a symbol is to be added via DU.
122 // we start from a secondary root instead of starting from the enviroment with the already completed owner that was added symbols.
123 // 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.
125 int[] secondaryRootOriginalQID
= new QualifiedId(instanceList
.ActivityId
).AsIDArray();
127 Fx
.Assert(secondaryRootOriginalQID
!= null && secondaryRootOriginalQID
.Length
> 1,
128 "CompensationParticipant is always an implementation child of a CompensableActivity, therefore it's IdSpace must be at least one level deep.");
130 int[] parentOfSecondaryRootOriginalQID
= new int[secondaryRootOriginalQID
.Length
- 1];
131 Array
.Copy(secondaryRootOriginalQID
, parentOfSecondaryRootOriginalQID
, secondaryRootOriginalQID
.Length
- 1);
133 List
<int> currentQIDBuilder
= new List
<int>();
134 for (int i
= 0; i
< parentOfSecondaryRootOriginalQID
.Length
; i
++)
137 // for each iteration of this for-loop,
138 // we are finding out if at every IdSpace level the map has any map entry whose activity has the CompensableActivity as an implementation decendant.
139 // The map may not exist for every IdSpace between the root and the CompensableActivity.
140 // 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.
142 // This walk on the definition activity tree determines the hypothetical execution-time chain of instances and environments.
143 // The ultimate goal is to prevent adding variables or arguments to a NoSymbols activity which has already completed,
144 // but its decendant CompensableActivity's compensation or confirmation handlers in the future may need to reference the added variables or arguments.
146 currentQIDBuilder
.Add(parentOfSecondaryRootOriginalQID
[i
]);
148 DynamicUpdateMap
.UpdatedActivity updatedActivity
= rootUpdateMap
.GetUpdatedActivity(new QualifiedId(currentQIDBuilder
.ToArray()), rootIdSpace
);
149 if (updatedActivity
.MapEntry
!= null)
151 // the activity of this entry either has the CompensableActivity as an implementation decendant, or is the CompensableActivity itself.
153 // walk the same-IdSpace-parent chain of the entry,
154 // look for an entry whose EnvironmentUpdateMap.IsAdditionToNoSymbols is true.
155 DynamicUpdateMapEntry entry
= updatedActivity
.MapEntry
;
158 if (!entry
.IsRemoval
&& entry
.HasEnvironmentUpdates
&& entry
.EnvironmentUpdateMap
.IsAdditionToNoSymbols
)
160 int[] noSymbolAddActivityIDArray
= currentQIDBuilder
.ToArray();
161 noSymbolAddActivityIDArray
[noSymbolAddActivityIDArray
.Length
- 1] = entry
.OldActivityId
;
162 QualifiedId noSymbolAddActivityQID
= new QualifiedId(noSymbolAddActivityIDArray
);
163 DynamicUpdateMap
.UpdatedActivity noSymbolAddUpdatedActivity
= rootUpdateMap
.GetUpdatedActivity(noSymbolAddActivityQID
, rootIdSpace
);
165 AddBlockingActivity(ref updateErrors
, noSymbolAddUpdatedActivity
, noSymbolAddActivityQID
, SR
.VariableOrArgumentAdditionToReferencedEnvironmentNoDUSupported
, null);
169 entry
= entry
.Parent
;
170 } while (entry
!= null);
178 private static bool IsInvalidEnvironmentUpdate(InstanceList instanceList
, DynamicUpdateMap
.UpdatedActivity updatedActivity
, ref Collection
<ActivityBlockingUpdate
> updateErrors
)
180 if (updatedActivity
.MapEntry
== null || !updatedActivity
.MapEntry
.HasEnvironmentUpdates
)
185 for (int j
= 0; j
< instanceList
.Count
; j
++)
187 ActivityInstance activityInstance
= instanceList
[j
] as ActivityInstance
;
188 if (activityInstance
!= null)
191 if (activityInstance
.SubState
== ActivityInstance
.Substate
.ResolvingVariables
)
193 // if the entry has Environment update to do when the instance is in the middle of resolving variable, it is an error.
194 error
= SR
.CannotUpdateEnvironmentInTheMiddleOfResolvingVariables
;
196 else if (activityInstance
.SubState
== ActivityInstance
.Substate
.ResolvingArguments
)
198 // if the entry has Environment update to do when the instance is in the middle of resolving arguments, it is an error.
199 error
= SR
.CannotUpdateEnvironmentInTheMiddleOfResolvingArguments
;
204 AddBlockingActivity(ref updateErrors
, updatedActivity
, new QualifiedId(instanceList
.ActivityId
), error
, activityInstance
.Id
);
210 LocationEnvironment environment
= instanceList
[j
] as LocationEnvironment
;
211 if (environment
!= null)
214 // environment that is referenced by a secondary root
215 // Adding a variable or argument that requires expression scheduling to this instanceless environment is not allowed.
217 List
<int> dummyIndexes
;
218 EnvironmentUpdateMap envMap
= updatedActivity
.MapEntry
.EnvironmentUpdateMap
;
220 if ((envMap
.HasVariableEntries
&& TryGatherSchedulableExpressions(envMap
.VariableEntries
, out dummyIndexes
)) ||
221 (envMap
.HasPrivateVariableEntries
&& TryGatherSchedulableExpressions(envMap
.PrivateVariableEntries
, out dummyIndexes
)) ||
222 (envMap
.HasArgumentEntries
&& TryGatherSchedulableExpressions(envMap
.ArgumentEntries
, out dummyIndexes
)))
224 AddBlockingActivity(ref updateErrors
, updatedActivity
, new QualifiedId(instanceList
.ActivityId
), SR
.VariableOrArgumentAdditionToReferencedEnvironmentNoDUSupported
, null);
234 private static bool IsRemovalOrRTUpdateBlockedOrBlockedByUser(DynamicUpdateMap
.UpdatedActivity updatedActivity
, QualifiedId oldQualifiedId
, out string error
)
237 if (updatedActivity
.MapEntry
.IsRemoval
)
241 error
= SR
.CannotRemoveExecutingActivityUpdateError(oldQualifiedId
, updatedActivity
.MapEntry
.DisplayName
);
243 else if (updatedActivity
.MapEntry
.IsRuntimeUpdateBlocked
)
245 error
= updatedActivity
.MapEntry
.BlockReasonMessage
?? UpdateBlockedReasonMessages
.Get(updatedActivity
.MapEntry
.BlockReason
);
247 else if (updatedActivity
.MapEntry
.IsUpdateBlockedByUpdateAuthor
)
249 error
= SR
.BlockedUpdateInsideActivityUpdateByUserError
;
252 return error
!= null;
255 // targetDefinition argument is optional.
256 private IList
<InstanceListNeedingUpdate
> GetInstanceListsNeedingUpdate(DynamicUpdateMap updateMap
, Activity targetDefinition
, List
<ActivityInstance
> secondaryRootInstances
, ref Collection
<ActivityBlockingUpdate
> updateErrors
)
258 IList
<InstanceListNeedingUpdate
> instanceListsToUpdate
= new List
<InstanceListNeedingUpdate
>();
259 if (this.rawDeserializedLists
== null)
261 // This instance doesn't have any active instances (it is complete).
262 return instanceListsToUpdate
;
265 IdSpace rootIdSpace
= null;
266 if (targetDefinition
!= null)
268 rootIdSpace
= targetDefinition
.MemberOf
;
271 for (int i
= 0; i
< this.rawDeserializedLists
.Length
; i
++)
273 InstanceList list
= this.rawDeserializedLists
[i
];
274 QualifiedId oldQualifiedId
= new QualifiedId(list
.ActivityId
);
276 if (updateMap
.IsImplementationAsRoot
)
278 int[] oldIdArray
= oldQualifiedId
.AsIDArray();
279 if (oldIdArray
.Length
== 1 && oldIdArray
[0] != 1)
281 throw FxTrace
.Exception
.AsError(new InstanceUpdateException(SR
.InvalidImplementationAsWorkflowRootForRuntimeState
));
286 InstanceListNeedingUpdate update
;
287 DynamicUpdateMap
.UpdatedActivity updatedActivity
= updateMap
.GetUpdatedActivity(oldQualifiedId
, rootIdSpace
);
289 if (CanCompensationOrConfirmationHandlerReferenceAddedSymbols(list
, updateMap
, rootIdSpace
, secondaryRootInstances
, ref updateErrors
))
293 else if (updatedActivity
.MapEntry
== null)
295 if (updatedActivity
.IdChanged
)
297 // this newQualifiedId is the new id for those InstanceLists whose IDs shifted by their parents' ID change
298 update
= new InstanceListNeedingUpdate
301 NewId
= updatedActivity
.NewId
306 // nothing changed, no map, no mapEntry
307 update
= new InstanceListNeedingUpdate
314 else if (updatedActivity
.MapEntry
.IsParentRemovedOrBlocked
)
318 else if (IsRemovalOrRTUpdateBlockedOrBlockedByUser(updatedActivity
, oldQualifiedId
, out error
))
320 string instanceId
= null;
321 for (int j
= 0; j
< list
.Count
; j
++)
323 ActivityInstance activityInstance
= list
[j
] as ActivityInstance
;
324 if (activityInstance
!= null)
326 instanceId
= activityInstance
.Id
;
330 AddBlockingActivity(ref updateErrors
, updatedActivity
, oldQualifiedId
, error
, instanceId
);
334 else if (IsInvalidEnvironmentUpdate(list
, updatedActivity
, ref updateErrors
))
340 // no validation error for this InstanceList
341 // add it to the list of InstanceLists to be updated
342 update
= new InstanceListNeedingUpdate
345 NewId
= updatedActivity
.NewId
,
346 UpdateMap
= updatedActivity
.Map
,
347 MapEntry
= updatedActivity
.MapEntry
,
348 NewActivity
= updatedActivity
.NewActivity
354 update
.OriginalId
= list
.ActivityId
;
355 instanceListsToUpdate
.Add(update
);
359 return instanceListsToUpdate
;
362 public void UpdateRawInstance(DynamicUpdateMap updateMap
, Activity targetDefinition
, List
<ActivityInstance
> secondaryRootInstances
, ref Collection
<ActivityBlockingUpdate
> updateErrors
)
364 this.updateList
= GetInstanceListsNeedingUpdate(updateMap
, targetDefinition
, secondaryRootInstances
, ref updateErrors
);
365 if (updateErrors
!= null && updateErrors
.Count
> 0)
368 // there is no need to proceed to updating the instances
372 // if UpdateType is either MapEntryExists or ParentIdShiftOnly,
373 // update the ActivityIDs and update Environments
374 // also, update the ImplementationVersion.
375 foreach (InstanceListNeedingUpdate update
in this.updateList
)
377 Fx
.Assert(update
.InstanceList
!= null, "update.InstanceList must not be null.");
379 if (update
.NothingChanged
)
384 Fx
.Assert(update
.NewId
!= null, "update.NewId must not be null.");
386 InstanceList instanceList
= update
.InstanceList
;
387 instanceList
.ActivityId
= update
.NewId
.AsByteArray();
389 if (update
.ParentIdShiftOnly
)
391 // this InstanceList must have been one of those whose IDs shifted by their parent's ID change,
392 // but no involvement in DU.
396 bool implementationVersionUpdateNeeded
= false;
397 if (update
.MapEntry
.ImplementationUpdateMap
!= null)
399 implementationVersionUpdateNeeded
= true;
402 if (update
.MapEntry
.HasEnvironmentUpdates
)
404 // update LocationEnvironemnt
406 Fx
.Assert(update
.NewActivity
!= null, "TryGetUpdateMapEntryFromRootMap should have thrown if it couldn't map to an activity");
407 instanceList
.UpdateEnvironments(update
.MapEntry
.EnvironmentUpdateMap
, update
.NewActivity
);
410 for (int i
= 0; i
< instanceList
.Count
; i
++)
412 ActivityInstance activityInstance
= instanceList
[i
] as ActivityInstance
;
414 if (implementationVersionUpdateNeeded
)
416 activityInstance
.ImplementationVersion
= update
.NewActivity
.ImplementationVersion
;
422 private static bool TryGatherSchedulableExpressions(IList
<EnvironmentUpdateMapEntry
> entries
, out List
<int> addedLocationReferenceIndexes
)
424 addedLocationReferenceIndexes
= null;
426 for (int i
= 0; i
< entries
.Count
; i
++)
428 EnvironmentUpdateMapEntry entry
= entries
[i
];
429 if (entry
.IsAddition
)
431 if (addedLocationReferenceIndexes
== null)
433 addedLocationReferenceIndexes
= new List
<int>();
435 addedLocationReferenceIndexes
.Add(entry
.NewOffset
);
439 return addedLocationReferenceIndexes
!= null;
442 // this is called after all instances have been loaded and fixedup
443 public void UpdateInstanceByActivityParticipation(ActivityExecutor activityExecutor
, DynamicUpdateMap rootMap
, ref Collection
<ActivityBlockingUpdate
> updateErrors
)
445 foreach (InstanceListNeedingUpdate participant
in this.updateList
)
447 if (participant
.NothingChanged
|| participant
.ParentIdShiftOnly
)
449 Fx
.Assert(participant
.UpdateMap
== null && participant
.MapEntry
== null, "UpdateMap and MapEntry must be null if we are here.");
451 // create a temporary NoChanges UpdateMap as well as a temporary no change MapEntry
452 // so that we can create a NativeActivityUpdateContext object in order to invoke UpdateInstance() on an activity which
453 // doesn't have a corresponding map and an map entry.
454 // The scenario enabled here is scheduling a newly added reference branch to a Parallel inside an activity's implementation.
455 participant
.UpdateMap
= DynamicUpdateMap
.DummyMap
;
456 participant
.MapEntry
= DynamicUpdateMapEntry
.DummyMapEntry
;
459 // now let activities participate in update
460 for (int i
= 0; i
< participant
.InstanceList
.Count
; i
++)
462 ActivityInstance instance
= participant
.InstanceList
[i
] as ActivityInstance
;
463 if (instance
== null)
468 IInstanceUpdatable activity
= instance
.Activity
as IInstanceUpdatable
;
469 if (activity
!= null && instance
.SubState
== ActivityInstance
.Substate
.Executing
)
471 NativeActivityUpdateContext updateContext
= new NativeActivityUpdateContext(this, activityExecutor
, instance
, participant
.UpdateMap
, participant
.MapEntry
, rootMap
);
474 activity
.InternalUpdateInstance(updateContext
);
476 if (updateContext
.IsUpdateDisallowed
)
478 ActivityBlockingUpdate
.AddBlockingActivity(ref updateErrors
, instance
.Activity
, new QualifiedId(participant
.OriginalId
).ToString(), updateContext
.DisallowedReason
, instance
.Id
);
489 throw FxTrace
.Exception
.AsError(new InstanceUpdateException(SR
.NativeActivityUpdateInstanceThrewException(e
.Message
), e
));
493 updateContext
.Dispose();
499 // Schedule evaluation of newly added arguments and newly added variables.
500 // This needs to happen after all the invokations of UpdateInstance above, so that newly
501 // added arguments and newly added variables get evaluated before any newly added activities get executed.
502 // We iterate the list in reverse so that parents are always scheduled after (and thus
503 // execute before) their children, which may depend on the parents.
504 for (int i
= this.updateList
.Count
- 1; i
>= 0; i
--)
506 InstanceListNeedingUpdate participant
= this.updateList
[i
];
508 if (!participant
.MapEntryExists
)
510 // if the given InstanceList has no map entry,
511 // then there is no new LocationReferences to resolve.
515 Fx
.Assert(participant
.MapEntry
!= null, "MapEntry must be non-null here.");
516 if (!participant
.MapEntry
.HasEnvironmentUpdates
)
518 // if there is no environment updates for this MapEntry,
519 // then there is no new LocationReferences to resolve.
523 for (int j
= 0; j
< participant
.InstanceList
.Count
; j
++)
525 ActivityInstance instance
= participant
.InstanceList
[j
] as ActivityInstance
;
526 if (instance
== null || instance
.SubState
!= ActivityInstance
.Substate
.Executing
)
528 // if the given ActivityInstance is not in Substate.Executing,
529 // then, do not try to resolve new LocationReferences
533 List
<int> addedArgumentIndexes
;
534 List
<int> addedVariableIndexes
;
535 List
<int> addedPrivateVariableIndexes
;
537 EnvironmentUpdateMap envMap
= participant
.MapEntry
.EnvironmentUpdateMap
;
539 if (envMap
.HasVariableEntries
&& TryGatherSchedulableExpressions(envMap
.VariableEntries
, out addedVariableIndexes
))
541 // schedule added variable default expressions
542 instance
.ResolveNewVariableDefaultsDuringDynamicUpdate(activityExecutor
, addedVariableIndexes
, false);
545 if (envMap
.HasPrivateVariableEntries
&& TryGatherSchedulableExpressions(envMap
.PrivateVariableEntries
, out addedPrivateVariableIndexes
))
547 // schedule added private variable default expressions
548 // HasPrivateMemberChanged() check disallows addition of private variable default that offsets the private IdSpace,
549 // However, the added private variable default expression can be an imported activity, which has no affect on the private IdSpace.
550 // For such case, we want to be able to schedule the imported default expressions here.
551 instance
.ResolveNewVariableDefaultsDuringDynamicUpdate(activityExecutor
, addedPrivateVariableIndexes
, true);
554 if (envMap
.HasArgumentEntries
&& TryGatherSchedulableExpressions(envMap
.ArgumentEntries
, out addedArgumentIndexes
))
556 // schedule added arguments
557 instance
.ResolveNewArgumentsDuringDynamicUpdate(activityExecutor
, addedArgumentIndexes
);
563 public void AddEntry(IActivityReference reference
, bool skipIfDuplicate
)
565 Activity activity
= reference
.Activity
;
567 InstanceList mappedInstances
;
568 if (this.InstanceMapping
.TryGetValue(activity
, out mappedInstances
))
570 mappedInstances
.Add(reference
, skipIfDuplicate
);
574 this.InstanceMapping
.Add(activity
, new InstanceList(reference
));
578 public void AddEntry(IActivityReference reference
)
580 AddEntry(reference
, false);
583 public void LoadActivityTree(Activity rootActivity
, ActivityInstance rootInstance
, List
<ActivityInstance
> secondaryRootInstances
, ActivityExecutor executor
)
585 Fx
.Assert(this.rawDeserializedLists
!= null, "We should always have deserialized some lists.");
587 this.instanceMapping
= new Dictionary
<Activity
, InstanceList
>(this.rawDeserializedLists
.Length
);
589 for (int i
= 0; i
< this.rawDeserializedLists
.Length
; i
++)
591 InstanceList list
= this.rawDeserializedLists
[i
];
593 if (!QualifiedId
.TryGetElementFromRoot(rootActivity
, list
.ActivityId
, out activity
))
595 throw FxTrace
.Exception
.AsError(new InvalidOperationException(SR
.ActivityInstanceFixupFailed
));
597 this.instanceMapping
.Add(activity
, list
);
598 list
.Load(activity
, this);
601 // We need to null this out once we've recreated the dictionary to avoid
602 // having out of sync data
603 this.rawDeserializedLists
= null;
605 // then walk our instance list, fixup parent references, and perform basic validation
606 Func
<ActivityInstance
, ActivityExecutor
, bool> processInstanceCallback
= new Func
<ActivityInstance
, ActivityExecutor
, bool>(OnActivityInstanceLoaded
);
608 rootInstance
.FixupInstance(null, this, executor
);
609 ActivityUtilities
.ProcessActivityInstanceTree(rootInstance
, executor
, processInstanceCallback
);
611 if (secondaryRootInstances
!= null)
613 foreach (ActivityInstance instance
in secondaryRootInstances
)
615 instance
.FixupInstance(null, this, executor
);
616 ActivityUtilities
.ProcessActivityInstanceTree(instance
, executor
, processInstanceCallback
);
621 bool OnActivityInstanceLoaded(ActivityInstance activityInstance
, ActivityExecutor executor
)
623 return activityInstance
.TryFixupChildren(this, executor
);
626 public bool RemoveEntry(IActivityReference reference
)
628 if (this.instanceMapping
== null)
633 Activity activity
= reference
.Activity
;
635 InstanceList mappedInstances
;
636 if (!this.InstanceMapping
.TryGetValue(activity
, out mappedInstances
))
641 if (mappedInstances
.Count
== 1)
643 this.InstanceMapping
.Remove(activity
);
647 mappedInstances
.Remove(reference
);
654 internal class InstanceList
: HybridCollection
<IActivityReference
>
656 public InstanceList(IActivityReference reference
)
661 [SuppressMessage(FxCop
.Category
.Performance
, FxCop
.Rule
.AvoidUncalledPrivateCode
,
662 Justification
= "Called by serialization")]
664 public byte[] ActivityId
671 [SuppressMessage(FxCop
.Category
.Usage
, FxCop
.Rule
.ReviewUnusedParameters
)]
672 [SuppressMessage(FxCop
.Category
.Usage
, "CA2238:ImplementSerializationMethodsCorrectly",
673 Justification
= "Needs to be internal for serialization in partial trust. We have set InternalsVisibleTo(System.Runtime.Serialization) to allow this.")]
674 internal void OnSerializing(StreamingContext context
)
679 public void Add(IActivityReference reference
, bool skipIfDuplicate
)
681 Fx
.Assert(this.Count
>= 1, "instance list should never be empty when we call Add");
685 if (base.SingleItem
!= null)
687 if (base.SingleItem
== reference
)
694 if (base.MultipleItems
.Contains(reference
))
704 public void Load(Activity activity
, ActivityInstanceMap instanceMap
)
706 Fx
.Assert(this.Count
>= 1, "instance list should never be empty on load");
707 if (base.SingleItem
!= null)
709 base.SingleItem
.Load(activity
, instanceMap
);
713 for (int i
= 0; i
< base.MultipleItems
.Count
; i
++)
715 base.MultipleItems
[i
].Load(activity
, instanceMap
);
720 public void UpdateEnvironments(EnvironmentUpdateMap map
, Activity activity
)
722 if (base.SingleItem
!= null)
724 IActivityReferenceWithEnvironment reference
= base.SingleItem
as IActivityReferenceWithEnvironment
;
725 if (reference
!= null)
727 reference
.UpdateEnvironment(map
, activity
);
732 for (int i
= 0; i
< base.MultipleItems
.Count
; i
++)
734 IActivityReferenceWithEnvironment reference
= base.MultipleItems
[i
] as IActivityReferenceWithEnvironment
;
735 if (reference
!= null)
737 reference
.UpdateEnvironment(map
, activity
);
745 public interface IActivityReference
747 Activity Activity { get; }
748 void Load(Activity activity
, ActivityInstanceMap instanceMap
);
751 public interface IActivityReferenceWithEnvironment
: IActivityReference
753 void UpdateEnvironment(EnvironmentUpdateMap map
, Activity activity
);
756 class InstanceListNeedingUpdate
758 // The list of IActivityReferences to be updated
759 public InstanceList InstanceList { get; set; }
761 public byte[] OriginalId { get; set; }
763 // The new ActivityId for these ActivityReferences.
764 public QualifiedId NewId { get; set; }
766 // The Map & MapEntry for this ActivityId, if there is one.
767 // Null if the activity's parent Id was updated, but not the activity itself,
768 // Or null if nothing changed.
769 public DynamicUpdateMap UpdateMap { get; set; }
770 public DynamicUpdateMapEntry MapEntry { get; set; }
772 // A pointer to this activity, in the new definition.
773 // Null if we don't have the definition loaded.
774 public Activity NewActivity { get; set; }
777 // the following three properties are mutual exlusive,
778 // meaning, one and only one of them evaluates to TRUE.
780 public bool NothingChanged
784 return this.MapEntry
== null && this.NewId
== null;
788 public bool MapEntryExists
792 return this.MapEntry
!= null;
796 public bool ParentIdShiftOnly
800 return this.MapEntry
== null && this.NewId
!= null;