RTTrackingProfile.cs source code in C# .NET

Source code for the .NET framework in C#

                        

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) 
        {
            List points;
            //
            // 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;
            List activities = 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 List includes ) 
        {
            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 List includes ) 
        {
            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) 
        {
            List points;
            //
            // 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;
            List activities = 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 List includes ) 
        {
            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 List includes ) 
        {
            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

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK