1 2 using System; 3 using System.Collections; 4 using System.Collections.Generic; 5 using System.Collections.ObjectModel; 6 using System.Text; 7 using System.Xml; 8 using System.Xml.Schema; 9 using System.IO; 10 using System.Reflection; 11 using System.Diagnostics; 12 using System.Runtime.Serialization; 13 using System.Security.Permissions; 14 using System.Globalization; 15 16 //using System.Workflow.Activities; 17 using System.Workflow.ComponentModel; 18 using System.Workflow.Runtime; 19 using System.Workflow.Runtime.Hosting; 20 using Hosting = System.Workflow.Runtime.Hosting; 21 using System.Workflow.Runtime.Tracking; 22 23 24 namespace System.Workflow.Runtime 25 { 26 /// <summary> 27 /// RTTrackingProfile contains functionality specific to the runtime such as 28 /// trackpoint and location matching and caching, cloning, handling dynamic updates... 29 /// </summary> 30 internal class RTTrackingProfile : ICloneable // ICloneable is deprecated 31 { 32 #region Private Data Members 33 // 34 // Client defined profile 35 private TrackingProfile _profile = null; 36 // 37 // Type of the workflow that this profile is associated to 38 private Type _workflowType = null; 39 private Type _serviceType = null; 40 // 41 // List of qualified ids and the trackpoints that declared themselves as matches during static examination 42 private Dictionary<string, List<ActivityTrackPointCacheItem>> _activities = new Dictionary<string, List<ActivityTrackPointCacheItem>>(); 43 private List<string> _activitiesIgnore = new List<string>(); 44 45 private Dictionary<string, List<UserTrackPoint>> _user = new Dictionary<string, List<UserTrackPoint>>(); 46 private List<string> _userIgnore = new List<string>(); 47 // 48 // Indicates that the RTTrackingProfile instance is private and is safe to modify for a specific instance 49 private bool _isPrivate = false; 50 // 51 // Indicates if a dynamic update is in-flight 52 private bool _pendingWorkflowChange = false; 53 // 54 // The changes for a dynamic update 55 private IList<WorkflowChangeAction> _pendingChanges = null; 56 // 57 // Activities (including those that are being added) can start executing while a dynamic update is pending 58 // These cannot be added to the main cache until the update succeeds because the update might roll back. 59 // However since we have to search for matching track points we might as well save that work. 60 // This list will be copied into the main cache if the dynamic update completes successfully 61 private Dictionary<string, List<ActivityTrackPointCacheItem>> _dynamicActivities = null; 62 private List<string> _dynamicActivitiesIgnore = null; 63 64 private Dictionary<string, List<UserTrackPoint>> _dynamicUser = null; 65 private List<string> _dynamicUserIgnore = null; 66 67 #endregion 68 69 #region Constructors 70 /// <summary> 71 /// Default constructor 72 /// </summary> RTTrackingProfile()73 protected RTTrackingProfile() 74 { 75 } 76 /// <summary> 77 /// Primary constructor 78 /// </summary> 79 /// <param name="root"></param> 80 /// <param name="workflowType"></param> 81 /// <param name="profile"></param> RTTrackingProfile(TrackingProfile profile, Activity root, Type serviceType)82 internal RTTrackingProfile(TrackingProfile profile, Activity root, Type serviceType) 83 { 84 if (null == profile) 85 throw new ArgumentNullException("profile"); 86 if (null == root) 87 throw new ArgumentNullException("root"); 88 if (null == serviceType) 89 throw new ArgumentNullException("serviceType"); 90 91 _workflowType = root.GetType(); 92 _serviceType = serviceType; 93 // 94 // "Clone" a private copy in case the tracking service holds a reference to 95 // the profile it gave us and attempts to modify it at a later point 96 TrackingProfileSerializer tps = new TrackingProfileSerializer(); 97 98 StringWriter writer = new StringWriter(System.Globalization.CultureInfo.InvariantCulture); 99 StringReader reader = null; 100 101 TrackingProfile privateProfile = null; 102 103 try 104 { 105 // 106 // Let exceptions bubble back to the tracking service - 107 // the profile must be valid per the schema. 108 tps.Serialize(writer, profile); 109 reader = new StringReader(writer.ToString()); 110 privateProfile = tps.Deserialize(reader); 111 } 112 finally 113 { 114 if (null != reader) 115 reader.Close(); 116 117 if (null != writer) 118 writer.Close(); 119 } 120 _profile = privateProfile; 121 122 CheckAllActivities((Activity)root); 123 } 124 125 /// <summary> 126 /// Constructor used for cloning. 127 /// </summary> 128 /// <param name="runtimeProfile">RTTrackingProfile to clone</param> 129 /// <remarks>All members are shallow copied! Use MakePrivate to deep copy after cloning.</remarks> RTTrackingProfile(RTTrackingProfile runtimeProfile)130 private RTTrackingProfile(RTTrackingProfile runtimeProfile) 131 { 132 // 133 // Shallow copy 134 _profile = runtimeProfile._profile; 135 _isPrivate = runtimeProfile._isPrivate; 136 _pendingChanges = runtimeProfile._pendingChanges; 137 _pendingWorkflowChange = runtimeProfile._pendingWorkflowChange; 138 _workflowType = runtimeProfile._workflowType; 139 // 140 // Deep copy the cache. Items in the cache can 141 // be shared but the cache themselves cannot as they may be modified 142 // 143 // Activity match and ignore cache 144 _activities = new Dictionary<string, List<ActivityTrackPointCacheItem>>(runtimeProfile._activities.Count); 145 foreach (KeyValuePair<string, List<ActivityTrackPointCacheItem>> kvp in runtimeProfile._activities) 146 _activities.Add(kvp.Key, runtimeProfile._activities[kvp.Key]); 147 148 _activitiesIgnore = new List<string>(runtimeProfile._activitiesIgnore); 149 // 150 // Pending dynamic update activity match and ignore cache 151 if (null != runtimeProfile._dynamicActivities) 152 { 153 _dynamicActivities = new Dictionary<string, List<ActivityTrackPointCacheItem>>(runtimeProfile._dynamicActivities.Count); 154 foreach (KeyValuePair<string, List<ActivityTrackPointCacheItem>> kvp in runtimeProfile._dynamicActivities) 155 _dynamicActivities.Add(kvp.Key, runtimeProfile._dynamicActivities[kvp.Key]); 156 } 157 158 if (null != runtimeProfile._dynamicActivitiesIgnore) 159 _dynamicActivitiesIgnore = new List<string>(runtimeProfile._dynamicActivitiesIgnore); 160 // 161 // User event match and ignore cache 162 _user = new Dictionary<string, List<UserTrackPoint>>(runtimeProfile._user.Count); 163 foreach (KeyValuePair<string, List<UserTrackPoint>> kvp in runtimeProfile._user) 164 _user.Add(kvp.Key, runtimeProfile._user[kvp.Key]); 165 166 _userIgnore = new List<string>(runtimeProfile._userIgnore); 167 // 168 // Pending dynamic update activity match and ignore cache 169 if (null != runtimeProfile._dynamicUser) 170 { 171 _dynamicUser = new Dictionary<string, List<UserTrackPoint>>(runtimeProfile._dynamicUser.Count); 172 foreach (KeyValuePair<string, List<UserTrackPoint>> kvp in runtimeProfile._dynamicUser) 173 _dynamicUser.Add(kvp.Key, kvp.Value); 174 } 175 176 if (null != runtimeProfile._dynamicUserIgnore) 177 _dynamicUserIgnore = new List<string>(runtimeProfile._dynamicUserIgnore); 178 } 179 180 #endregion 181 182 #region Properties 183 /// <summary> 184 /// Indicates if the profile is specific to an individual instance. 185 /// </summary> 186 internal bool IsPrivate 187 { 188 get { return _isPrivate; } 189 set 190 { 191 if (!(value) && (_isPrivate)) 192 throw new InvalidOperationException(ExecutionStringManager.CannotResetIsPrivate); 193 194 _isPrivate = value; 195 } 196 } 197 /// <summary> 198 /// Type of workflow to which this profile is associated 199 /// </summary> 200 internal Type WorkflowType 201 { 202 get { return _workflowType; } 203 } 204 205 /// <summary> 206 /// Version of the profile 207 /// </summary> 208 internal Version Version 209 { 210 get { return _profile.Version; } 211 } 212 213 #endregion 214 215 #region Internal Methods for Listeners 216 TryTrackActivityEvent(Activity activity, ActivityExecutionStatus status, IServiceProvider provider, ActivityTrackingRecord record)217 internal bool TryTrackActivityEvent(Activity activity, ActivityExecutionStatus status, IServiceProvider provider, ActivityTrackingRecord record) 218 { 219 List<ActivityTrackPointCacheItem> points; 220 // 221 // Check the match caches. 222 if (TryGetCacheItems(activity, out points)) 223 { 224 bool ret = false; 225 foreach (ActivityTrackPointCacheItem item in points) 226 { 227 if (item.HasLocationConditions) 228 { 229 if (!item.Point.IsMatch(activity, status)) 230 continue; 231 } 232 233 if (item.Events.Contains(status)) 234 { 235 ret = true; 236 item.Point.Track(activity, provider, record.Body); 237 record.Annotations.AddRange(item.Point.Annotations); 238 } 239 } 240 return ret; 241 } 242 return false; 243 } 244 TryTrackUserEvent(Activity activity, string keyName, object argument, WorkflowExecutor exec, UserTrackingRecord record)245 internal bool TryTrackUserEvent(Activity activity, string keyName, object argument, WorkflowExecutor exec, UserTrackingRecord record) 246 { 247 List<UserTrackPoint> points; 248 if (TryGetCacheItems(activity, out points)) 249 { 250 bool ret = false; 251 foreach (UserTrackPoint point in points) 252 { 253 if (point.IsMatch(activity, keyName, argument)) 254 { 255 ret = true; 256 point.Track(activity, argument, exec, record.Body); 257 record.Annotations.AddRange(point.Annotations); 258 } 259 } 260 return ret; 261 } 262 return false; 263 } 264 TryTrackInstanceEvent(TrackingWorkflowEvent status, WorkflowTrackingRecord record)265 internal bool TryTrackInstanceEvent(TrackingWorkflowEvent status, WorkflowTrackingRecord record) 266 { 267 bool track = false; 268 foreach (WorkflowTrackPoint point in _profile.WorkflowTrackPoints) 269 { 270 if (point.IsMatch(status)) 271 { 272 record.Annotations.AddRange(point.Annotations); 273 track = true; 274 } 275 } 276 return track; 277 } 278 279 280 /// <summary> 281 /// Called by TrackingListener to determine if a subscription is needed for an activity. 282 /// Also used as an entry point for dynamically building cache entries for dynamically added activities. 283 /// </summary> 284 /// <param name="activity"></param> 285 /// <param name="exec"></param> 286 /// <returns></returns> ActivitySubscriptionNeeded(Activity activity)287 internal bool ActivitySubscriptionNeeded(Activity activity) 288 { 289 List<ActivityTrackPointCacheItem> points = null; 290 if ((!_pendingWorkflowChange) || ((_pendingWorkflowChange) && (!IsPendingUpdateActivity(activity, true)))) 291 { 292 // 293 // A dynamic update is not in progress or 294 // the activity is not part of the dynamic update. 295 // The main cache has all matching track points 296 // 297 // 298 bool retry = true; 299 while (retry) 300 { 301 if (_activitiesIgnore.Contains(activity.QualifiedName)) 302 return false; 303 304 if (_activities.TryGetValue(activity.QualifiedName, out points)) 305 return true; 306 else 307 // 308 // This activity isn't in either cache, look it up in the profile and add to cache 309 CheckActivity(activity); 310 } 311 return false; 312 } 313 else 314 { 315 // 316 // Dynamic update is in progress and this activity is being added as part of the update 317 // Search the profile for matching track points and add them to the dynamic cache 318 // (copied to the main cache at the successful completion of the update) 319 // Don't go through CheckActivity because that adds to the main cache 320 List<UserTrackPoint> user = null; 321 if (CreateCacheItems(activity, out user)) 322 CacheInsertUpdatePending(activity.QualifiedName, user); 323 else 324 _dynamicUserIgnore.Add(activity.QualifiedName); 325 326 if (CreateCacheItems(activity, out points)) 327 { 328 CacheInsertUpdatePending(activity.QualifiedName, points); 329 return true; 330 } 331 else 332 { 333 _dynamicActivitiesIgnore.Add(activity.QualifiedName); 334 return false; 335 } 336 } 337 } 338 WorkflowChangeBegin(IList<WorkflowChangeAction> changeActions)339 public void WorkflowChangeBegin(IList<WorkflowChangeAction> changeActions) 340 { 341 Debug.Assert(!_pendingWorkflowChange, "_pendingWorkflowChange should be false."); 342 if (_pendingWorkflowChange) 343 throw new InvalidOperationException(ExecutionStringManager.DynamicUpdateIsNotPending); 344 345 if (!_isPrivate) 346 throw new InvalidOperationException(ExecutionStringManager.ProfileIsNotPrivate); 347 // 348 // Initialize the temp dictionary for activities that are spun up during the update process 349 // If the update succeeds we'll copy these to the main _subscriptions dictionary. 350 _dynamicActivities = new Dictionary<string, List<ActivityTrackPointCacheItem>>(); 351 _dynamicActivitiesIgnore = new List<string>(); 352 353 _dynamicUser = new Dictionary<string, List<UserTrackPoint>>(); 354 _dynamicUserIgnore = new List<string>(); 355 356 _pendingChanges = changeActions; 357 _pendingWorkflowChange = true; 358 } 359 WorkflowChangeCommit()360 public void WorkflowChangeCommit() 361 { 362 Debug.Assert(_pendingWorkflowChange, "Workflow change is not pending - no change to commit"); 363 364 if (!_pendingWorkflowChange) 365 return; 366 367 if (!_isPrivate) 368 throw new InvalidOperationException(ExecutionStringManager.ProfileIsNotPrivate); 369 // 370 // Remove items that have been deleted by this update 371 // Must do all removes first as there may be a new action 372 // with the same qid as a previous action that is being removed 373 if (null != _pendingChanges) 374 { 375 foreach (WorkflowChangeAction action in _pendingChanges) 376 { 377 if (action is RemovedActivityAction) 378 { 379 // 380 // Remove all references to this activity that might exist in our caches 381 string qId = ((RemovedActivityAction)action).OriginalRemovedActivity.QualifiedName; 382 _activities.Remove(qId); 383 _activitiesIgnore.Remove(qId); 384 _user.Remove(qId); 385 _userIgnore.Remove(qId); 386 } 387 } 388 } 389 // 390 // Copy any pending cache items to the regular activity track point cache 391 if ((null != _dynamicActivities) && (_dynamicActivities.Count > 0)) 392 foreach (KeyValuePair<string, List<ActivityTrackPointCacheItem>> kvp in _dynamicActivities) 393 _activities.Add(kvp.Key, kvp.Value); 394 395 if ((null != _dynamicActivitiesIgnore) && (_dynamicActivitiesIgnore.Count > 0)) 396 _activitiesIgnore.AddRange(_dynamicActivitiesIgnore); 397 398 if ((null != _dynamicUser) && (_dynamicUser.Count > 0)) 399 foreach (KeyValuePair<string, List<UserTrackPoint>> kvp in _dynamicUser) 400 _user.Add(kvp.Key, kvp.Value); 401 402 if ((null != _dynamicUserIgnore) && (_dynamicUserIgnore.Count > 0)) 403 _userIgnore.AddRange(_dynamicUserIgnore); 404 // 405 // All done, clean up 406 _dynamicActivities = null; 407 _dynamicActivitiesIgnore = null; 408 _dynamicUser = null; 409 _dynamicUserIgnore = null; 410 _pendingChanges = null; 411 _pendingWorkflowChange = false; 412 } 413 WorkflowChangeRollback()414 public void WorkflowChangeRollback() 415 { 416 // 417 // Just clean up, there isn't any work to rollback because 418 // any subscriptions that may have been added for a pending add activity 419 // won't ever be hit as the activities haven't been added to the tree. 420 _dynamicActivities = null; 421 _dynamicActivitiesIgnore = null; 422 _dynamicUser = null; 423 _dynamicUserIgnore = null; 424 _pendingChanges = null; 425 _pendingWorkflowChange = false; 426 } 427 428 #endregion 429 430 #region Private Cache Methods 431 432 /// <summary> 433 /// Create the static qualifiedid to trackpoint map 434 /// </summary> 435 /// <param name="root"></param> CheckAllActivities(Activity activity)436 private void CheckAllActivities(Activity activity) 437 { 438 CheckActivity((Activity)activity); 439 // 440 // Walk down the activity tree 441 // Use EnabledActivities to get invisible activities 442 // EnabledActivities will not return commented activities 443 if (activity is CompositeActivity) 444 foreach (Activity a in GetAllEnabledActivities((CompositeActivity)activity)) 445 CheckAllActivities(a); 446 } 447 448 /// <summary> 449 /// Recursively walk the activity tree and find all track points that match each activity 450 /// </summary> 451 /// <param name="activity"></param> CheckActivity(Activity activity)452 private void CheckActivity(Activity activity) 453 { 454 // 455 // Build caches of activity status change events 456 string qId = activity.QualifiedName; 457 List<ActivityTrackPointCacheItem> activities = null; 458 if (CreateCacheItems(activity, out activities)) 459 CacheInsert(qId, activities); 460 else 461 _activitiesIgnore.Add(qId); 462 463 // 464 // Build caches of user events 465 List<UserTrackPoint> user = null; 466 if (CreateCacheItems(activity, out user)) 467 CacheInsert(qId, user); 468 else 469 _userIgnore.Add(qId); 470 } 471 472 /// <summary> 473 /// Find all trackpoints that match an activity. 474 /// </summary> 475 /// <param name="activity">Activity for which to determine subscription needs</param> 476 /// <param name="includes">List to be populated with matching track points</param> 477 /// <returns>true if a subscription is needed; false if not</returns> CreateCacheItems(Activity activity, out List<ActivityTrackPointCacheItem> includes)478 private bool CreateCacheItems(Activity activity, out List<ActivityTrackPointCacheItem> includes) 479 { 480 includes = new List<ActivityTrackPointCacheItem>(); 481 // 482 // Check if we have any trackpoints that match this activity 483 foreach (ActivityTrackPoint point in _profile.ActivityTrackPoints) 484 { 485 List<ActivityExecutionStatus> events; 486 bool hasCondition = false; 487 if (point.IsMatch(activity, out events, out hasCondition)) 488 includes.Add(new ActivityTrackPointCacheItem(point, events, hasCondition)); 489 } 490 491 return (includes.Count > 0); 492 } 493 494 /// <summary> 495 /// Find all trackpoints that match user events for an activity. 496 /// </summary> 497 /// <param name="activity">Activity for which to determine subscription needs</param> 498 /// <param name="includes">List to be populated with matching track points</param> 499 /// <returns>true if a subscription is needed; false if not</returns> CreateCacheItems(Activity activity, out List<UserTrackPoint> includes)500 private bool CreateCacheItems(Activity activity, out List<UserTrackPoint> includes) 501 { 502 includes = new List<UserTrackPoint>(); 503 // 504 // Check if we have any trackpoints that match this activity 505 foreach (UserTrackPoint point in _profile.UserTrackPoints) 506 { 507 if (point.IsMatch(activity)) 508 includes.Add(point); 509 } 510 511 return (includes.Count > 0); 512 } 513 CacheInsert(string qualifiedID, List<ActivityTrackPointCacheItem> points)514 private void CacheInsert(string qualifiedID, List<ActivityTrackPointCacheItem> points) 515 { 516 // 517 // Check to make sure the item isn't in the dictionary 518 // If not add all track points 519 Debug.Assert(!_activities.ContainsKey(qualifiedID), "QualifiedName is already in the activities cache"); 520 if (_activities.ContainsKey(qualifiedID)) 521 throw new InvalidOperationException(ExecutionStringManager.RTProfileActCacheDupKey); 522 523 foreach (ActivityTrackPointCacheItem point in points) 524 CacheInsert(qualifiedID, point); 525 } 526 CacheInsert(string qualifiedID, List<UserTrackPoint> points)527 private void CacheInsert(string qualifiedID, List<UserTrackPoint> points) 528 { 529 // 530 // Check to make sure the item isn't in the dictionary 531 // If not add all track points 532 Debug.Assert(!_user.ContainsKey(qualifiedID), "QualifiedName is already in the user cache"); 533 if (_user.ContainsKey(qualifiedID)) 534 throw new InvalidOperationException(ExecutionStringManager.RTProfileActCacheDupKey); 535 536 foreach (UserTrackPoint point in points) 537 CacheInsert(qualifiedID, point); 538 } 539 CacheInsert(string qualifiedID, ActivityTrackPointCacheItem point)540 private void CacheInsert(string qualifiedID, ActivityTrackPointCacheItem point) 541 { 542 List<ActivityTrackPointCacheItem> points = null; 543 544 if (!_activities.TryGetValue(qualifiedID, out points)) 545 { 546 points = new List<ActivityTrackPointCacheItem>(); 547 _activities.Add(qualifiedID, points); 548 } 549 points.Add(point); 550 } 551 CacheInsert(string qualifiedID, UserTrackPoint point)552 private void CacheInsert(string qualifiedID, UserTrackPoint point) 553 { 554 List<UserTrackPoint> points = null; 555 556 if (!_user.TryGetValue(qualifiedID, out points)) 557 { 558 points = new List<UserTrackPoint>(); 559 _user.Add(qualifiedID, points); 560 } 561 points.Add(point); 562 } 563 CacheInsertUpdatePending(string qualifiedID, List<ActivityTrackPointCacheItem> points)564 private void CacheInsertUpdatePending(string qualifiedID, List<ActivityTrackPointCacheItem> points) 565 { 566 // 567 // The activity has been added during a pending dynamic change 568 // add it to a temporary lookup which will be copied to real cache 569 // when the dynamic update commits. 570 if ((!_isPrivate) || (!_pendingWorkflowChange)) 571 throw new InvalidOperationException(ExecutionStringManager.ProfileIsNotPrivate); 572 573 if (null == _dynamicActivities) 574 throw new InvalidOperationException(ExecutionStringManager.RTProfileDynamicActCacheIsNull); 575 576 List<ActivityTrackPointCacheItem> tmp = null; 577 if (!_dynamicActivities.TryGetValue(qualifiedID, out tmp)) 578 { 579 tmp = new List<ActivityTrackPointCacheItem>(); 580 _dynamicActivities.Add(qualifiedID, tmp); 581 } 582 583 foreach (ActivityTrackPointCacheItem point in points) 584 tmp.Add(point); 585 } 586 TryGetCacheItems(Activity activity, out List<ActivityTrackPointCacheItem> points)587 private bool TryGetCacheItems(Activity activity, out List<ActivityTrackPointCacheItem> points) 588 { 589 points = null; 590 if ((!_pendingWorkflowChange) || ((_pendingWorkflowChange) && (!IsPendingUpdateActivity(activity, true)))) 591 { 592 // 593 // A dynamic update is not in progress or this activity 594 // is not being added by the current dynamic update. 595 // The main cache holds all matching track points 596 return _activities.TryGetValue(activity.QualifiedName, out points); 597 } 598 else 599 { 600 // 601 // Dynamic update is in progress 602 return _dynamicActivities.TryGetValue(activity.QualifiedName, out points); 603 } 604 } 605 CacheInsertUpdatePending(string qualifiedID, List<UserTrackPoint> points)606 private void CacheInsertUpdatePending(string qualifiedID, List<UserTrackPoint> points) 607 { 608 // 609 // The activity has been added during a pending dynamic change 610 // add it to a temporary lookup which will be copied to real cache 611 // when the dynamic update commits. 612 if ((!_isPrivate) || (!_pendingWorkflowChange)) 613 throw new InvalidOperationException(ExecutionStringManager.ProfileIsNotPrivate); 614 615 if (null == _dynamicUser) 616 throw new InvalidOperationException(ExecutionStringManager.RTProfileDynamicActCacheIsNull); 617 618 List<UserTrackPoint> tmp = null; 619 if (!_dynamicUser.TryGetValue(qualifiedID, out tmp)) 620 { 621 tmp = new List<UserTrackPoint>(); 622 _dynamicUser.Add(qualifiedID, tmp); 623 } 624 625 foreach (UserTrackPoint point in points) 626 tmp.Add(point); 627 } 628 TryGetCacheItems(Activity activity, out List<UserTrackPoint> points)629 private bool TryGetCacheItems(Activity activity, out List<UserTrackPoint> points) 630 { 631 points = null; 632 if ((!_pendingWorkflowChange) || ((_pendingWorkflowChange) && (!IsPendingUpdateActivity(activity, true)))) 633 { 634 // 635 // A dynamic update is not in progress or this activity 636 // is not being added by the current dynamic update. 637 // The main cache holds all matching track points 638 return _user.TryGetValue(activity.QualifiedName, out points); 639 } 640 else 641 { 642 // 643 // Dynamic update is in progress 644 return _dynamicUser.TryGetValue(activity.QualifiedName, out points); 645 } 646 } 647 #endregion 648 649 #region Private Methods 650 651 652 // This function returns all the executable activities including secondary flow activities. GetAllEnabledActivities(CompositeActivity compositeActivity)653 public IList<Activity> GetAllEnabledActivities(CompositeActivity compositeActivity) 654 { 655 if (compositeActivity == null) 656 throw new ArgumentNullException("compositeActivity"); 657 658 List<Activity> allActivities = new List<Activity>(compositeActivity.EnabledActivities); 659 660 foreach (Activity secondaryFlowActivity in ((ISupportAlternateFlow)compositeActivity).AlternateFlowActivities) 661 { 662 if (!allActivities.Contains(secondaryFlowActivity)) 663 allActivities.Add(secondaryFlowActivity); 664 } 665 666 return allActivities; 667 } 668 IsPendingUpdateActivity(Activity activity, bool addedOnly)669 private bool IsPendingUpdateActivity(Activity activity, bool addedOnly) 670 { 671 // 672 // If we don't have an update going on this method isn't valid 673 if ((!_isPrivate) || (!_pendingWorkflowChange)) 674 throw new InvalidOperationException(ExecutionStringManager.ProfileIsNotPrivate); 675 // 676 // if we don't have any changes we're done 677 if ((null == _pendingChanges || _pendingChanges.Count <= 0)) 678 return false; 679 680 foreach (WorkflowChangeAction action in _pendingChanges) 681 { 682 string qualifiedId = null; 683 if (action is ActivityChangeAction) 684 { 685 if (action is AddedActivityAction) 686 { 687 qualifiedId = ((AddedActivityAction)action).AddedActivity.QualifiedName; 688 } 689 else if (action is RemovedActivityAction) 690 { 691 if (!addedOnly) 692 qualifiedId = ((RemovedActivityAction)action).OriginalRemovedActivity.QualifiedName; 693 } 694 else 695 { 696 Debug.Assert(false, ExecutionStringManager.UnknownActivityActionType); 697 } 698 699 if ((null != qualifiedId) 700 && (0 == String.Compare(activity.QualifiedName, qualifiedId, StringComparison.Ordinal))) 701 { 702 return true; 703 } 704 } 705 } 706 return false; 707 } 708 709 #endregion 710 711 #region ICloneable Members 712 ICloneable.Clone()713 object ICloneable.Clone() 714 { 715 return this.Clone(); 716 } 717 Clone()718 internal RTTrackingProfile Clone() 719 { 720 return new RTTrackingProfile(this); 721 } 722 723 #endregion 724 725 #region Contained Types 726 727 private struct ActivityTrackPointCacheItem 728 { ActivityTrackPointCacheItemSystem.Workflow.Runtime.RTTrackingProfile.ActivityTrackPointCacheItem729 internal ActivityTrackPointCacheItem(ActivityTrackPoint point, List<ActivityExecutionStatus> events, bool hasConditions) 730 { 731 if (null == point) 732 throw new ArgumentNullException("point"); 733 if (null == events) 734 throw new ArgumentNullException("events"); 735 736 Point = point; 737 Events = events; 738 HasLocationConditions = hasConditions; 739 } 740 741 internal ActivityTrackPoint Point; 742 internal List<ActivityExecutionStatus> Events; 743 internal bool HasLocationConditions; 744 } 745 746 #endregion 747 } 748 } 749