File: Tracking\SqlTrackingWorkflowInstance.cs
Project: ndp\cdf\src\WF\RunTime\System.Workflow.Runtime.csproj (System.Workflow.Runtime)
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Reflection;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.ComponentModel.Design.Serialization;
 
using System.Workflow.Runtime;
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Serialization;
using System.Workflow.Runtime.Hosting;
 
namespace System.Workflow.Runtime.Tracking
{
    [Obsolete("The System.Workflow.* types are deprecated.  Instead, please use the new types from System.Activities.*")]
    public class SqlTrackingWorkflowInstance
    {
        #region Private Members
 
        private delegate void LoadFromReader(SqlDataReader reader, object parameter);
 
        private static int _deadlock = 1205;
        private static short _retries = 5;
        private string _connectionString = null;
        private bool _autoRefresh = false;
 
        DateTime _currDT = DateTime.UtcNow,
            _actMinDT = SqlDateTime.MinValue.Value,
            _userMinDT = SqlDateTime.MinValue.Value,
            _instMinDT = SqlDateTime.MinValue.Value,
            _childMinDT = SqlDateTime.MinValue.Value,
            _changesMinDT = SqlDateTime.MinValue.Value,
            _invMinDT = SqlDateTime.MinValue.Value;
 
        long _internalId = -1;
 
        Guid _id;
        DateTime _initialized;
        Guid _invoker = Guid.Empty;
        WorkflowStatus _status;
        Type _workflowType = null;
        bool _changed = false;
 
        List<ActivityTrackingRecord> _activityEvents = new List<ActivityTrackingRecord>();
        List<UserTrackingRecord> _userEvents = new List<UserTrackingRecord>();
        List<WorkflowTrackingRecord> _workflowEvents = new List<WorkflowTrackingRecord>();
        List<SqlTrackingWorkflowInstance> _invoked = new List<SqlTrackingWorkflowInstance>();
 
        Activity _def = null;
 
        #endregion Private Members
 
        #region Constructors
 
        private SqlTrackingWorkflowInstance() { }
 
        internal SqlTrackingWorkflowInstance(string connectionString)
        {
            if (null == connectionString)
                throw new ArgumentNullException("connectionString");
            _connectionString = connectionString;
        }
 
        #endregion Constructors
 
        #region Properties
 
        public bool AutoRefresh
        {
            get { return _autoRefresh; }
            set { _autoRefresh = value; }
        }
 
        public Guid WorkflowInstanceId
        {
            get { return _id; }
            set { _id = value; }
        }
 
        public long WorkflowInstanceInternalId
        {
            get { return _internalId; }
            set { _internalId = value; }
        }
 
        public DateTime Initialized
        {
            get { return _initialized; }
            set { _initialized = value; }
        }
 
        public Guid InvokingWorkflowInstanceId
        {
            get { return _invoker; }
            set { _invoker = value; }
        }
 
        public WorkflowStatus Status
        {
            get { return _status; }
            set { _status = value; }
        }
 
        public Type WorkflowType
        {
            get { return _workflowType; }
            set { _workflowType = value; }
        }
 
        public bool WorkflowDefinitionUpdated
        {
            get
            {
                if (_autoRefresh)
                    Refresh();
 
                LoadDef();
                return _changed;
            }
        }
 
        public IList<ActivityTrackingRecord> ActivityEvents
        {
            get
            {
                if (_autoRefresh)
                    Refresh();
 
                LoadActivityEvents();
                return _activityEvents;
            }
        }
 
        public IList<UserTrackingRecord> UserEvents
        {
            get
            {
                if (_autoRefresh)
                    Refresh();
 
                LoadUserEvents();
                return _userEvents;
            }
        }
 
        public IList<WorkflowTrackingRecord> WorkflowEvents
        {
            get
            {
                if (_autoRefresh)
                    Refresh();
 
                LoadWorkflowEvents();
                return _workflowEvents;
            }
        }
 
