Updates referencesource to .NET 4.7
[mono-project.git] / mcs / class / referencesource / System.Activities / System / Activities / Runtime / ActivityInstanceMap.cs
blobc4983a022b0aa6ce40d5476cd746b40ad98c3cc7
1 //-----------------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //-----------------------------------------------------------------------------
5 namespace System.Activities.Runtime
7 using System;
8 using System.Collections.Generic;
9 using System.Globalization;
10 using System.Diagnostics.CodeAnalysis;
11 using System.Runtime;
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
35 get
37 if (this.instanceMapping == null || this.instanceMapping.Count == 0)
39 return this.rawDeserializedLists;
41 else
43 InstanceList[] lists = new InstanceList[this.instanceMapping.Count];
44 int index = 0;
45 foreach (KeyValuePair<Activity, InstanceList> entry in this.instanceMapping)
47 entry.Value.ActivityId = entry.Key.QualifiedId.AsByteArray();
48 lists[index] = entry.Value;
49 index++;
52 return lists;
55 set
57 Fx.Assert(value != null, "We don't serialize the default value.");
59 this.rawDeserializedLists = value;
63 IDictionary<Activity, InstanceList> InstanceMapping
65 get
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);
82 else
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)
104 return true;
108 return false;
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))
118 continue;
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);
166 return true;
169 entry = entry.Parent;
170 } while (entry != null);
175 return false;
178 private static bool IsInvalidEnvironmentUpdate(InstanceList instanceList, DynamicUpdateMap.UpdatedActivity updatedActivity, ref Collection<ActivityBlockingUpdate> updateErrors)
180 if (updatedActivity.MapEntry == null || !updatedActivity.MapEntry.HasEnvironmentUpdates)
182 return false;
185 for (int j = 0; j < instanceList.Count; j++)
187 ActivityInstance activityInstance = instanceList[j] as ActivityInstance;
188 if (activityInstance != null)
190 string error = 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;
202 if (error != null)
204 AddBlockingActivity(ref updateErrors, updatedActivity, new QualifiedId(instanceList.ActivityId), error, activityInstance.Id);
205 return true;
208 else
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);
225 return true;
231 return false;
234 private static bool IsRemovalOrRTUpdateBlockedOrBlockedByUser(DynamicUpdateMap.UpdatedActivity updatedActivity, QualifiedId oldQualifiedId, out string error)
236 error = null;
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));
285 string error;
286 InstanceListNeedingUpdate update;
287 DynamicUpdateMap.UpdatedActivity updatedActivity = updateMap.GetUpdatedActivity(oldQualifiedId, rootIdSpace);
289 if (CanCompensationOrConfirmationHandlerReferenceAddedSymbols(list, updateMap, rootIdSpace, secondaryRootInstances, ref updateErrors))
291 update = null;
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
300 InstanceList = list,
301 NewId = updatedActivity.NewId
304 else
306 // nothing changed, no map, no mapEntry
307 update = new InstanceListNeedingUpdate
309 InstanceList = list,
310 NewId = null,
314 else if (updatedActivity.MapEntry.IsParentRemovedOrBlocked)
316 update = null;
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;
327 break;
330 AddBlockingActivity(ref updateErrors, updatedActivity, oldQualifiedId, error, instanceId);
332 update = null;
334 else if (IsInvalidEnvironmentUpdate(list, updatedActivity, ref updateErrors))
336 update = null;
338 else
340 // no validation error for this InstanceList
341 // add it to the list of InstanceLists to be updated
342 update = new InstanceListNeedingUpdate
344 InstanceList = list,
345 NewId = updatedActivity.NewId,
346 UpdateMap = updatedActivity.Map,
347 MapEntry = updatedActivity.MapEntry,
348 NewActivity = updatedActivity.NewActivity
352 if (update != null)
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)
367 // error found.
368 // there is no need to proceed to updating the instances
369 return;
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)
381 continue;
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.
393 continue;
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)
465 continue;
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);
479 continue;
482 catch (Exception e)
484 if (Fx.IsFatal(e))
486 throw;
489 throw FxTrace.Exception.AsError(new InstanceUpdateException(SR.NativeActivityUpdateInstanceThrewException(e.Message), e));
491 finally
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.
512 continue;
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.
520 continue;
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
530 continue;
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);
572 else
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];
592 Activity activity;
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)
630 return false;
633 Activity activity = reference.Activity;
635 InstanceList mappedInstances;
636 if (!this.InstanceMapping.TryGetValue(activity, out mappedInstances))
638 return false;
641 if (mappedInstances.Count == 1)
643 this.InstanceMapping.Remove(activity);
645 else
647 mappedInstances.Remove(reference);
650 return true;
653 [DataContract]
654 internal class InstanceList : HybridCollection<IActivityReference>
656 public InstanceList(IActivityReference reference)
657 : base(reference)
661 [SuppressMessage(FxCop.Category.Performance, FxCop.Rule.AvoidUncalledPrivateCode,
662 Justification = "Called by serialization")]
663 [DataMember]
664 public byte[] ActivityId
666 get;
667 set;
670 [OnSerializing]
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)
676 base.Compress();
679 public void Add(IActivityReference reference, bool skipIfDuplicate)
681 Fx.Assert(this.Count >= 1, "instance list should never be empty when we call Add");
683 if (skipIfDuplicate)
685 if (base.SingleItem != null)
687 if (base.SingleItem == reference)
689 return;
692 else
694 if (base.MultipleItems.Contains(reference))
696 return;
701 Add(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);
711 else
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);
730 else
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;