Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / cdf / src / WF / RunTime / RTTrackingProfile.cs / 1305376 / RTTrackingProfile.cs
using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Text; using System.Xml; using System.Xml.Schema; using System.IO; using System.Reflection; using System.Diagnostics; using System.Runtime.Serialization; using System.Security.Permissions; using System.Globalization; //using System.Workflow.Activities; using System.Workflow.ComponentModel; using System.Workflow.Runtime; using System.Workflow.Runtime.Hosting; using Hosting = System.Workflow.Runtime.Hosting; using System.Workflow.Runtime.Tracking; namespace System.Workflow.Runtime { ////// RTTrackingProfile contains functionality specific to the runtime such as /// trackpoint and location matching and caching, cloning, handling dynamic updates... /// internal class RTTrackingProfile : ICloneable // ICloneable is deprecated { #region Private Data Members // // Client defined profile private TrackingProfile _profile = null; // // Type of the workflow that this profile is associated to private Type _workflowType = null; private Type _serviceType = null; // // List of qualified ids and the trackpoints that declared themselves as matches during static examination private Dictionary> _activities = new Dictionary >(); private List _activitiesIgnore = new List (); private Dictionary > _user = new Dictionary >(); private List _userIgnore = new List (); // // Indicates that the RTTrackingProfile instance is private and is safe to modify for a specific instance private bool _isPrivate = false; // // Indicates if a dynamic update is in-flight private bool _pendingWorkflowChange = false; // // The changes for a dynamic update private IList _pendingChanges = null; // // Activities (including those that are being added) can start executing while a dynamic update is pending // These cannot be added to the main cache until the update succeeds because the update might roll back. // However since we have to search for matching track points we might as well save that work. // This list will be copied into the main cache if the dynamic update completes successfully private Dictionary > _dynamicActivities = null; private List _dynamicActivitiesIgnore = null; private Dictionary > _dynamicUser = null; private List _dynamicUserIgnore = null; #endregion #region Constructors /// /// Default constructor /// protected RTTrackingProfile() { } ////// Primary constructor /// /// /// /// internal RTTrackingProfile(TrackingProfile profile, Activity root, Type serviceType) { if ( null == profile ) throw new ArgumentNullException( "profile" ); if ( null == root ) throw new ArgumentNullException( "root" ); if ( null == serviceType ) throw new ArgumentNullException( "serviceType" ); _workflowType = root.GetType(); _serviceType = serviceType; // // "Clone" a private copy in case the tracking service holds a reference to // the profile it gave us and attempts to modify it at a later point TrackingProfileSerializer tps = new TrackingProfileSerializer(); StringWriter writer = new StringWriter( System.Globalization.CultureInfo.InvariantCulture ); StringReader reader = null; TrackingProfile privateProfile = null; try { // // Let exceptions bubble back to the tracking service - // the profile must be valid per the schema. tps.Serialize( writer, profile ); reader = new StringReader( writer.ToString() ); privateProfile = tps.Deserialize( reader ); } finally { if ( null != reader ) reader.Close(); if ( null != writer ) writer.Close(); } _profile = privateProfile; CheckAllActivities( ( Activity ) root ); } ////// Constructor used for cloning. /// /// RTTrackingProfile to clone ///All members are shallow copied! Use MakePrivate to deep copy after cloning. private RTTrackingProfile( RTTrackingProfile runtimeProfile ) { // // Shallow copy _profile = runtimeProfile._profile; _isPrivate = runtimeProfile._isPrivate; _pendingChanges = runtimeProfile._pendingChanges; _pendingWorkflowChange = runtimeProfile._pendingWorkflowChange; _workflowType = runtimeProfile._workflowType; // // Deep copy the cache. Items in the cache can // be shared but the cache themselves cannot as they may be modified // // Activity match and ignore cache _activities = new Dictionary>( runtimeProfile._activities.Count ); foreach ( KeyValuePair > kvp in runtimeProfile._activities ) _activities.Add( kvp.Key, runtimeProfile._activities[kvp.Key] ); _activitiesIgnore = new List ( runtimeProfile._activitiesIgnore ); // // Pending dynamic update activity match and ignore cache if ( null != runtimeProfile._dynamicActivities ) { _dynamicActivities = new Dictionary >( runtimeProfile._dynamicActivities.Count ); foreach ( KeyValuePair > kvp in runtimeProfile._dynamicActivities ) _dynamicActivities.Add( kvp.Key, runtimeProfile._dynamicActivities[kvp.Key] ); } if ( null != runtimeProfile._dynamicActivitiesIgnore ) _dynamicActivitiesIgnore = new List ( runtimeProfile._dynamicActivitiesIgnore ); // // User event match and ignore cache _user = new Dictionary >( runtimeProfile._user.Count ); foreach ( KeyValuePair > kvp in runtimeProfile._user ) _user.Add( kvp.Key, runtimeProfile._user[kvp.Key] ); _userIgnore = new List ( runtimeProfile._userIgnore ); // // Pending dynamic update activity match and ignore cache if ( null != runtimeProfile._dynamicUser ) { _dynamicUser = new Dictionary >( runtimeProfile._dynamicUser.Count ); foreach ( KeyValuePair > kvp in runtimeProfile._dynamicUser ) _dynamicUser.Add( kvp.Key, kvp.Value ); } if ( null != runtimeProfile._dynamicUserIgnore ) _dynamicUserIgnore = new List ( runtimeProfile._dynamicUserIgnore ); } #endregion #region Properties /// /// Indicates if the profile is specific to an individual instance. /// internal bool IsPrivate { get { return _isPrivate; } set { if ( !( value ) && ( _isPrivate ) ) throw new InvalidOperationException( ExecutionStringManager.CannotResetIsPrivate ); _isPrivate = value; } } ////// Type of workflow to which this profile is associated /// internal Type WorkflowType { get { return _workflowType; } } ////// Version of the profile /// internal Version Version { get { return _profile.Version; } } #endregion #region Internal Methods for Listeners internal bool TryTrackActivityEvent(Activity activity, ActivityExecutionStatus status, IServiceProvider provider, ActivityTrackingRecord record) { Listpoints; // // Check the match caches. if ( TryGetCacheItems( activity, out points ) ) { bool ret = false; foreach ( ActivityTrackPointCacheItem item in points ) { if (item.HasLocationConditions) { if (!item.Point.IsMatch(activity, status)) continue; } if ( item.Events.Contains( status ) ) { ret = true; item.Point.Track( activity, provider, record.Body ); record.Annotations.AddRange( item.Point.Annotations ); } } return ret; } return false; } internal bool TryTrackUserEvent( Activity activity, string keyName, object argument, WorkflowExecutor exec, UserTrackingRecord record ) { List points; if ( TryGetCacheItems( activity, out points ) ) { bool ret = false; foreach ( UserTrackPoint point in points ) { if ( point.IsMatch( activity, keyName, argument ) ) { ret = true; point.Track( activity, argument, exec, record.Body ); record.Annotations.AddRange( point.Annotations ); } } return ret; } return false; } internal bool TryTrackInstanceEvent( TrackingWorkflowEvent status, WorkflowTrackingRecord record ) { bool track = false; foreach ( WorkflowTrackPoint point in _profile.WorkflowTrackPoints ) { if ( point.IsMatch( status ) ) { record.Annotations.AddRange( point.Annotations ); track = true; } } return track; } /// /// Called by TrackingListener to determine if a subscription is needed for an activity. /// Also used as an entry point for dynamically building cache entries for dynamically added activities. /// /// /// ///internal bool ActivitySubscriptionNeeded( Activity activity ) { List points = null; if ( ( !_pendingWorkflowChange ) || ( ( _pendingWorkflowChange ) && ( !IsPendingUpdateActivity( activity, true ) ) ) ) { // // A dynamic update is not in progress or // the activity is not part of the dynamic update. // The main cache has all matching track points // // bool retry = true; while ( retry ) { if ( _activitiesIgnore.Contains( activity.QualifiedName ) ) return false; if ( _activities.TryGetValue( activity.QualifiedName, out points ) ) return true; else // // This activity isn't in either cache, look it up in the profile and add to cache CheckActivity( activity ); } return false; } else { // // Dynamic update is in progress and this activity is being added as part of the update // Search the profile for matching track points and add them to the dynamic cache // (copied to the main cache at the successful completion of the update) // Don't go through CheckActivity because that adds to the main cache List user = null; if ( CreateCacheItems( activity, out user ) ) CacheInsertUpdatePending( activity.QualifiedName, user ); else _dynamicUserIgnore.Add( activity.QualifiedName ); if ( CreateCacheItems( activity, out points ) ) { CacheInsertUpdatePending( activity.QualifiedName, points ); return true; } else { _dynamicActivitiesIgnore.Add( activity.QualifiedName ); return false; } } } public void WorkflowChangeBegin( IList changeActions ) { Debug.Assert( !_pendingWorkflowChange, "_pendingWorkflowChange should be false." ); if ( _pendingWorkflowChange ) throw new InvalidOperationException( ExecutionStringManager.DynamicUpdateIsNotPending ); if ( !_isPrivate ) throw new InvalidOperationException( ExecutionStringManager.ProfileIsNotPrivate ); // // Initialize the temp dictionary for activities that are spun up during the update process // If the update succeeds we'll copy these to the main _subscriptions dictionary. _dynamicActivities = new Dictionary >(); _dynamicActivitiesIgnore = new List (); _dynamicUser = new Dictionary >(); _dynamicUserIgnore = new List (); _pendingChanges = changeActions; _pendingWorkflowChange = true; } public void WorkflowChangeCommit() { Debug.Assert( _pendingWorkflowChange, "Workflow change is not pending - no change to commit" ); if ( !_pendingWorkflowChange ) return; if ( !_isPrivate ) throw new InvalidOperationException( ExecutionStringManager.ProfileIsNotPrivate ); // // Remove items that have been deleted by this update // Must do all removes first as there may be a new action // with the same qid as a previous action that is being removed if ( null != _pendingChanges ) { foreach ( WorkflowChangeAction action in _pendingChanges) { if ( action is RemovedActivityAction ) { // // Remove all references to this activity that might exist in our caches string qId = ( ( RemovedActivityAction ) action ).OriginalRemovedActivity.QualifiedName; _activities.Remove( qId ); _activitiesIgnore.Remove( qId ); _user.Remove( qId ); _userIgnore.Remove( qId ); } } } // // Copy any pending cache items to the regular activity track point cache if ( ( null != _dynamicActivities ) && ( _dynamicActivities.Count > 0 ) ) foreach ( KeyValuePair > kvp in _dynamicActivities ) _activities.Add( kvp.Key, kvp.Value ); if ( ( null != _dynamicActivitiesIgnore ) && ( _dynamicActivitiesIgnore.Count > 0 ) ) _activitiesIgnore.AddRange( _dynamicActivitiesIgnore ); if ( ( null != _dynamicUser ) && ( _dynamicUser.Count > 0 ) ) foreach ( KeyValuePair > kvp in _dynamicUser ) _user.Add( kvp.Key, kvp.Value ); if ( ( null != _dynamicUserIgnore ) && ( _dynamicUserIgnore.Count > 0 ) ) _userIgnore.AddRange( _dynamicUserIgnore ); // // All done, clean up _dynamicActivities = null; _dynamicActivitiesIgnore = null; _dynamicUser = null; _dynamicUserIgnore = null; _pendingChanges = null; _pendingWorkflowChange = false; } public void WorkflowChangeRollback() { // // Just clean up, there isn't any work to rollback because // any subscriptions that may have been added for a pending add activity // won't ever be hit as the activities haven't been added to the tree. _dynamicActivities = null; _dynamicActivitiesIgnore = null; _dynamicUser = null; _dynamicUserIgnore = null; _pendingChanges = null; _pendingWorkflowChange = false; } #endregion #region Private Cache Methods /// /// Create the static qualifiedid to trackpoint map /// /// private void CheckAllActivities( Activity activity ) { CheckActivity( ( Activity ) activity ); // // Walk down the activity tree // Use EnabledActivities to get invisible activities // EnabledActivities will not return commented activities if ( activity is CompositeActivity ) foreach ( Activity a in GetAllEnabledActivities( ( CompositeActivity ) activity ) ) CheckAllActivities( a ); } ////// Recursively walk the activity tree and find all track points that match each activity /// /// private void CheckActivity( Activity activity ) { // // Build caches of activity status change events string qId = activity.QualifiedName; Listactivities = null; if ( CreateCacheItems( activity, out activities ) ) CacheInsert( qId, activities ); else _activitiesIgnore.Add( qId ); // // Build caches of user events List user = null; if ( CreateCacheItems( activity, out user ) ) CacheInsert( qId, user ); else _userIgnore.Add( qId ); } /// /// Find all trackpoints that match an activity. /// /// Activity for which to determine subscription needs /// List to be populated with matching track points ///true if a subscription is needed; false if not private bool CreateCacheItems( Activity activity, out Listincludes ) { includes = new List (); // // Check if we have any trackpoints that match this activity foreach ( ActivityTrackPoint point in _profile.ActivityTrackPoints ) { List events; bool hasCondition = false; if ( point.IsMatch( activity, out events, out hasCondition ) ) includes.Add( new ActivityTrackPointCacheItem( point, events, hasCondition ) ); } return ( includes.Count > 0 ); } /// /// Find all trackpoints that match user events for an activity. /// /// Activity for which to determine subscription needs /// List to be populated with matching track points ///true if a subscription is needed; false if not private bool CreateCacheItems( Activity activity, out Listincludes ) { includes = new List (); // // Check if we have any trackpoints that match this activity foreach ( UserTrackPoint point in _profile.UserTrackPoints ) { if ( point.IsMatch( activity ) ) includes.Add( point ); } return ( includes.Count > 0 ); } private void CacheInsert( string qualifiedID, List points ) { // // Check to make sure the item isn't in the dictionary // If not add all track points Debug.Assert( !_activities.ContainsKey( qualifiedID ), "QualifiedName is already in the activities cache" ); if ( _activities.ContainsKey( qualifiedID ) ) throw new InvalidOperationException( ExecutionStringManager.RTProfileActCacheDupKey ); foreach ( ActivityTrackPointCacheItem point in points ) CacheInsert( qualifiedID, point ); } private void CacheInsert( string qualifiedID, List points ) { // // Check to make sure the item isn't in the dictionary // If not add all track points Debug.Assert( !_user.ContainsKey( qualifiedID ), "QualifiedName is already in the user cache" ); if ( _user.ContainsKey( qualifiedID ) ) throw new InvalidOperationException( ExecutionStringManager.RTProfileActCacheDupKey ); foreach ( UserTrackPoint point in points ) CacheInsert( qualifiedID, point ); } private void CacheInsert( string qualifiedID, ActivityTrackPointCacheItem point ) { List points = null; if ( !_activities.TryGetValue( qualifiedID, out points ) ) { points = new List (); _activities.Add( qualifiedID, points ); } points.Add( point ); } private void CacheInsert( string qualifiedID, UserTrackPoint point ) { List points = null; if ( !_user.TryGetValue( qualifiedID, out points ) ) { points = new List (); _user.Add( qualifiedID, points ); } points.Add( point ); } private void CacheInsertUpdatePending( string qualifiedID, List points ) { // // The activity has been added during a pending dynamic change // add it to a temporary lookup which will be copied to real cache // when the dynamic update commits. if ( ( !_isPrivate ) || ( !_pendingWorkflowChange ) ) throw new InvalidOperationException( ExecutionStringManager.ProfileIsNotPrivate ); if ( null == _dynamicActivities ) throw new InvalidOperationException( ExecutionStringManager.RTProfileDynamicActCacheIsNull ); List tmp = null; if ( !_dynamicActivities.TryGetValue( qualifiedID, out tmp ) ) { tmp = new List (); _dynamicActivities.Add( qualifiedID, tmp ); } foreach ( ActivityTrackPointCacheItem point in points ) tmp.Add( point ); } private bool TryGetCacheItems( Activity activity, out List points ) { points = null; if ( ( !_pendingWorkflowChange ) || ( ( _pendingWorkflowChange ) && ( !IsPendingUpdateActivity( activity, true ) ) ) ) { // // A dynamic update is not in progress or this activity // is not being added by the current dynamic update. // The main cache holds all matching track points return _activities.TryGetValue( activity.QualifiedName, out points ); } else { // // Dynamic update is in progress return _dynamicActivities.TryGetValue( activity.QualifiedName, out points ); } } private void CacheInsertUpdatePending( string qualifiedID, List points ) { // // The activity has been added during a pending dynamic change // add it to a temporary lookup which will be copied to real cache // when the dynamic update commits. if ( ( !_isPrivate ) || ( !_pendingWorkflowChange ) ) throw new InvalidOperationException( ExecutionStringManager.ProfileIsNotPrivate ); if ( null == _dynamicUser ) throw new InvalidOperationException( ExecutionStringManager.RTProfileDynamicActCacheIsNull ); List tmp = null; if ( !_dynamicUser.TryGetValue( qualifiedID, out tmp ) ) { tmp = new List (); _dynamicUser.Add( qualifiedID, tmp ); } foreach ( UserTrackPoint point in points ) tmp.Add( point ); } private bool TryGetCacheItems( Activity activity, out List points ) { points = null; if ( ( !_pendingWorkflowChange ) || ( ( _pendingWorkflowChange ) && ( !IsPendingUpdateActivity( activity, true ) ) ) ) { // // A dynamic update is not in progress or this activity // is not being added by the current dynamic update. // The main cache holds all matching track points return _user.TryGetValue( activity.QualifiedName, out points ); } else { // // Dynamic update is in progress return _dynamicUser.TryGetValue( activity.QualifiedName, out points ); } } #endregion #region Private Methods // This function returns all the executable activities including secondary flow activities. public IList GetAllEnabledActivities( CompositeActivity compositeActivity ) { if ( compositeActivity == null ) throw new ArgumentNullException( "compositeActivity" ); List allActivities = new List ( compositeActivity.EnabledActivities ); foreach ( Activity secondaryFlowActivity in ( ( ISupportAlternateFlow ) compositeActivity ).AlternateFlowActivities ) { if ( !allActivities.Contains( secondaryFlowActivity ) ) allActivities.Add( secondaryFlowActivity ); } return allActivities; } private bool IsPendingUpdateActivity( Activity activity, bool addedOnly ) { // // If we don't have an update going on this method isn't valid if ( ( !_isPrivate ) || ( !_pendingWorkflowChange ) ) throw new InvalidOperationException( ExecutionStringManager.ProfileIsNotPrivate ); // // if we don't have any changes we're done if ( ( null == _pendingChanges || _pendingChanges.Count <= 0 ) ) return false; foreach ( WorkflowChangeAction action in _pendingChanges) { string qualifiedId = null; if ( action is ActivityChangeAction ) { if ( action is AddedActivityAction ) { qualifiedId = ( ( AddedActivityAction ) action ).AddedActivity.QualifiedName; } else if ( action is RemovedActivityAction ) { if ( !addedOnly ) qualifiedId = ( ( RemovedActivityAction ) action ).OriginalRemovedActivity.QualifiedName; } else { Debug.Assert( false, ExecutionStringManager.UnknownActivityActionType ); } if ((null != qualifiedId) && (0 == String.Compare(activity.QualifiedName, qualifiedId, StringComparison.Ordinal))) { return true; } } } return false; } #endregion #region ICloneable Members object ICloneable.Clone() { return this.Clone(); } internal RTTrackingProfile Clone() { return new RTTrackingProfile( this ); } #endregion #region Contained Types private struct ActivityTrackPointCacheItem { internal ActivityTrackPointCacheItem(ActivityTrackPoint point, List events, bool hasConditions) { if ( null == point ) throw new ArgumentNullException( "point" ); if ( null == events ) throw new ArgumentNullException( "events" ); Point = point; Events = events; HasLocationConditions = hasConditions; } internal ActivityTrackPoint Point; internal List Events; internal bool HasLocationConditions; } #endregion } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved. using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Text; using System.Xml; using System.Xml.Schema; using System.IO; using System.Reflection; using System.Diagnostics; using System.Runtime.Serialization; using System.Security.Permissions; using System.Globalization; //using System.Workflow.Activities; using System.Workflow.ComponentModel; using System.Workflow.Runtime; using System.Workflow.Runtime.Hosting; using Hosting = System.Workflow.Runtime.Hosting; using System.Workflow.Runtime.Tracking; namespace System.Workflow.Runtime { /// /// RTTrackingProfile contains functionality specific to the runtime such as /// trackpoint and location matching and caching, cloning, handling dynamic updates... /// internal class RTTrackingProfile : ICloneable // ICloneable is deprecated { #region Private Data Members // // Client defined profile private TrackingProfile _profile = null; // // Type of the workflow that this profile is associated to private Type _workflowType = null; private Type _serviceType = null; // // List of qualified ids and the trackpoints that declared themselves as matches during static examination private Dictionary> _activities = new Dictionary >(); private List _activitiesIgnore = new List (); private Dictionary > _user = new Dictionary >(); private List _userIgnore = new List (); // // Indicates that the RTTrackingProfile instance is private and is safe to modify for a specific instance private bool _isPrivate = false; // // Indicates if a dynamic update is in-flight private bool _pendingWorkflowChange = false; // // The changes for a dynamic update private IList _pendingChanges = null; // // Activities (including those that are being added) can start executing while a dynamic update is pending // These cannot be added to the main cache until the update succeeds because the update might roll back. // However since we have to search for matching track points we might as well save that work. // This list will be copied into the main cache if the dynamic update completes successfully private Dictionary > _dynamicActivities = null; private List _dynamicActivitiesIgnore = null; private Dictionary > _dynamicUser = null; private List _dynamicUserIgnore = null; #endregion #region Constructors /// /// Default constructor /// protected RTTrackingProfile() { } ////// Primary constructor /// /// /// /// internal RTTrackingProfile(TrackingProfile profile, Activity root, Type serviceType) { if ( null == profile ) throw new ArgumentNullException( "profile" ); if ( null == root ) throw new ArgumentNullException( "root" ); if ( null == serviceType ) throw new ArgumentNullException( "serviceType" ); _workflowType = root.GetType(); _serviceType = serviceType; // // "Clone" a private copy in case the tracking service holds a reference to // the profile it gave us and attempts to modify it at a later point TrackingProfileSerializer tps = new TrackingProfileSerializer(); StringWriter writer = new StringWriter( System.Globalization.CultureInfo.InvariantCulture ); StringReader reader = null; TrackingProfile privateProfile = null; try { // // Let exceptions bubble back to the tracking service - // the profile must be valid per the schema. tps.Serialize( writer, profile ); reader = new StringReader( writer.ToString() ); privateProfile = tps.Deserialize( reader ); } finally { if ( null != reader ) reader.Close(); if ( null != writer ) writer.Close(); } _profile = privateProfile; CheckAllActivities( ( Activity ) root ); } ////// Constructor used for cloning. /// /// RTTrackingProfile to clone ///All members are shallow copied! Use MakePrivate to deep copy after cloning. private RTTrackingProfile( RTTrackingProfile runtimeProfile ) { // // Shallow copy _profile = runtimeProfile._profile; _isPrivate = runtimeProfile._isPrivate; _pendingChanges = runtimeProfile._pendingChanges; _pendingWorkflowChange = runtimeProfile._pendingWorkflowChange; _workflowType = runtimeProfile._workflowType; // // Deep copy the cache. Items in the cache can // be shared but the cache themselves cannot as they may be modified // // Activity match and ignore cache _activities = new Dictionary>( runtimeProfile._activities.Count ); foreach ( KeyValuePair > kvp in runtimeProfile._activities ) _activities.Add( kvp.Key, runtimeProfile._activities[kvp.Key] ); _activitiesIgnore = new List ( runtimeProfile._activitiesIgnore ); // // Pending dynamic update activity match and ignore cache if ( null != runtimeProfile._dynamicActivities ) { _dynamicActivities = new Dictionary >( runtimeProfile._dynamicActivities.Count ); foreach ( KeyValuePair > kvp in runtimeProfile._dynamicActivities ) _dynamicActivities.Add( kvp.Key, runtimeProfile._dynamicActivities[kvp.Key] ); } if ( null != runtimeProfile._dynamicActivitiesIgnore ) _dynamicActivitiesIgnore = new List ( runtimeProfile._dynamicActivitiesIgnore ); // // User event match and ignore cache _user = new Dictionary >( runtimeProfile._user.Count ); foreach ( KeyValuePair > kvp in runtimeProfile._user ) _user.Add( kvp.Key, runtimeProfile._user[kvp.Key] ); _userIgnore = new List ( runtimeProfile._userIgnore ); // // Pending dynamic update activity match and ignore cache if ( null != runtimeProfile._dynamicUser ) { _dynamicUser = new Dictionary >( runtimeProfile._dynamicUser.Count ); foreach ( KeyValuePair > kvp in runtimeProfile._dynamicUser ) _dynamicUser.Add( kvp.Key, kvp.Value ); } if ( null != runtimeProfile._dynamicUserIgnore ) _dynamicUserIgnore = new List ( runtimeProfile._dynamicUserIgnore ); } #endregion #region Properties /// /// Indicates if the profile is specific to an individual instance. /// internal bool IsPrivate { get { return _isPrivate; } set { if ( !( value ) && ( _isPrivate ) ) throw new InvalidOperationException( ExecutionStringManager.CannotResetIsPrivate ); _isPrivate = value; } } ////// Type of workflow to which this profile is associated /// internal Type WorkflowType { get { return _workflowType; } } ////// Version of the profile /// internal Version Version { get { return _profile.Version; } } #endregion #region Internal Methods for Listeners internal bool TryTrackActivityEvent(Activity activity, ActivityExecutionStatus status, IServiceProvider provider, ActivityTrackingRecord record) { Listpoints; // // Check the match caches. if ( TryGetCacheItems( activity, out points ) ) { bool ret = false; foreach ( ActivityTrackPointCacheItem item in points ) { if (item.HasLocationConditions) { if (!item.Point.IsMatch(activity, status)) continue; } if ( item.Events.Contains( status ) ) { ret = true; item.Point.Track( activity, provider, record.Body ); record.Annotations.AddRange( item.Point.Annotations ); } } return ret; } return false; } internal bool TryTrackUserEvent( Activity activity, string keyName, object argument, WorkflowExecutor exec, UserTrackingRecord record ) { List points; if ( TryGetCacheItems( activity, out points ) ) { bool ret = false; foreach ( UserTrackPoint point in points ) { if ( point.IsMatch( activity, keyName, argument ) ) { ret = true; point.Track( activity, argument, exec, record.Body ); record.Annotations.AddRange( point.Annotations ); } } return ret; } return false; } internal bool TryTrackInstanceEvent( TrackingWorkflowEvent status, WorkflowTrackingRecord record ) { bool track = false; foreach ( WorkflowTrackPoint point in _profile.WorkflowTrackPoints ) { if ( point.IsMatch( status ) ) { record.Annotations.AddRange( point.Annotations ); track = true; } } return track; } /// /// Called by TrackingListener to determine if a subscription is needed for an activity. /// Also used as an entry point for dynamically building cache entries for dynamically added activities. /// /// /// ///internal bool ActivitySubscriptionNeeded( Activity activity ) { List points = null; if ( ( !_pendingWorkflowChange ) || ( ( _pendingWorkflowChange ) && ( !IsPendingUpdateActivity( activity, true ) ) ) ) { // // A dynamic update is not in progress or // the activity is not part of the dynamic update. // The main cache has all matching track points // // bool retry = true; while ( retry ) { if ( _activitiesIgnore.Contains( activity.QualifiedName ) ) return false; if ( _activities.TryGetValue( activity.QualifiedName, out points ) ) return true; else // // This activity isn't in either cache, look it up in the profile and add to cache CheckActivity( activity ); } return false; } else { // // Dynamic update is in progress and this activity is being added as part of the update // Search the profile for matching track points and add them to the dynamic cache // (copied to the main cache at the successful completion of the update) // Don't go through CheckActivity because that adds to the main cache List user = null; if ( CreateCacheItems( activity, out user ) ) CacheInsertUpdatePending( activity.QualifiedName, user ); else _dynamicUserIgnore.Add( activity.QualifiedName ); if ( CreateCacheItems( activity, out points ) ) { CacheInsertUpdatePending( activity.QualifiedName, points ); return true; } else { _dynamicActivitiesIgnore.Add( activity.QualifiedName ); return false; } } } public void WorkflowChangeBegin( IList changeActions ) { Debug.Assert( !_pendingWorkflowChange, "_pendingWorkflowChange should be false." ); if ( _pendingWorkflowChange ) throw new InvalidOperationException( ExecutionStringManager.DynamicUpdateIsNotPending ); if ( !_isPrivate ) throw new InvalidOperationException( ExecutionStringManager.ProfileIsNotPrivate ); // // Initialize the temp dictionary for activities that are spun up during the update process // If the update succeeds we'll copy these to the main _subscriptions dictionary. _dynamicActivities = new Dictionary >(); _dynamicActivitiesIgnore = new List (); _dynamicUser = new Dictionary >(); _dynamicUserIgnore = new List (); _pendingChanges = changeActions; _pendingWorkflowChange = true; } public void WorkflowChangeCommit() { Debug.Assert( _pendingWorkflowChange, "Workflow change is not pending - no change to commit" ); if ( !_pendingWorkflowChange ) return; if ( !_isPrivate ) throw new InvalidOperationException( ExecutionStringManager.ProfileIsNotPrivate ); // // Remove items that have been deleted by this update // Must do all removes first as there may be a new action // with the same qid as a previous action that is being removed if ( null != _pendingChanges ) { foreach ( WorkflowChangeAction action in _pendingChanges) { if ( action is RemovedActivityAction ) { // // Remove all references to this activity that might exist in our caches string qId = ( ( RemovedActivityAction ) action ).OriginalRemovedActivity.QualifiedName; _activities.Remove( qId ); _activitiesIgnore.Remove( qId ); _user.Remove( qId ); _userIgnore.Remove( qId ); } } } // // Copy any pending cache items to the regular activity track point cache if ( ( null != _dynamicActivities ) && ( _dynamicActivities.Count > 0 ) ) foreach ( KeyValuePair > kvp in _dynamicActivities ) _activities.Add( kvp.Key, kvp.Value ); if ( ( null != _dynamicActivitiesIgnore ) && ( _dynamicActivitiesIgnore.Count > 0 ) ) _activitiesIgnore.AddRange( _dynamicActivitiesIgnore ); if ( ( null != _dynamicUser ) && ( _dynamicUser.Count > 0 ) ) foreach ( KeyValuePair > kvp in _dynamicUser ) _user.Add( kvp.Key, kvp.Value ); if ( ( null != _dynamicUserIgnore ) && ( _dynamicUserIgnore.Count > 0 ) ) _userIgnore.AddRange( _dynamicUserIgnore ); // // All done, clean up _dynamicActivities = null; _dynamicActivitiesIgnore = null; _dynamicUser = null; _dynamicUserIgnore = null; _pendingChanges = null; _pendingWorkflowChange = false; } public void WorkflowChangeRollback() { // // Just clean up, there isn't any work to rollback because // any subscriptions that may have been added for a pending add activity // won't ever be hit as the activities haven't been added to the tree. _dynamicActivities = null; _dynamicActivitiesIgnore = null; _dynamicUser = null; _dynamicUserIgnore = null; _pendingChanges = null; _pendingWorkflowChange = false; } #endregion #region Private Cache Methods /// /// Create the static qualifiedid to trackpoint map /// /// private void CheckAllActivities( Activity activity ) { CheckActivity( ( Activity ) activity ); // // Walk down the activity tree // Use EnabledActivities to get invisible activities // EnabledActivities will not return commented activities if ( activity is CompositeActivity ) foreach ( Activity a in GetAllEnabledActivities( ( CompositeActivity ) activity ) ) CheckAllActivities( a ); } ////// Recursively walk the activity tree and find all track points that match each activity /// /// private void CheckActivity( Activity activity ) { // // Build caches of activity status change events string qId = activity.QualifiedName; Listactivities = null; if ( CreateCacheItems( activity, out activities ) ) CacheInsert( qId, activities ); else _activitiesIgnore.Add( qId ); // // Build caches of user events List user = null; if ( CreateCacheItems( activity, out user ) ) CacheInsert( qId, user ); else _userIgnore.Add( qId ); } /// /// Find all trackpoints that match an activity. /// /// Activity for which to determine subscription needs /// List to be populated with matching track points ///true if a subscription is needed; false if not private bool CreateCacheItems( Activity activity, out Listincludes ) { includes = new List (); // // Check if we have any trackpoints that match this activity foreach ( ActivityTrackPoint point in _profile.ActivityTrackPoints ) { List events; bool hasCondition = false; if ( point.IsMatch( activity, out events, out hasCondition ) ) includes.Add( new ActivityTrackPointCacheItem( point, events, hasCondition ) ); } return ( includes.Count > 0 ); } /// /// Find all trackpoints that match user events for an activity. /// /// Activity for which to determine subscription needs /// List to be populated with matching track points ///true if a subscription is needed; false if not private bool CreateCacheItems( Activity activity, out Listincludes ) { includes = new List (); // // Check if we have any trackpoints that match this activity foreach ( UserTrackPoint point in _profile.UserTrackPoints ) { if ( point.IsMatch( activity ) ) includes.Add( point ); } return ( includes.Count > 0 ); } private void CacheInsert( string qualifiedID, List points ) { // // Check to make sure the item isn't in the dictionary // If not add all track points Debug.Assert( !_activities.ContainsKey( qualifiedID ), "QualifiedName is already in the activities cache" ); if ( _activities.ContainsKey( qualifiedID ) ) throw new InvalidOperationException( ExecutionStringManager.RTProfileActCacheDupKey ); foreach ( ActivityTrackPointCacheItem point in points ) CacheInsert( qualifiedID, point ); } private void CacheInsert( string qualifiedID, List points ) { // // Check to make sure the item isn't in the dictionary // If not add all track points Debug.Assert( !_user.ContainsKey( qualifiedID ), "QualifiedName is already in the user cache" ); if ( _user.ContainsKey( qualifiedID ) ) throw new InvalidOperationException( ExecutionStringManager.RTProfileActCacheDupKey ); foreach ( UserTrackPoint point in points ) CacheInsert( qualifiedID, point ); } private void CacheInsert( string qualifiedID, ActivityTrackPointCacheItem point ) { List points = null; if ( !_activities.TryGetValue( qualifiedID, out points ) ) { points = new List (); _activities.Add( qualifiedID, points ); } points.Add( point ); } private void CacheInsert( string qualifiedID, UserTrackPoint point ) { List points = null; if ( !_user.TryGetValue( qualifiedID, out points ) ) { points = new List (); _user.Add( qualifiedID, points ); } points.Add( point ); } private void CacheInsertUpdatePending( string qualifiedID, List points ) { // // The activity has been added during a pending dynamic change // add it to a temporary lookup which will be copied to real cache // when the dynamic update commits. if ( ( !_isPrivate ) || ( !_pendingWorkflowChange ) ) throw new InvalidOperationException( ExecutionStringManager.ProfileIsNotPrivate ); if ( null == _dynamicActivities ) throw new InvalidOperationException( ExecutionStringManager.RTProfileDynamicActCacheIsNull ); List tmp = null; if ( !_dynamicActivities.TryGetValue( qualifiedID, out tmp ) ) { tmp = new List (); _dynamicActivities.Add( qualifiedID, tmp ); } foreach ( ActivityTrackPointCacheItem point in points ) tmp.Add( point ); } private bool TryGetCacheItems( Activity activity, out List points ) { points = null; if ( ( !_pendingWorkflowChange ) || ( ( _pendingWorkflowChange ) && ( !IsPendingUpdateActivity( activity, true ) ) ) ) { // // A dynamic update is not in progress or this activity // is not being added by the current dynamic update. // The main cache holds all matching track points return _activities.TryGetValue( activity.QualifiedName, out points ); } else { // // Dynamic update is in progress return _dynamicActivities.TryGetValue( activity.QualifiedName, out points ); } } private void CacheInsertUpdatePending( string qualifiedID, List points ) { // // The activity has been added during a pending dynamic change // add it to a temporary lookup which will be copied to real cache // when the dynamic update commits. if ( ( !_isPrivate ) || ( !_pendingWorkflowChange ) ) throw new InvalidOperationException( ExecutionStringManager.ProfileIsNotPrivate ); if ( null == _dynamicUser ) throw new InvalidOperationException( ExecutionStringManager.RTProfileDynamicActCacheIsNull ); List tmp = null; if ( !_dynamicUser.TryGetValue( qualifiedID, out tmp ) ) { tmp = new List (); _dynamicUser.Add( qualifiedID, tmp ); } foreach ( UserTrackPoint point in points ) tmp.Add( point ); } private bool TryGetCacheItems( Activity activity, out List points ) { points = null; if ( ( !_pendingWorkflowChange ) || ( ( _pendingWorkflowChange ) && ( !IsPendingUpdateActivity( activity, true ) ) ) ) { // // A dynamic update is not in progress or this activity // is not being added by the current dynamic update. // The main cache holds all matching track points return _user.TryGetValue( activity.QualifiedName, out points ); } else { // // Dynamic update is in progress return _dynamicUser.TryGetValue( activity.QualifiedName, out points ); } } #endregion #region Private Methods // This function returns all the executable activities including secondary flow activities. public IList GetAllEnabledActivities( CompositeActivity compositeActivity ) { if ( compositeActivity == null ) throw new ArgumentNullException( "compositeActivity" ); List allActivities = new List ( compositeActivity.EnabledActivities ); foreach ( Activity secondaryFlowActivity in ( ( ISupportAlternateFlow ) compositeActivity ).AlternateFlowActivities ) { if ( !allActivities.Contains( secondaryFlowActivity ) ) allActivities.Add( secondaryFlowActivity ); } return allActivities; } private bool IsPendingUpdateActivity( Activity activity, bool addedOnly ) { // // If we don't have an update going on this method isn't valid if ( ( !_isPrivate ) || ( !_pendingWorkflowChange ) ) throw new InvalidOperationException( ExecutionStringManager.ProfileIsNotPrivate ); // // if we don't have any changes we're done if ( ( null == _pendingChanges || _pendingChanges.Count <= 0 ) ) return false; foreach ( WorkflowChangeAction action in _pendingChanges) { string qualifiedId = null; if ( action is ActivityChangeAction ) { if ( action is AddedActivityAction ) { qualifiedId = ( ( AddedActivityAction ) action ).AddedActivity.QualifiedName; } else if ( action is RemovedActivityAction ) { if ( !addedOnly ) qualifiedId = ( ( RemovedActivityAction ) action ).OriginalRemovedActivity.QualifiedName; } else { Debug.Assert( false, ExecutionStringManager.UnknownActivityActionType ); } if ((null != qualifiedId) && (0 == String.Compare(activity.QualifiedName, qualifiedId, StringComparison.Ordinal))) { return true; } } } return false; } #endregion #region ICloneable Members object ICloneable.Clone() { return this.Clone(); } internal RTTrackingProfile Clone() { return new RTTrackingProfile( this ); } #endregion #region Contained Types private struct ActivityTrackPointCacheItem { internal ActivityTrackPointCacheItem(ActivityTrackPoint point, List events, bool hasConditions) { if ( null == point ) throw new ArgumentNullException( "point" ); if ( null == events ) throw new ArgumentNullException( "events" ); Point = point; Events = events; HasLocationConditions = hasConditions; } internal ActivityTrackPoint Point; internal List Events; internal bool HasLocationConditions; } #endregion } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- CompileLiteralTextParser.cs
- dsa.cs
- TransferRequestHandler.cs
- BasicViewGenerator.cs
- MetadataProperty.cs
- EntityProviderServices.cs
- ContentIterators.cs
- MethodToken.cs
- Wildcard.cs
- WebMessageEncodingBindingElement.cs
- RelationshipEndMember.cs
- XmlSerializationGeneratedCode.cs
- TypeUsageBuilder.cs
- SqlProcedureAttribute.cs
- hwndwrapper.cs
- FormsIdentity.cs
- PageHandlerFactory.cs
- WebConfigurationFileMap.cs
- SequenceDesignerAccessibleObject.cs
- MeasureItemEvent.cs
- BrowserCapabilitiesCompiler.cs
- PageParser.cs
- ControlValuePropertyAttribute.cs
- PopOutPanel.cs
- SmiEventSink.cs
- baseshape.cs
- PathNode.cs
- OleDbError.cs
- ItemChangedEventArgs.cs
- EdmConstants.cs
- URL.cs
- ClientFormsIdentity.cs
- LinqToSqlWrapper.cs
- FontSource.cs
- AudioStateChangedEventArgs.cs
- InteropBitmapSource.cs
- ConfigurationManagerInternalFactory.cs
- ExtensionCollection.cs
- processwaithandle.cs
- StickyNoteContentControl.cs
- KerberosTicketHashIdentifierClause.cs
- XDRSchema.cs
- StandardOleMarshalObject.cs
- ComponentEditorForm.cs
- WorkflowLayouts.cs
- HierarchicalDataBoundControl.cs
- SessionIDManager.cs
- SponsorHelper.cs
- AppliesToBehaviorDecisionTable.cs
- CreateRefExpr.cs
- CacheDependency.cs
- InstanceStoreQueryResult.cs
- SingletonConnectionReader.cs
- RootProfilePropertySettingsCollection.cs
- HttpCapabilitiesEvaluator.cs
- SmiSettersStream.cs
- LookupBindingPropertiesAttribute.cs
- Itemizer.cs
- GridLengthConverter.cs
- AppendHelper.cs
- ValidatedControlConverter.cs
- Model3DGroup.cs
- AccessibleObject.cs
- SystemSounds.cs
- ExecutionEngineException.cs
- FilterableAttribute.cs
- InteropAutomationProvider.cs
- TypeCodeDomSerializer.cs
- DbConnectionPool.cs
- X509CertificateTrustedIssuerElement.cs
- EntityWithChangeTrackerStrategy.cs
- AccessViolationException.cs
- ErrorFormatter.cs
- QilSortKey.cs
- SendMessageRecord.cs
- FlatButtonAppearance.cs
- ValidatorUtils.cs
- TextTreeObjectNode.cs
- RankException.cs
- SqlRowUpdatingEvent.cs
- ImageIndexConverter.cs
- ObjectDataSourceStatusEventArgs.cs
- ConnectionStringSettingsCollection.cs
- ToolboxCategoryItems.cs
- SQLBytes.cs
- DocComment.cs
- Profiler.cs
- SeverityFilter.cs
- ClientApiGenerator.cs
- XmlWrappingReader.cs
- SqlInternalConnectionSmi.cs
- Speller.cs
- TimelineCollection.cs
- Trace.cs
- TraceListener.cs
- LogLogRecordEnumerator.cs
- ListSourceHelper.cs
- SByteStorage.cs
- SqlFormatter.cs
- ComEventsMethod.cs