        public Activity WorkflowDefinition
        {
            get
            {
                if (_autoRefresh)
                    Refresh();
 
                LoadDef();
                return _def;
            }
        }
 
        public IList<SqlTrackingWorkflowInstance> InvokedWorkflows
        {
            get
            {
                if (_autoRefresh)
                    Refresh();
                LoadInvokedWorkflows();
                return _invoked;
            }
        }
        #endregion Properties
 
        public void Refresh()
        {
            _currDT = DateTime.UtcNow;
        }
 
        private void LoadActivityEvents()
        {
            SqlCommand cmd = CreateInternalIdDateTimeCommand("[dbo].[GetActivityEventsWithDetails]", _actMinDT);
 
            ExecuteRetried(cmd, LoadActivityEventsFromReader);
        }
 
        private void LoadActivityEventsFromReader(SqlDataReader reader, object parameter)
        {
            if (null == reader)
                throw new ArgumentNullException("reader");
 
            //
            // There should always be 4 recordsets in this reader!
            //
 
            Dictionary<long, ActivityTrackingRecord> activities = new Dictionary<long, ActivityTrackingRecord>();
            //
            // Build a dictionary of activity records so that we can match 
            // annotation and artifact records from subsequent recordsets
            DateTime tmpMin = SqlDateTime.MinValue.Value;
            while (reader.Read())
            {
                string qId = reader.GetString(0);
                ActivityExecutionStatus status = (ActivityExecutionStatus)reader[1];
                DateTime dt = reader.GetDateTime(2);
                Guid context = reader.GetGuid(3), parentContext = reader.GetGuid(4);
                int order = reader.GetInt32(5);
 
                if (reader.IsDBNull(6) || reader.IsDBNull(7))
                    throw new InvalidOperationException(String.Format(System.Globalization.CultureInfo.CurrentCulture, ExecutionStringManager.SqlTrackingTypeNotFound, qId));
 
                Type type = Type.GetType(reader.GetString(6) + ", " + reader.GetString(7), true, false);
                long eventId = reader.GetInt64(8);
 
                DateTime dbDt = reader.GetDateTime(9);
 
                activities.Add(eventId, new ActivityTrackingRecord(type, qId, context, parentContext, status, dt, order, null));
                if (dbDt > tmpMin)
                    tmpMin = dbDt;
            }
 
            if (!reader.NextResult())
                throw new ArgumentException(ExecutionStringManager.InvalidActivityEventReader);
 
            //
            // If we have annotations on the event itself, add them
            while (reader.Read())
            {
                long eventId = reader.GetInt64(0);
                string annotation = null;
 
                if (!reader.IsDBNull(1))
                    annotation = reader.GetString(1);
 
                ActivityTrackingRecord activity = null;
                if (activities.TryGetValue(eventId, out activity))
                {
                    if (null != activity)
                        activity.Annotations.Add(annotation);
                }
            }
 
            if (!reader.NextResult())
                throw new ArgumentException(ExecutionStringManager.InvalidActivityEventReader);
 
            //
            // Build a dictionary of artifact records so that we can match 
            // annotation records from subsequent recordsets
            BinaryFormatter formatter = new BinaryFormatter();
            Dictionary<long, TrackingDataItem> artifacts = new Dictionary<long, TrackingDataItem>();
            while (reader.Read())
            {
                long eventId = reader.GetInt64(0);
                long artId = reader.GetInt64(1);
                string name = reader.GetString(2), strData = null;
                object data = null;
                //
                // These may both be null
                if (!reader.IsDBNull(3))
                    strData = reader.GetString(3);
 
                if (!reader.IsDBNull(4))
                    data = formatter.Deserialize(new MemoryStream((Byte[])reader[4]));
 
                TrackingDataItem item = new TrackingDataItem();
                item.FieldName = name;
                if (null != data)
                    item.Data = data;
                else
                    item.Data = strData;
 
                artifacts.Add(artId, item);
                //
                // Find the event to which this artifact belongs and add it to the record
                ActivityTrackingRecord activity = null;
                if (activities.TryGetValue(eventId, out activity))
                {
                    if (null != activity)
                        activity.Body.Add(item);
                }
            }
 
            if (!reader.NextResult())
                throw new ArgumentException(ExecutionStringManager.InvalidActivityEventReader);
 
            //
            // If we have annotations add them to the appropriate artifact
            while (reader.Read())
            {
                long artId = reader.GetInt64(0);
                string annotation = null;
 
                if (!reader.IsDBNull(1))
                    annotation = reader.GetString(1);
                //
                // Find the right artifact and give it the annotation
                TrackingDataItem item = null;
                if (artifacts.TryGetValue(artId, out item))
                {
                    if (null != item)
                        item.Annotations.Add(annotation);
                }
            }
 
            _activityEvents.AddRange(activities.Values);
 
            //
            // Set the min value to the most recent event that we got with this query
            // Don't overwrite the previous min if nothing came back for this query
            if (tmpMin > SqlDateTime.MinValue.Value)
                _actMinDT = tmpMin;
            return;
        }
 
