|
//-----------------------------------------------------------------------------
// 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
// having out of sync data
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;
}
}
}
}
}
|