        private void LoadUserEvents()
        {
            SqlCommand cmd = CreateInternalIdDateTimeCommand("[dbo].[GetUserEventsWithDetails]", _userMinDT);
 
            ExecuteRetried(cmd, LoadUserEventsFromReader);
        }
 
        private void LoadUserEventsFromReader(SqlDataReader reader, object parameter)
        {
            if (null == reader)
                throw new ArgumentNullException("reader");
 
            //
            // There should always be 4 recordsets in this reader!
            //
 
            BinaryFormatter formatter = new BinaryFormatter();
            Dictionary<long, UserTrackingRecord> userEvents = new Dictionary<long, UserTrackingRecord>();
            //
            // Build a dictionary of activity records so that we can match 
            // annotation and artifact records from subsequent recordsets
            DateTime tmpMin = SqlDateTime.MinValue.Value;
            while (reader.Read())
            {
                string qId = reader.GetString(0);
                DateTime dt = reader.GetDateTime(1);
                Guid context = reader.GetGuid(2), parentContext = reader.GetGuid(3);
                int order = reader.GetInt32(4);
                string key = null;
                if (!reader.IsDBNull(5))
                    key = reader.GetString(5);
                //
                // Get the user data from the serialized column if we can
                // Try the string column if serialized column is null
                // If both are null the user data was null originally
                object userData = null;
                if (!reader.IsDBNull(7))
                    userData = formatter.Deserialize(new MemoryStream((Byte[])reader[7]));
                else if (!reader.IsDBNull(6))
                    userData = reader.GetString(6);
 
                if (reader.IsDBNull(8) || reader.IsDBNull(9))
                    throw new InvalidOperationException(String.Format(System.Globalization.CultureInfo.CurrentCulture, ExecutionStringManager.SqlTrackingTypeNotFound, qId));
 
                Type type = Type.GetType(reader.GetString(8) + ", " + reader.GetString(9), true, false);
                long eventId = reader.GetInt64(10);
 
                DateTime dbDt = reader.GetDateTime(11);
 
                userEvents.Add(eventId, new UserTrackingRecord(type, qId, context, parentContext, dt, order, key, userData));
 
                if (dbDt > tmpMin)
                    tmpMin = dbDt;
            }
 
            if (!reader.NextResult())
                throw new ArgumentException(ExecutionStringManager.InvalidUserEventReader);
 
            //
            // If we have annotations on the event itself, add them
            while (reader.Read())
            {
                long eventId = reader.GetInt64(0);
                string annotation = null;
 
                if (!reader.IsDBNull(1))
                    annotation = reader.GetString(1);
 
                UserTrackingRecord user = null;
                if (userEvents.TryGetValue(eventId, out user))
                {
                    if (null != user)
                        user.Annotations.Add(annotation);
                }
            }
 
            if (!reader.NextResult())
                throw new ArgumentException(ExecutionStringManager.InvalidUserEventReader);
 
            //
            // Build a dictionary of artifact records so that we can match 
            // annotation records from subsequent recordsets
            Dictionary<long, TrackingDataItem> artifacts = new Dictionary<long, TrackingDataItem>();
            while (reader.Read())
            {
                long eventId = reader.GetInt64(0);
                long artId = reader.GetInt64(1);
                string name = reader.GetString(2), strData = null;
                object data = null;
                //
                // These may both be null
                if (!reader.IsDBNull(3))
                    strData = reader.GetString(3);
 
                if (!reader.IsDBNull(4))
                    data = formatter.Deserialize(new MemoryStream((Byte[])reader[4]));
 
                TrackingDataItem item = new TrackingDataItem();
                item.FieldName = name;
                if (null != data)
                    item.Data = data;
                else
                    item.Data = strData;
 
                artifacts.Add(artId, item);
                //
                // Find the event to which this artifact belongs and add it to the record
                UserTrackingRecord user = null;
                if (userEvents.TryGetValue(eventId, out user))
                {
                    if (null != user)
                        user.Body.Add(item);
                }
            }
 
            if (!reader.NextResult())
                throw new ArgumentException(ExecutionStringManager.InvalidUserEventReader);
 
            //
            // If we have annotations add them to the appropriate artifact
            while (reader.Read())
            {
                long artId = reader.GetInt64(0);
                string annotation = null;
 
                if (!reader.IsDBNull(1))
                    annotation = reader.GetString(1);
                //
                // Find the right artifact and give it the annotation
                TrackingDataItem item = null;
                if (artifacts.TryGetValue(artId, out item))
                {
                    if (null != item)
                        item.Annotations.Add(annotation);
                }
            }
 
            _userEvents.AddRange(userEvents.Values);
            //
            // Set the min dt to query for next time to the most recent event we got for this query.
            // Don't overwrite the previous min if nothing came back for this query
            if (tmpMin > SqlDateTime.MinValue.Value)
                _userMinDT = tmpMin;
            return;
        }
 
        private void LoadWorkflowEvents()
        {
            SqlCommand cmd = CreateInternalIdDateTimeCommand("[dbo].[GetWorkflowInstanceEventsWithDetails]", _instMinDT);
 
            ExecuteRetried(cmd, LoadWorkflowEventsFromReader);
        }
 
        private void LoadWorkflowEventsFromReader(SqlDataReader reader, object parameter)
        {
            if (null == reader)
                throw new ArgumentNullException("reader");
 
            //
            // There should always be 2 recordsets in this reader!
            //
 
            DateTime tmpMin = SqlDateTime.MinValue.Value;
 
            Dictionary<long, WorkflowTrackingRecord> inst = new Dictionary<long, WorkflowTrackingRecord>();
            while (reader.Read())
            {
                TrackingWorkflowEvent evt = (TrackingWorkflowEvent)reader[0];
                DateTime dt = reader.GetDateTime(1);
                int order = reader.GetInt32(2);
 
                object tmp = null;
                EventArgs args = null;
                if (!reader.IsDBNull(3))
                {
                    BinaryFormatter formatter = new BinaryFormatter();
                    tmp = formatter.Deserialize(new MemoryStream((Byte[])reader[3]));
                    if (tmp is EventArgs)
                        args = (EventArgs)tmp;
                }
                long eventId = reader.GetInt64(4);
 
                DateTime dbDt = reader.GetDateTime(5);
 
                inst.Add(eventId, new WorkflowTrackingRecord(evt, dt, order, args));
 
                if (dbDt > tmpMin)
                    tmpMin = dbDt;
            }
 
            if (!reader.NextResult())
                throw new ArgumentException(ExecutionStringManager.InvalidWorkflowInstanceEventReader);
 
            //
            // Add any annotations
            while (reader.Read())
            {
                long eventId = reader.GetInt64(0);
                string annotation = null;
 
                if (!reader.IsDBNull(1))
                    annotation = reader.GetString(1);
 
                WorkflowTrackingRecord rec = null;
                if (inst.TryGetValue(eventId, out rec))
                {
                    if (null != rec)
                        rec.Annotations.Add(annotation);
                }
            }
 
            if (!reader.IsClosed)
                reader.Close();
            //
            // Check if we have any WorkflowChange events in this list
            // If so pull back the change actions and reconstruct the args property
            foreach (KeyValuePair<long, WorkflowTrackingRecord> kvp in inst)
            {
                WorkflowTrackingRecord rec = kvp.Value;
                if (TrackingWorkflowEvent.Changed != rec.TrackingWorkflowEvent)
                    continue;
 
                SqlCommand cmd = new SqlCommand();
                cmd.CommandType = CommandType.StoredProcedure;
                cmd.CommandText = "[dbo].[GetWorkflowChangeEventArgs]";
                cmd.Parameters.Add(new SqlParameter("@WorkflowInstanceInternalId", _internalId));
                cmd.Parameters.Add(new SqlParameter("@BeginDateTime", SqlDateTime.MinValue.Value));
                cmd.Parameters.Add(new SqlParameter("@WorkflowInstanceEventId", kvp.Key));
 
                ExecuteRetried(cmd, LoadWorkflowChangeEventArgsFromReader, rec);
            }
 
            _workflowEvents.AddRange(inst.Values);
            //
            // set the min for the next query to the most recent event from this query
            // Don't overwrite the previous min if nothing came back for this query
            if (tmpMin > SqlDateTime.MinValue.Value)
                _instMinDT = tmpMin;
        }
 
        private void LoadWorkflowChangeEventArgsFromReader(SqlDataReader reader, object parameter)
        {
            if (null == reader)
                throw new ArgumentNullException("reader");
 
            if (null == parameter)
                throw new ArgumentNullException("parameter");
 
            WorkflowTrackingRecord record = parameter as WorkflowTrackingRecord;
 
            if (null == record)
                throw new ArgumentException(ExecutionStringManager.InvalidWorkflowChangeEventArgsParameter, "parameter");
 
            if (!reader.Read())
                throw new ArgumentException(ExecutionStringManager.InvalidWorkflowChangeEventArgsReader);
 
            StringReader sr = new StringReader(reader.GetString(0));
 
            //Deserialize the xoml and set the root activity
            Activity def = null;
            WorkflowMarkupSerializer serializer = new WorkflowMarkupSerializer();
            DesignerSerializationManager serializationManager = new DesignerSerializationManager();
            IList errors = null;
            try
            {
                using (serializationManager.CreateSession())
                {
                    using (XmlReader xmlReader = XmlReader.Create(sr))
                    {
                        def = serializer.Deserialize(serializationManager, xmlReader) as Activity;
                        errors = serializationManager.Errors;
                    }
                }
            }
            finally
            {
                sr.Close();
            }
 
            if ((null == def) || ((null != errors) && (errors.Count > 0)))
                throw new WorkflowMarkupSerializationException(ExecutionStringManager.WorkflowMarkupDeserializationError);
 
            if (!reader.NextResult())
                throw new ArgumentException(ExecutionStringManager.InvalidWorkflowChangeEventArgsReader);
            //
            // There is a result set that we don't care about for this scenario, skip it
            if (!reader.NextResult())
                throw new ArgumentException(ExecutionStringManager.InvalidWorkflowChangeEventArgsReader);
 
            List<WorkflowChangeAction> actions = new List<WorkflowChangeAction>();
            DateTime currDT = DateTime.MinValue;
            int currEventOrder = -1;
            int currOrder = -1;
 
            while (reader.Read())
            {
                DateTime dt = reader.GetDateTime(1);
                int eventOrder = reader.GetInt32(2);
                int order = reader.GetInt32(3);
                //
                // Build temp lists as we read the results but
                // only save the last set of change actions
                if (dt > currDT && eventOrder > currEventOrder)
                {
                    currEventOrder = eventOrder;
                    currOrder = order;
                    currDT = dt;
                    actions = new List<WorkflowChangeAction>();
                }
 
                using (sr = new StringReader(reader.GetString(0)))
                {
                    using (serializationManager.CreateSession())
                    {
                        using (XmlReader xmlReader = XmlReader.Create(sr))
                        {
                            ActivityChangeAction aAction = serializer.Deserialize(serializationManager, xmlReader) as ActivityChangeAction;
 
                            errors = serializationManager.Errors;
                            if (null == aAction)
                                throw new WorkflowMarkupSerializationException(ExecutionStringManager.WorkflowMarkupDeserializationError);
 
                            actions.Add(aAction);
                            aAction.ApplyTo(def);
                        }
                    }
                }
            }
 
            record.EventArgs = new TrackingWorkflowChangedEventArgs(actions, def);
        }
 
        private void LoadDef()
        {
            SqlCommand cmd = null;
            //
            // If we don't have the definition load it
            if (null == _def)
            {
                cmd = new SqlCommand();
                cmd.CommandType = CommandType.StoredProcedure;
                cmd.CommandText = "[dbo].[GetWorkflowDefinition]";
                cmd.Parameters.Add(new SqlParameter("@WorkflowInstanceInternalId", _internalId));
 
                ExecuteRetried(cmd, LoadDefFromReader);
            }
            //
            // Now check for changes.  If we find changes apply them to the definition
            cmd = CreateInternalIdDateTimeCommand("[dbo].[GetWorkflowChanges]", _changesMinDT);
 
            ExecuteRetried(cmd, LoadChangesFromReader);
        }
 
        private void LoadDefFromReader(SqlDataReader reader, object parameter)
        {
            if (null == reader)
                throw new ArgumentNullException("reader");
 
            if (!reader.Read())
                throw new ArgumentException(ExecutionStringManager.InvalidDefinitionReader);
 
            StringReader sr = new StringReader(reader.GetString(0));
 
            //Deserialize the xoml and set the root activity
            WorkflowMarkupSerializer serializer = new WorkflowMarkupSerializer();
            DesignerSerializationManager serializationManager = new DesignerSerializationManager();
            IList errors = null;
            try
            {
                using (serializationManager.CreateSession())
                {
                    using (XmlReader xmlReader = XmlReader.Create(sr))
                    {
                        _def = serializer.Deserialize(serializationManager, xmlReader) as Activity;
                        errors = serializationManager.Errors;
                    }
                }
            }
            finally
            {
                sr.Close();
            }
 
            if ((null == _def) || ((null != errors) && (errors.Count > 0)))
                throw new WorkflowMarkupSerializationException(ExecutionStringManager.WorkflowMarkupDeserializationError);
        }
 
        private void LoadChangesFromReader(SqlDataReader reader, object parameter)
        {
            if (!reader.Read())
                return;
            //
            // Reset the min to the most recent change event
            DateTime tmpDT = _changesMinDT;
            if (!reader.IsDBNull(0))
                tmpDT = reader.GetDateTime(0);
 
            if (reader.NextResult())
            {
                WorkflowMarkupSerializer serializer = new WorkflowMarkupSerializer();
                DesignerSerializationManager serializationManager = new DesignerSerializationManager();
                while (reader.Read())
                {
                    IList errors = null;
                    using (StringReader sr = new StringReader(reader.GetString(0)))
                    {
                        using (serializationManager.CreateSession())
                        {
                            using (XmlReader xmlReader = XmlReader.Create(sr))
                            {
                                ActivityChangeAction aAction = serializer.Deserialize(serializationManager, xmlReader) as ActivityChangeAction;
 
                                errors = serializationManager.Errors;
                                if (null != aAction)
                                    aAction.ApplyTo(_def);
                                else
                                    throw new WorkflowMarkupSerializationException(ExecutionStringManager.WorkflowMarkupDeserializationError);
                            }
                        }
                    }
                }
            }
 
            if (tmpDT > _changesMinDT)
            {
                _changed = true;
                _changesMinDT = tmpDT;
            }
        }
 
        private void LoadInvokedWorkflows()
        {
            SqlCommand cmd = new SqlCommand();
 
            cmd.CommandText = "[dbo].[GetInvokedWorkflows]";
            cmd.CommandType = CommandType.StoredProcedure;
 
            SqlParameter param = new SqlParameter("@WorkflowInstanceId", SqlDbType.UniqueIdentifier);
            param.Value = _id;
            cmd.Parameters.Add(param);
 
            param = new SqlParameter("@BeginDateTime", SqlDbType.DateTime);
            param.Value = _invMinDT;
            cmd.Parameters.Add(param);
 
            param = new SqlParameter("@EndDateTime", SqlDbType.DateTime);
            param.Value = _currDT;
            cmd.Parameters.Add(param);
 
            ExecuteRetried(cmd, LoadInvokedWorkflowsFromReader);
        }
 
        private void LoadInvokedWorkflowsFromReader(SqlDataReader reader, object parameter)
        {
            if (null == reader)
                throw new ArgumentNullException("reader");
 
            DateTime tmpMin = SqlDateTime.MinValue.Value;
            while (reader.Read())
            {
                SqlTrackingWorkflowInstance inst = SqlTrackingQuery.BuildInstance(reader, _connectionString);
 
                if (inst.Initialized > tmpMin)
                    tmpMin = inst.Initialized;
 
                _invoked.Add(inst);
            }
            //
            // set the min for the next query to the most recently invoked instance from this query
            // Don't overwrite the previous min if nothing came back for this query
            if (tmpMin > SqlDateTime.MinValue.Value)
                _invMinDT = tmpMin;
        }
 
        private void ExecuteRetried(SqlCommand cmd, LoadFromReader loader)
        {
            ExecuteRetried(cmd, loader, null);
        }
 
        private void ExecuteRetried(SqlCommand cmd, LoadFromReader loader, object loadFromReaderParam)
        {
            SqlDataReader reader = null;
            short count = 0;
            while (true)
            {
                try
                {
                    using (SqlConnection conn = new SqlConnection(_connectionString))
                    {
                        cmd.Connection = conn;
                        cmd.Connection.Open();
                        reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
                        loader(reader, loadFromReaderParam);
                        break;
                    }
                }
                catch (SqlException se)
                {
                    //
                    // Retry if we deadlocked.
                    // All other exceptions bubble
                    if ((_deadlock == se.Number) && (++count < _retries))
                        continue;
 
                    throw;
                }
                finally
                {
                    if ((null != reader) && (!reader.IsClosed))
                        reader.Close();
                }
            }
        }
 
        private SqlCommand CreateInternalIdDateTimeCommand(string commandText, DateTime minDT)
        {
            return CreateInternalIdDateTimeCommand(commandText, minDT, _currDT);
        }
 
        private SqlCommand CreateInternalIdDateTimeCommand(string commandText, DateTime minDT, DateTime maxDT)
        {
            if (null == commandText)
                throw new ArgumentNullException("commandText");
 
            SqlCommand cmd = new SqlCommand();
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.CommandText = commandText;
            //
            // Add parameter values for GetActivityEvents
            SqlParameter param = new SqlParameter("@WorkflowInstanceInternalId", SqlDbType.BigInt);
            param.Value = _internalId;
            cmd.Parameters.Add(param);
 
            param = new SqlParameter("@BeginDateTime", SqlDbType.DateTime);
            param.Value = minDT;
            cmd.Parameters.Add(param);
 
            param = new SqlParameter("@EndDateTime", SqlDbType.DateTime);
            param.Value = maxDT;
            cmd.Parameters.Add(param);
 
            return cmd;
        }
 
    }
}