SqlWorkflowPersistenceService.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 / Hosting / SqlWorkflowPersistenceService.cs / 1305376 / SqlWorkflowPersistenceService.cs

                            using System; 
using System.IO;
using System.Transactions;
using System.Diagnostics;
using System.Data; 
using System.Data.Common;
using System.Data.SqlTypes; 
using System.Data.SqlClient; 
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary; 
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration; 
using System.Text.RegularExpressions;
using System.Security.Permissions; 
using System.Threading; 

using System.Workflow.Runtime.Hosting; 
using System.Workflow.Runtime;
using System.Workflow.ComponentModel;
using System.Globalization;
 
namespace System.Workflow.Runtime.Hosting
{ 
    #region PersistenceDBAccessor 

 
    internal sealed class PendingWorkItem
    {
        public enum ItemType { Instance, CompletedScope, ActivationComplete };
 
        public ItemType Type;
        public Guid InstanceId; 
        public Guid StateId; 
        public Byte[] SerializedActivity;
        public int Status; 
        public int Blocked;
        public string Info;
        public bool Unlocked;
        public SqlDateTime NextTimer; 
    }
 
    ///  
    /// This class does DB accessing work in the context of one connection
    ///  
    internal sealed class PersistenceDBAccessor : IDisposable
    {

        DbResourceAllocator dbResourceAllocator; 
        DbTransaction localTransaction;
        DbConnection connection; 
        bool needToCloseConnection; 
        DbRetry dbRetry = null;
 
        private class RetryReadException : Exception
        {
        }
 
#if DEBUG
        private static Dictionary _persistenceToDatabaseMap = new Dictionary(); 
 
        private static void InsertToDbMap(Guid serviceId, string dbName)
        { 
            lock (_persistenceToDatabaseMap)
            {
                if (!_persistenceToDatabaseMap.ContainsKey(serviceId))
                { 
                    _persistenceToDatabaseMap[serviceId] = dbName;
                    WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService({0}): writing to database: {1}", serviceId.ToString(), dbName); 
                } 
            }
        } 
#endif
        #region Constructor/Disposer

 
        /// 
        /// DB access done under a transaction and uses a connection enlisted to this transaction. 
        ///  
        /// Helper to get database connection/command/store procedure parameters/etc
        /// The transaction to do this work under 
        internal PersistenceDBAccessor(
            DbResourceAllocator dbResourceAllocator,
            System.Transactions.Transaction transaction,
            WorkflowCommitWorkBatchService transactionService) 
        {
            this.dbResourceAllocator = dbResourceAllocator; 
            this.localTransaction = DbResourceAllocator.GetLocalTransaction( 
                transactionService, transaction);
            // Get a connection enlisted to this transaction, may or may not need to be freed depending on 
            // if the transaction does connection sharing
            this.connection = this.dbResourceAllocator.GetEnlistedConnection(
                transactionService, transaction, out needToCloseConnection);
            // 
            // No retries for external transactions
            this.dbRetry = new DbRetry(false); 
        } 

        ///  
        /// DB access done without a transaction in a newly opened connection
        /// 
        /// Helper to get database connection/command/store procedure parameters/etc
        internal PersistenceDBAccessor(DbResourceAllocator dbResourceAllocator, bool enableRetries) 
        {
            this.dbResourceAllocator = dbResourceAllocator; 
            this.dbRetry = new DbRetry(enableRetries); 
            DbConnection conn = null;
            short count = 0; 
            while(true)
            {
                try
                { 
                    WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService OpenConnection start: " + DateTime.UtcNow.ToString("G", System.Globalization.CultureInfo.InvariantCulture));
                    conn = this.dbResourceAllocator.OpenNewConnection(); 
                    WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService. OpenConnection end: " + DateTime.UtcNow.ToString("G", System.Globalization.CultureInfo.InvariantCulture)); 

                    if ((null == conn) || (ConnectionState.Open != conn.State)) 
                        throw new InvalidOperationException(ExecutionStringManager.InvalidConnection);
                    break;
                }
                catch(Exception e) 
                {
                    WorkflowTrace.Host.TraceEvent(TraceEventType.Error, 0, "SqlWorkflowPersistenceService caught exception from OpenConnection: " + e.ToString()); 
 
                    if (dbRetry.TryDoRetry(ref count))
                    { 
                        WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService retrying.");
                        continue;
                    }
                    throw; 
                }
            } 
            connection = conn; 
            needToCloseConnection = true;
        } 


        public void Dispose()
        { 
            if (needToCloseConnection)
            { 
                Debug.Assert(this.connection != null, "No connection to dispose"); 
                this.connection.Dispose();
            } 
        }

        #endregion Constructor/Disposer
 
        #region Public Methods Exposed for the Batch to call
 
        private object DbOwnerId(Guid ownerId) 
        {
            // Empty guid signals no lock, but the database uses null for that, so convert empty to null 
            if (ownerId == Guid.Empty)
                return null;
            return ownerId;
        } 

        public void InsertInstanceState(PendingWorkItem item, Guid ownerId, DateTime ownedUntil) 
        { 
            /* sproc params
            @uidInstanceID uniqueidentifier, 
            @state image,
            @status int,
            @artifacts image,
            @queueingState image, 
            @unlocked int,
            @blocked int, 
            @info ntext, 
            @ownerId uniqueidentifier,
            @ownedUntil datetime 
            @nextTimer datetime
            */
            DbCommand command = NewStoredProcCommand("InsertInstanceState");
            command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("@uidInstanceID", item.InstanceId)); 
            command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("@state", item.SerializedActivity));
            command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("@status", item.Status)); 
            command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("@unlocked", item.Unlocked)); 
            command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("@blocked", item.Blocked));
            command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("@info", item.Info)); 
            command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("@ownedUntil", ownedUntil == DateTime.MaxValue ? SqlDateTime.MaxValue : ownedUntil));
            command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("@ownerID", DbOwnerId(ownerId)));
            command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("@nextTimer", item.NextTimer));
            DbParameter p1 = this.dbResourceAllocator.NewDbParameter(); 
            p1.ParameterName = "@result";
            p1.DbType = DbType.Int32; 
            p1.Value = 0; 
            p1.Direction = ParameterDirection.InputOutput;
            command.Parameters.Add(p1); 
            //command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("@result", DbType.Int32, ParameterDirection.Output));
            DbParameter p2 = this.dbResourceAllocator.NewDbParameter();
            p2.ParameterName = "@currentOwnerID";
            p2.DbType = DbType.Guid; 
            p2.Value = Guid.Empty;
            p2.Direction = ParameterDirection.InputOutput; 
            command.Parameters.Add(p2); 
            //command.Parameters.Add(new DbParameter(this.dbResourceAllocator.NewDbParameter("@currentOwnerID", DbType.Guid, ParameterDirection.InputOutput));
#if DEBUG 
            InsertToDbMap(ownerId, connection.Database);
#endif
            WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService({0}): inserting instance: {1}, unlocking: {2} database: {3}", ownerId.ToString(), item.InstanceId.ToString(), item.Unlocked.ToString(), connection.Database);
            // 
            // Cannot retry locally here as we don't own the tx
            // Rely on external retries at the batch commit or workflow level (for tx scopes) 
            command.ExecuteNonQuery(); 
            CheckOwnershipResult(command);
        } 


        public void InsertCompletedScope(Guid instanceId, Guid scopeId, Byte[] state)
        { 
            /* sproc params
            @completedScopeID uniqueidentifier, 
            @state image 
            */
            DbCommand command = NewStoredProcCommand("InsertCompletedScope"); 
            command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("instanceID", instanceId));
            command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("completedScopeID", scopeId));
            command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("state", state));
            // 
            // Cannot retry locally here as we don't own the tx
            // Rely on external retries at the batch commit or workflow level (for tx scopes) 
            command.ExecuteNonQuery(); 
        }
 
        public void ActivationComplete(Guid instanceId, Guid ownerId)
        {
            /* sproc params
            @instanceID uniqueidentifier, 
            @ownerID uniqueidentifier,
            */ 
            DbCommand command = NewStoredProcCommand("UnlockInstanceState"); 
            command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("@uidInstanceID", instanceId));
            command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("@ownerID", DbOwnerId(ownerId))); 
#if DEBUG
            InsertToDbMap(ownerId, connection.Database);
#endif
            WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService({0}): unlocking instance: {1}, database: {2}", ownerId.ToString(), instanceId.ToString(), connection.Database); 
            command.ExecuteNonQuery();
        } 
 
        public IList RetrieveNonblockingInstanceStateIds(Guid ownerId, DateTime ownedUntil)
        { 
            List gs = null;
            DbDataReader dr = null;
            short count = 0;
            while (true) 
            {
                try 
                { 
                    //
                    // Check and reset the connection as needed before building the command 
                    if ((null == connection) || (ConnectionState.Open != connection.State))
                        ResetConnection();

                    DbCommand command = NewStoredProcCommand("RetrieveNonblockingInstanceStateIds"); 
                    command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("@ownedUntil", ownedUntil == DateTime.MaxValue ? SqlDateTime.MaxValue : ownedUntil));
                    command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("@ownerID", DbOwnerId(ownerId))); 
                    command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("@now", DateTime.UtcNow)); 

                    WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService.RetrieveNonblockingInstanceStateIds ExecuteReader start: " + DateTime.UtcNow.ToString("G", System.Globalization.CultureInfo.InvariantCulture)); 
                    dr = command.ExecuteReader(CommandBehavior.CloseConnection);
                    WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService.RetrieveNonblockingInstanceStateIds ExecuteReader end: " + DateTime.UtcNow.ToString("G", System.Globalization.CultureInfo.InvariantCulture));

                    gs = new List(); 
                    while (dr.Read())
                    { 
                        gs.Add(dr.GetGuid(0)); 
                    }
                    break; 
                }
                catch(Exception e)
                {
                    WorkflowTrace.Host.TraceEvent(TraceEventType.Error, 0, "SqlWorkflowPersistenceService.RetrieveNonblockingInstanceStateIds caught exception from ExecuteReader: " + e.ToString()); 

                    if (dbRetry.TryDoRetry(ref count)) 
                    { 
                        WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService.RetrieveNonblockingInstanceStateIds retrying.");
                        continue; 
                    }
                    throw;
                }
                finally 
                {
                    if (dr != null) 
                        dr.Close(); 
                }
            } 

            return gs;
        }
 
        public bool TryRetrieveANonblockingInstanceStateId(Guid ownerId, DateTime ownedUntil, out Guid instanceId)
        { 
            short count = 0; 
            while (true)
            { 
                try
                {
                    //
                    // Check and reset the connection as needed before building the command 
                    if ((null == connection) || (ConnectionState.Open != connection.State))
                        ResetConnection(); 
 
                    DbCommand command = NewStoredProcCommand("RetrieveANonblockingInstanceStateId");
                    command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("@ownedUntil", ownedUntil == DateTime.MaxValue ? SqlDateTime.MaxValue : ownedUntil)); 
                    command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("@ownerID", DbOwnerId(ownerId)));
                    DbParameter p2 = this.dbResourceAllocator.NewDbParameter();
                    p2.ParameterName = "@uidInstanceID";
                    p2.DbType = DbType.Guid; 
                    p2.Value = null;
                    p2.Direction = ParameterDirection.InputOutput; 
 
                    command.Parameters.Add(p2);
 
                    DbParameter found = this.dbResourceAllocator.NewDbParameter();
                    found.ParameterName = "@found";
                    found.DbType = DbType.Boolean;
                    found.Value = null; 
                    found.Direction = ParameterDirection.Output;
 
                    command.Parameters.Add(found); 

                    WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService.TryRetrieveANonblockingInstanceStateId ExecuteNonQuery start: " + DateTime.UtcNow.ToString("G", System.Globalization.CultureInfo.InvariantCulture)); 
                    command.ExecuteNonQuery();
                    WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService.TryRetrieveANonblockingInstanceStateId ExecuteNonQuery end: " + DateTime.UtcNow.ToString("G", System.Globalization.CultureInfo.InvariantCulture));

                    if ((null != found.Value) && ((bool)found.Value)) 
                    {
                        instanceId = (Guid)p2.Value; 
                        return true; 
                    }
                    else 
                    {
                        instanceId = Guid.Empty;
                        return false;
                    } 
                }
                catch(Exception e) 
                { 
                    WorkflowTrace.Host.TraceEvent(TraceEventType.Error, 0, "SqlWorkflowPersistenceService.TryRetrieveANonblockingInstanceStateId caught exception from ExecuteNonQuery: " + e.ToString());
 
                    if (dbRetry.TryDoRetry(ref count))
                    {
                        WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService.TryRetrieveANonblockingInstanceStateId retrying.");
                        continue; 
                    }
                    throw; 
                } 
            }
 
        }


        public IList RetrieveExpiredTimerIds(Guid ownerId, DateTime ownedUntil) 
        {
            List gs = null; 
            DbDataReader dr = null; 

            short count = 0; 
            while (true)
            {
                try
                { 
                    //
                    // Check and reset the connection as needed before building the command 
                    if ((null == connection) || (ConnectionState.Open != connection.State)) 
                        ResetConnection();
 
                    DbCommand command = NewStoredProcCommand("RetrieveExpiredTimerIds");
                    command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("@ownedUntil", ownedUntil == DateTime.MaxValue ? SqlDateTime.MaxValue : ownedUntil));
                    command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("@ownerID", DbOwnerId(ownerId)));
                    command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("@now", DateTime.UtcNow)); 

                    WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService.RetrieveExpiredTimerIds ExecuteReader start: " + DateTime.UtcNow.ToString("G", System.Globalization.CultureInfo.InvariantCulture)); 
                    dr = command.ExecuteReader(CommandBehavior.CloseConnection); 
                    WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService.RetrieveExpiredTimerIds ExecuteReader end: " + DateTime.UtcNow.ToString("G", System.Globalization.CultureInfo.InvariantCulture));
 
                    gs = new List();
                    while (dr.Read())
                    {
                        gs.Add(dr.GetGuid(0)); 
                    }
                    break; 
                } 
                catch(Exception e)
                { 
                    WorkflowTrace.Host.TraceEvent(TraceEventType.Error, 0, "SqlWorkflowPersistenceService.RetrieveExpiredTimerIds caught exception from ExecuteReader: " + e.ToString());

                    if (dbRetry.TryDoRetry(ref count))
                    { 
                        WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService.RetrieveExpiredTimerIds retrying.");
                        continue; 
                    } 
                    throw;
                } 
                finally
                {
                    if (dr != null)
                        dr.Close(); 
                }
            } 
 
            return gs;
        } 

        public Byte[] RetrieveInstanceState(Guid instanceStateId, Guid ownerId, DateTime timeout)
        {
            short count = 0; 
            byte[] state = null;
            while (true) 
            { 
                try
                { 
                    //
                    // Check and reset the connection as needed before building the command
                    if ((null == connection) || (ConnectionState.Open != connection.State))
                        ResetConnection(); 

                    DbCommand command = NewStoredProcCommand("RetrieveInstanceState"); 
                    command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("@uidInstanceID", instanceStateId)); 
                    command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("@ownerID", DbOwnerId(ownerId)));
                    command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("@ownedUntil", timeout == DateTime.MaxValue ? SqlDateTime.MaxValue : timeout)); 
                    DbParameter p1 = this.dbResourceAllocator.NewDbParameter();
                    p1.ParameterName = "@result";
                    p1.DbType = DbType.Int32;
                    p1.Value = 0; 
                    p1.Direction = ParameterDirection.InputOutput;
                    command.Parameters.Add(p1); 
                    //command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("@result", DbType.Int32, ParameterDirection.Output)); 
                    DbParameter p2 = this.dbResourceAllocator.NewDbParameter();
                    p2.ParameterName = "@currentOwnerID"; 
                    p2.DbType = DbType.Guid;
                    p2.Value = Guid.Empty;
                    p2.Direction = ParameterDirection.InputOutput;
                    command.Parameters.Add(p2); 
                    //command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("@currentOwnerID", DbType.Guid, ParameterDirection.InputOutput));
#if DEBUG 
                    InsertToDbMap(ownerId, connection.Database); 
#endif
                    WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService({0}): retreiving instance: {1}, database: {2}", ownerId.ToString(), instanceStateId.ToString(), connection.Database); 
                    state = RetrieveStateFromDB(command, true, instanceStateId);

                    break;
                } 
                catch (Exception e)
                { 
                    WorkflowTrace.Host.TraceEvent(TraceEventType.Error, 0, "SqlWorkflowPersistenceService.RetrieveInstanceState caught exception: " + e.ToString()); 

                    if (dbRetry.TryDoRetry(ref count)) 
                    {
                        WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService.RetrieveInstanceState retrying.");
                        continue;
                    } 
                    else if (e is RetryReadException)    // ### hardcoded retry to work around sql ADM64 read bug ###
                    { 
                        count++; 
                        if (count < 10)
                            continue; 
                        else
                            break;  // give up
                    }
                    throw; 
                }
            } 
 
            if (state == null || state.Length == 0)
            { 
                Exception e = new InvalidOperationException(string.Format(Thread.CurrentThread.CurrentCulture, ExecutionStringManager.InstanceNotFound, instanceStateId));
                e.Data["WorkflowNotFound"] = true;
                throw e;
            } 

            return state; 
        } 

        public Byte[] RetrieveCompletedScope(Guid scopeId) 
        {
            short count = 0;
            byte[] state = null;
            while (true) 
            {
                try 
                { 
                    //
                    // Check and reset the connection as needed before building the command 
                    if ((null == connection) || (ConnectionState.Open != connection.State))
                        ResetConnection();

                    DbCommand command = NewStoredProcCommand("RetrieveCompletedScope"); 
                    command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("@completedScopeID", scopeId));
                    DbParameter p1 = this.dbResourceAllocator.NewDbParameter(); 
                    p1.ParameterName = "@result"; 
                    p1.DbType = DbType.Int32;
                    p1.Value = 0; 
                    p1.Direction = ParameterDirection.InputOutput;
                    command.Parameters.Add(p1);

                    state = RetrieveStateFromDB(command, false, WorkflowEnvironment.WorkflowInstanceId); 

                    break; 
                } 
                catch(Exception e)
                { 
                    WorkflowTrace.Host.TraceEvent(TraceEventType.Error, 0, "SqlWorkflowPersistenceService.RetrieveCompletedScope caught exception: " + e.ToString());

                    if (dbRetry.TryDoRetry(ref count))
                    { 
                        WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService.RetrieveCompletedScope retrying.");
                        continue; 
                    } 
                    else if (e is RetryReadException)    // ### hardcoded retry to work around sql ADM64 read bug ###
                    { 
                        count++;
                        if (count < 10)
                            continue;
                        else 
                            break;  // give up
                    } 
                    throw; 
                }
            } 

            if (state == null || state.Length == 0)
                throw new InvalidOperationException(
                    string.Format(Thread.CurrentThread.CurrentCulture, 
                    ExecutionStringManager.CompletedScopeNotFound, scopeId));
            return state; 
        } 

        #endregion 

        #region private DB accessing methods

        ///  
        /// This should only be called for non batch commits
        ///  
        ///  
        private DbConnection ResetConnection()
        { 
            if (null != localTransaction)
                throw new InvalidOperationException(ExecutionStringManager.InvalidOpConnectionReset);

            if (!needToCloseConnection) 
                throw new InvalidOperationException(ExecutionStringManager.InvalidOpConnectionNotLocal);
 
            if ((null != connection) && (ConnectionState.Closed != connection.State)) 
                connection.Close();
            connection.Dispose(); 

            connection = this.dbResourceAllocator.OpenNewConnection();

            return connection; 
        }
 
        ///  
        /// Returns a stored procedure type DBCommand object with current connection and transaction
        ///  
        /// 
        /// the command object
        private DbCommand NewStoredProcCommand(string commandText)
        { 
            DbCommand command = DbResourceAllocator.NewCommand(commandText, this.connection, this.localTransaction);
            command.CommandType = CommandType.StoredProcedure; 
 
            return command;
        } 

        private static void CheckOwnershipResult(DbCommand command)
        {
            DbParameter result = command.Parameters["@result"]; 
            if (result != null && result.Value != null && (int)result.Value == -2)   // -2 is an ownership conflict
            { 
                if (command.Parameters.Contains("@currentOwnerID")) 
                {
                    Guid currentOwnerId = Guid.Empty; 
                    if (command.Parameters["@currentOwnerID"].Value is System.Guid)
                        currentOwnerId = (Guid)command.Parameters["@currentOwnerID"].Value;
                    Guid myId = (Guid)command.Parameters["@ownerID"].Value;
                    Guid instId = (Guid)command.Parameters["@uidInstanceID"].Value; 
                    WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService({0}): owership violation with {1} on instance {2}", myId.ToString(), currentOwnerId.ToString(), instId);
                } 
                DbParameter instanceId = command.Parameters["@uidInstanceID"]; 
                throw new WorkflowOwnershipException((Guid)instanceId.Value);
            } 
        }

        /// 
        /// Helper to Public methods RetrieveInstanceState and RetrieveCompletedScope. 
        /// Retrieves an object from the DB by calling the specified stored procedure with specified stored proc params.
        ///  
        /// Contains the stored procedure setting to be used to query against the Database 
        /// an object to be casted to an activity
        ///     In case of RetrieveInstanceState, only running or suspended instances are returned and 
        ///     exception is thrown for completed/terminated/not-found instances
        /// 
        private static Byte[] RetrieveStateFromDB(DbCommand command, bool checkOwnership, Guid instanceId)
        { 
            DbDataReader dr = null;
            byte[] result = null; 
 
            try
            { 
                WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService.RetrieveStateFromDB {0} ExecuteReader start: {1}", instanceId, DateTime.UtcNow.ToString("G", System.Globalization.CultureInfo.InvariantCulture));
                dr = command.ExecuteReader();
                WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService.RetrieveStateFromDB {0} ExecuteReader end: {1}", instanceId, DateTime.UtcNow.ToString("G", System.Globalization.CultureInfo.InvariantCulture));
 
                if (dr.Read())
                { 
                    result = (byte[])dr.GetValue(0); 
                }
                else 
                {
                    DbParameter resultParam = command.Parameters["@result"];
                    if (resultParam == null || resultParam.Value == null)
                    { 
                        WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService.RetrieveStateFromDB Failed to read results {0}", instanceId);
                    } 
                    else if ((int)resultParam.Value > 0)    // found results but failed to read - sql bug - retry the query 
                    {
                        WorkflowTrace.Host.TraceEvent(TraceEventType.Error, 0, "SqlWorkflowPersistenceService.RetrieveStateFromDB Failed to read results {1}, @result == {0}", (int)resultParam.Value, instanceId); 
                        throw new RetryReadException();
                    }
                }
            } 
            finally
            { 
                if (dr != null) 
                    dr.Close();
            } 

            if (checkOwnership)
                CheckOwnershipResult(command);
 
            return result;
        } 
 
        #endregion private DB accessing methods
 

        internal IEnumerable RetrieveAllInstanceDescriptions()
        {
            List retval = new List(); 
            DbDataReader dr = null;
            try 
            { 
                DbCommand command = NewStoredProcCommand("RetrieveAllInstanceDescriptions");
                dr = command.ExecuteReader(CommandBehavior.CloseConnection); 
                while (dr.Read())
                {
                    retval.Add(new SqlPersistenceWorkflowInstanceDescription(
                        dr.GetGuid(0), 
                        (WorkflowStatus)dr.GetInt32(1),
                        dr.GetInt32(2) == 1 ? true : false, 
                        dr.GetString(3), 
                        (SqlDateTime)dr.GetDateTime(4)
                    )); 
                }
            }
            finally
            { 
                if (dr != null)
                    dr.Close(); 
            } 
            return retval;
        } 
    }
    #endregion

    public class SqlWorkflowPersistenceService : WorkflowPersistenceService, IPendingWork 
    {
        #region constants 
        // Configure parameters for this services 
        private const string InstanceOwnershipTimeoutSecondsToken = "OwnershipTimeoutSeconds";
        private const string UnloadOnIdleToken = "UnloadOnIdle"; 
        private const string EnableRetriesToken = "EnableRetries";

        #endregion constants
 
        private bool _enableRetries = false;
        private bool _ignoreCommonEnableRetries = false; 
        private DbResourceAllocator _dbResourceAllocator; 
        private WorkflowCommitWorkBatchService _transactionService;
        private Guid _serviceInstanceId = Guid.Empty; 
        private TimeSpan _ownershipDelta;
        private Boolean _unloadOnIdle;
        const string LoadingIntervalToken = "LoadIntervalSeconds";
        TimeSpan loadingInterval = new TimeSpan(0, 2, 0); 
        private TimeSpan maxLoadingInterval = new TimeSpan(365, 0, 0, 0, 0);
        SmartTimer loadingTimer; 
        object timerLock = new object(); 
        TimeSpan infinite = new TimeSpan(Timeout.Infinite);
        private static int _deadlock = 1205; 

        // Saved from constructor input to be used in service start initialization
        NameValueCollection configParameters;
        string unvalidatedConnectionString; 

        public Guid ServiceInstanceId 
        { 
            get
            { 
                return _serviceInstanceId;
            }
        }
 
        public TimeSpan LoadingInterval
        { 
            get { return loadingInterval; } 
        }
 
        public bool EnableRetries
        {
            get { return _enableRetries; }
            set 
            {
                _enableRetries = value; 
                _ignoreCommonEnableRetries = true; 
            }
        } 

        private DateTime OwnershipTimeout
        {
            get 
            {
                DateTime timeout; 
                if (_ownershipDelta == TimeSpan.MaxValue) 
                    timeout = DateTime.MaxValue;
                else 
                    timeout = DateTime.UtcNow + _ownershipDelta;
                return timeout;
            }
        } 

        public SqlWorkflowPersistenceService(string connectionString) 
        { 
            if (String.IsNullOrEmpty(connectionString))
                throw new ArgumentNullException("connectionString", ExecutionStringManager.MissingConnectionString); 

            this.unvalidatedConnectionString = connectionString;
        }
 
        public SqlWorkflowPersistenceService(NameValueCollection parameters)
        { 
            if (parameters == null) 
                throw new ArgumentNullException("parameters", ExecutionStringManager.MissingParameters);
 
            _ownershipDelta = TimeSpan.MaxValue;   // default is to never timeout
            if (parameters != null)
            {
                foreach (string key in parameters.Keys) 
                {
                    if (key.Equals(DbResourceAllocator.ConnectionStringToken, StringComparison.OrdinalIgnoreCase)) 
                    { 
                        // the resource allocator (below) will process the connection string
                    } 
                    else if (key.Equals(SqlWorkflowPersistenceService.InstanceOwnershipTimeoutSecondsToken, StringComparison.OrdinalIgnoreCase))
                    {
                        int seconds = Convert.ToInt32(parameters[SqlWorkflowPersistenceService.InstanceOwnershipTimeoutSecondsToken],System.Globalization.CultureInfo.CurrentCulture);
                        if ( seconds < 0 ) 
                            throw new ArgumentOutOfRangeException(InstanceOwnershipTimeoutSecondsToken, seconds, ExecutionStringManager.InvalidOwnershipTimeoutValue);
                        _ownershipDelta = new TimeSpan(0, 0, seconds); 
                        _serviceInstanceId = Guid.NewGuid(); 
                        continue;
                    } 
                    else if (key.Equals(SqlWorkflowPersistenceService.UnloadOnIdleToken, StringComparison.OrdinalIgnoreCase))
                    {
                        _unloadOnIdle = bool.Parse(parameters[key]);
                    } 
                    else if (key.Equals(LoadingIntervalToken, StringComparison.OrdinalIgnoreCase))
                    { 
                        int interval = int.Parse(parameters[key], CultureInfo.CurrentCulture); 
                        if (interval > 0)
                            this.loadingInterval = new TimeSpan(0, 0, interval); 
                        else
                            this.loadingInterval = TimeSpan.Zero;
                        if (this.loadingInterval > maxLoadingInterval)
                            throw new ArgumentOutOfRangeException(LoadingIntervalToken, this.LoadingInterval, ExecutionStringManager.LoadingIntervalTooLarge); 
                    }
                    else if (key.Equals(SqlWorkflowPersistenceService.EnableRetriesToken, StringComparison.OrdinalIgnoreCase)) 
                    { 
                        //
                        // We have a local value for enable retries 
                        _enableRetries = bool.Parse(parameters[key]);
                        _ignoreCommonEnableRetries = true;
                    }
                    else 
                    {
                        throw new ArgumentException( 
                            String.Format(Thread.CurrentThread.CurrentCulture, ExecutionStringManager.UnknownConfigurationParameter, key), "parameters"); 
                    }
                } 
            }

            this.configParameters = parameters;
        } 

        public SqlWorkflowPersistenceService(string connectionString, bool unloadOnIdle, TimeSpan instanceOwnershipDuration, TimeSpan loadingInterval) 
        { 
            if (String.IsNullOrEmpty(connectionString))
                throw new ArgumentNullException("connectionString", ExecutionStringManager.MissingConnectionString); 
            if (loadingInterval > maxLoadingInterval)
                throw new ArgumentOutOfRangeException("loadingInterval", loadingInterval, ExecutionStringManager.LoadingIntervalTooLarge);
            if (instanceOwnershipDuration < TimeSpan.Zero)
                throw new ArgumentOutOfRangeException("instanceOwnershipDuration", instanceOwnershipDuration, ExecutionStringManager.InvalidOwnershipTimeoutValue); 

            this._ownershipDelta = instanceOwnershipDuration; 
            this._unloadOnIdle = unloadOnIdle; 
            this.loadingInterval = loadingInterval;
            this.unvalidatedConnectionString = connectionString; 
            _serviceInstanceId = Guid.NewGuid();
        }

        #region WorkflowRuntimeService 

        override protected internal void Start() 
        { 
            WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService({1}): Starting, LoadInternalSeconds={0}", loadingInterval.TotalSeconds, _serviceInstanceId.ToString());
 
            _dbResourceAllocator = new DbResourceAllocator(this.Runtime, this.configParameters, this.unvalidatedConnectionString);

            // Check connection string mismatch if using SharedConnectionWorkflowTransactionService
            _transactionService = Runtime.GetService(); 
            _dbResourceAllocator.DetectSharedConnectionConflict(_transactionService);
 
            // 
            // If we didn't find a local value for enable retries
            // check in the common section 
            if ((!_ignoreCommonEnableRetries)&&(null != base.Runtime))
            {
                NameValueConfigurationCollection commonConfigurationParameters = base.Runtime.CommonParameters;
                if (commonConfigurationParameters != null) 
                {
                    // Then scan for connection string in the common configuration parameters section 
                    foreach (string key in commonConfigurationParameters.AllKeys) 
                    {
                        if (string.Compare(EnableRetriesToken, key, StringComparison.OrdinalIgnoreCase) == 0) 
                        {
                            _enableRetries = bool.Parse(commonConfigurationParameters[key].Value);
                            break;
                        } 
                    }
                } 
            } 

            base.Start(); 
        }

        protected override void OnStarted()
        { 
            if (loadingInterval > TimeSpan.Zero)
            { 
                lock (timerLock) 
                {
                    base.OnStarted(); 
                    loadingTimer = new SmartTimer(new TimerCallback(LoadWorkflowsWithExpiredTimers), null, loadingInterval, loadingInterval);
                }
            }
            RecoverRunningWorkflowInstances(); 
        }
 
        protected internal override void  Stop() 
        {
            WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService({0}): Stopping", _serviceInstanceId.ToString()); 
            lock (timerLock)
            {
                base.Stop();
                if (loadingTimer != null) 
                {
                    loadingTimer.Dispose(); 
                    loadingTimer = null; 
                }
            } 
        }

        #endregion WorkflowRuntimeService
 
        private void RecoverRunningWorkflowInstances()
        { 
            if (Guid.Empty == _serviceInstanceId) 
            {
                // 
                // Only one host, get all the ids in one go
                IList instanceIds = null;
                using (PersistenceDBAccessor persistenceDBAccessor = new PersistenceDBAccessor(_dbResourceAllocator, _enableRetries))
                { 
                    instanceIds = persistenceDBAccessor.RetrieveNonblockingInstanceStateIds(_serviceInstanceId, OwnershipTimeout);
                } 
                foreach (Guid instanceId in instanceIds) 
                {
                    try 
                    {
                        WorkflowInstance instance = Runtime.GetWorkflow(instanceId);
                        instance.Load();
                    } 
                    catch (Exception e)
                    { 
                        RaiseServicesExceptionNotHandledEvent(e, instanceId); 
                    }
                } 
            }
            else
            {
                using (PersistenceDBAccessor persistenceDBAccessor = new PersistenceDBAccessor(_dbResourceAllocator, _enableRetries)) 
                {
                    // 
                    // Load one at a time to avoid thrashing with other hosts 
                    Guid instanceId;
                    while (persistenceDBAccessor.TryRetrieveANonblockingInstanceStateId(_serviceInstanceId, OwnershipTimeout, out instanceId)) 
                    {
                        try
                        {
                            WorkflowInstance instance = Runtime.GetWorkflow(instanceId); 
                            instance.Load();
                        } 
                        catch (Exception e) 
                        {
                            RaiseServicesExceptionNotHandledEvent(e, instanceId); 
                        }
                    }
                }
            } 
        }
 
 
        private void LoadWorkflowsWithExpiredTimers(object ignored)
        { 
            lock (timerLock)
            {
                if (this.State == WorkflowRuntimeServiceState.Started)
                { 
                    IList ids = null;
                    try 
                    { 

                        ids = LoadExpiredTimerIds(); 
                    }
                    catch (Exception e)
                    {
                        RaiseServicesExceptionNotHandledEvent(e, Guid.Empty); 
                    }
                    if (ids != null) 
                    { 
                        foreach (Guid id in ids)
                        { 
                            WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService({1}): Loading instance with expired timers {0}", id, _serviceInstanceId.ToString());
                            try
                            {
                                Runtime.GetWorkflow(id).Load(); 
                            }
                            // Ignore cases where the workflow has been stolen out from under us 
                            catch (WorkflowOwnershipException) 
                            { }
                            catch (ObjectDisposedException) 
                            {
                                throw;
                            }
                            catch (InvalidOperationException ioe) 
                            {
                                if ( !ioe.Data.Contains("WorkflowNotFound")) 
                                    RaiseServicesExceptionNotHandledEvent(ioe, id); 
                            }
                            catch (Exception e) 
                            {
                                RaiseServicesExceptionNotHandledEvent(e, id);
                            }
                        } 
                    }
                } 
            } 
        }
 

        // uidInstanceID, status, blocked, info, nextTimer

        internal protected override void SaveWorkflowInstanceState(Activity rootActivity, bool unlock) 
        {
            if (rootActivity == null) 
                throw new ArgumentNullException("rootActivity"); 

            WorkflowStatus workflowStatus = WorkflowPersistenceService.GetWorkflowStatus(rootActivity); 
            bool isInstanceBlocked = WorkflowPersistenceService.GetIsBlocked(rootActivity);
            string instanceInfo = WorkflowPersistenceService.GetSuspendOrTerminateInfo(rootActivity);
            Guid contextGuid = (Guid)rootActivity.GetValue(Activity.ActivityContextGuidProperty);
 
            PendingWorkItem item = new PendingWorkItem();
            item.Type = PendingWorkItem.ItemType.Instance; 
            item.InstanceId = WorkflowEnvironment.WorkflowInstanceId; 
            if (workflowStatus != WorkflowStatus.Completed && workflowStatus != WorkflowStatus.Terminated)
                item.SerializedActivity = WorkflowPersistenceService.GetDefaultSerializedForm(rootActivity); 
            else
                item.SerializedActivity = new Byte[0];
            item.Status = (int)workflowStatus;
            item.Blocked = isInstanceBlocked ? 1 : 0; 
            item.Info = instanceInfo;
            item.StateId = contextGuid; 
            item.Unlocked = unlock; 
            TimerEventSubscriptionCollection timers = (TimerEventSubscriptionCollection)rootActivity.GetValue(TimerEventSubscriptionCollection.TimerCollectionProperty);
            Debug.Assert(timers != null, "TimerEventSubscriptionCollection should never be null, but it is"); 
            TimerEventSubscription sub = timers.Peek();
            item.NextTimer = sub == null ? SqlDateTime.MaxValue : sub.ExpiresAt;
            if (item.Info == null)
                item.Info = ""; 

            WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService({4}):Committing instance {0}, Blocked={1}, Unlocked={2}, NextTimer={3}", contextGuid.ToString(), item.Blocked, item.Unlocked, item.NextTimer.Value.ToLocalTime(), _serviceInstanceId.ToString()); 
 
            WorkflowEnvironment.WorkBatch.Add(this, item);
        } 

        internal protected override void UnlockWorkflowInstanceState(Activity rootActivity)
        {
            PendingWorkItem item = new PendingWorkItem(); 
            item.Type = PendingWorkItem.ItemType.ActivationComplete;
            item.InstanceId = WorkflowEnvironment.WorkflowInstanceId; 
            WorkflowEnvironment.WorkBatch.Add(this, item); 
            WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService({0}):Unlocking instance {1}", _serviceInstanceId.ToString(), item.InstanceId.ToString());
        } 

        internal protected override Activity LoadWorkflowInstanceState(Guid id)
        {
            using (PersistenceDBAccessor persistenceDBAccessor = new PersistenceDBAccessor(_dbResourceAllocator, _enableRetries)) 
            {
                WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService({0}):Loading instance {1}", _serviceInstanceId.ToString(), id.ToString()); 
                byte[] state = persistenceDBAccessor.RetrieveInstanceState(id, _serviceInstanceId, OwnershipTimeout); 
                return WorkflowPersistenceService.RestoreFromDefaultSerializedForm(state, null);
            } 

        }

        public IList LoadExpiredTimerWorkflowIds() 
        {
            if (State == WorkflowRuntimeServiceState.Started) 
            { 
                return LoadExpiredTimerIds();
            } 
            else
            {
                throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, ExecutionStringManager.WorkflowRuntimeNotStarted));
            } 
        }
 
        private IList LoadExpiredTimerIds() 
        {
            using (PersistenceDBAccessor persistenceDBAccessor = new PersistenceDBAccessor(_dbResourceAllocator, _enableRetries)) 
            {
                return persistenceDBAccessor.RetrieveExpiredTimerIds(_serviceInstanceId, OwnershipTimeout);
            }
        } 

        internal protected override void SaveCompletedContextActivity(Activity completedScopeActivity) 
        { 
            PendingWorkItem item = new PendingWorkItem();
            item.Type = PendingWorkItem.ItemType.CompletedScope; 
            item.SerializedActivity = WorkflowPersistenceService.GetDefaultSerializedForm(completedScopeActivity);
            item.InstanceId = WorkflowEnvironment.WorkflowInstanceId;
            item.StateId = ((ActivityExecutionContextInfo)completedScopeActivity.GetValue(Activity.ActivityExecutionContextInfoProperty)).ContextGuid;
 
            WorkflowEnvironment.WorkBatch.Add(this, item);
        } 
 
        internal protected override Activity LoadCompletedContextActivity(Guid id, Activity outerActivity)
        { 
            using (PersistenceDBAccessor persistenceDBAccessor = new PersistenceDBAccessor(_dbResourceAllocator,_enableRetries))
            {
                byte[] state = persistenceDBAccessor.RetrieveCompletedScope(id);
                return WorkflowPersistenceService.RestoreFromDefaultSerializedForm(state, outerActivity); 
            }
        } 
 
        internal protected override bool UnloadOnIdle(Activity activity)
        { 
            return _unloadOnIdle;
        }

        public IEnumerable GetAllWorkflows() 
        {
            if (State == WorkflowRuntimeServiceState.Started) 
            { 
                using (PersistenceDBAccessor persistenceDBAccessor = new PersistenceDBAccessor(_dbResourceAllocator,_enableRetries))
                { 
                    return persistenceDBAccessor.RetrieveAllInstanceDescriptions();
                }
            }
            else 
            {
                throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, ExecutionStringManager.WorkflowRuntimeNotStarted)); 
            } 
        }
 
        #region IPendingWork methods

        bool IPendingWork.MustCommit(ICollection items)
        { 
            return true;
        } 
 
        /// 
        /// Commmit the work items using the transaction 
        /// 
        /// 
        /// 
        void IPendingWork.Commit(System.Transactions.Transaction transaction, ICollection items) 
        {
            PersistenceDBAccessor persistenceDBAccessor = null; 
            try 
            {
                persistenceDBAccessor = new PersistenceDBAccessor(_dbResourceAllocator, transaction, _transactionService); 
                foreach (PendingWorkItem item in items)
                {
                    switch (item.Type)
                    { 
                        case PendingWorkItem.ItemType.Instance:
                            persistenceDBAccessor.InsertInstanceState(item, _serviceInstanceId, OwnershipTimeout); 
                            break; 

                        case PendingWorkItem.ItemType.CompletedScope: 
                            persistenceDBAccessor.InsertCompletedScope(item.InstanceId, item.StateId, item.SerializedActivity);
                            break;

                        case PendingWorkItem.ItemType.ActivationComplete: 
                            persistenceDBAccessor.ActivationComplete(item.InstanceId, _serviceInstanceId);
                            break; 
 
                        default:
                            Debug.Assert(false, "Committing unknown pending work item type in SqlPersistenceService.Commit()"); 
                            break;
                    }

                } 
            }
            catch (SqlException se) 
            { 
                WorkflowTrace.Runtime.TraceEvent(TraceEventType.Error, 0, "SqlWorkflowPersistenceService({1})Exception thrown while persisting instance: {0}", se.Message, _serviceInstanceId.ToString());
                WorkflowTrace.Runtime.TraceEvent(TraceEventType.Error, 0, "stacktrace : {0}", se.StackTrace); 

                if (se.Number == _deadlock)
                {
                    PersistenceException pe = new PersistenceException(se.Message, se); 
                    throw pe;
                } 
                else 
                {
                    throw; 
                }

            }
            catch (Exception e) 
            {
                WorkflowTrace.Runtime.TraceEvent(TraceEventType.Error, 0, "SqlWorkflowPersistenceService({1}): Exception thrown while persisting instance: {0}", e.Message, _serviceInstanceId.ToString()); 
                WorkflowTrace.Runtime.TraceEvent(TraceEventType.Error, 0, "stacktrace : {0}", e.StackTrace); 
                throw e;
            } 
            finally
            {
                if (persistenceDBAccessor != null)
                    persistenceDBAccessor.Dispose(); 
            }
        } 
 

        ///  
        /// Perform necesssary cleanup. Called when the scope
        /// has finished processing this batch of work items
        /// 
        ///  
        /// 
        void IPendingWork.Complete(bool succeeded, ICollection items) 
        { 
            if(loadingTimer != null && succeeded)
            { 
                foreach (PendingWorkItem item in items)
                {
                    if (item.Type.Equals(PendingWorkItem.ItemType.Instance))
                    { 
                        loadingTimer.Update((DateTime)item.NextTimer);
                    } 
                } 
            }
        } 

        #endregion IPendingWork Methods

    } 

    internal class SmartTimer : IDisposable 
    { 
        private object locker = new object();
        private Timer timer; 
        private DateTime next;
        private bool nextChanged;
        private TimeSpan period;
        private TimerCallback callback; 
        private TimeSpan minUpdate = new TimeSpan(0, 0, 5);
        private TimeSpan infinite = new TimeSpan(Timeout.Infinite); 
 
        public SmartTimer(TimerCallback callback, object state, TimeSpan due, TimeSpan period)
        { 
            this.period = period;
            this.callback = callback;
            this.next = DateTime.UtcNow + due;
            this.timer = new Timer(HandleCallback, state, due, infinite); 
        }
 
        public void Update(DateTime newNext) 
        {
            if (newNext < next && (next - DateTime.UtcNow) > minUpdate) 
            {
                lock (locker)
                {
                    if (newNext < next && (next - DateTime.UtcNow) > minUpdate && timer != null) 
                    {
                        next = newNext; 
                        nextChanged = true; 
                        TimeSpan when = next - DateTime.UtcNow;
                        if(when < TimeSpan.Zero) 
                            when = TimeSpan.Zero;
                        timer.Change(when, infinite);
                    }
                } 
            }
        } 
 
        private void HandleCallback(object state)
        { 
            try
            {
                callback(state);
            } 
            finally
            { 
                lock (locker) 
                {
                    if (timer != null) 
                    {
                        if (!nextChanged)
                            next = DateTime.UtcNow + period;
                        else 
                            nextChanged = false;
                        TimeSpan when = next - DateTime.UtcNow; 
                        if (when < TimeSpan.Zero) 
                            when = TimeSpan.Zero;
                        timer.Change(when, infinite); 
                    }
                }
            }
        } 

 
        public void Dispose() 
        {
            lock (locker) 
            {
                if (timer != null)
                {
                    timer.Dispose(); 
                    timer = null;
                } 
            } 
        }
    } 
}


// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
using System; 
using System.IO;
using System.Transactions;
using System.Diagnostics;
using System.Data; 
using System.Data.Common;
using System.Data.SqlTypes; 
using System.Data.SqlClient; 
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary; 
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration; 
using System.Text.RegularExpressions;
using System.Security.Permissions; 
using System.Threading; 

using System.Workflow.Runtime.Hosting; 
using System.Workflow.Runtime;
using System.Workflow.ComponentModel;
using System.Globalization;
 
namespace System.Workflow.Runtime.Hosting
{ 
    #region PersistenceDBAccessor 

 
    internal sealed class PendingWorkItem
    {
        public enum ItemType { Instance, CompletedScope, ActivationComplete };
 
        public ItemType Type;
        public Guid InstanceId; 
        public Guid StateId; 
        public Byte[] SerializedActivity;
        public int Status; 
        public int Blocked;
        public string Info;
        public bool Unlocked;
        public SqlDateTime NextTimer; 
    }
 
    ///  
    /// This class does DB accessing work in the context of one connection
    ///  
    internal sealed class PersistenceDBAccessor : IDisposable
    {

        DbResourceAllocator dbResourceAllocator; 
        DbTransaction localTransaction;
        DbConnection connection; 
        bool needToCloseConnection; 
        DbRetry dbRetry = null;
 
        private class RetryReadException : Exception
        {
        }
 
#if DEBUG
        private static Dictionary _persistenceToDatabaseMap = new Dictionary(); 
 
        private static void InsertToDbMap(Guid serviceId, string dbName)
        { 
            lock (_persistenceToDatabaseMap)
            {
                if (!_persistenceToDatabaseMap.ContainsKey(serviceId))
                { 
                    _persistenceToDatabaseMap[serviceId] = dbName;
                    WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService({0}): writing to database: {1}", serviceId.ToString(), dbName); 
                } 
            }
        } 
#endif
        #region Constructor/Disposer

 
        /// 
        /// DB access done under a transaction and uses a connection enlisted to this transaction. 
        ///  
        /// Helper to get database connection/command/store procedure parameters/etc
        /// The transaction to do this work under 
        internal PersistenceDBAccessor(
            DbResourceAllocator dbResourceAllocator,
            System.Transactions.Transaction transaction,
            WorkflowCommitWorkBatchService transactionService) 
        {
            this.dbResourceAllocator = dbResourceAllocator; 
            this.localTransaction = DbResourceAllocator.GetLocalTransaction( 
                transactionService, transaction);
            // Get a connection enlisted to this transaction, may or may not need to be freed depending on 
            // if the transaction does connection sharing
            this.connection = this.dbResourceAllocator.GetEnlistedConnection(
                transactionService, transaction, out needToCloseConnection);
            // 
            // No retries for external transactions
            this.dbRetry = new DbRetry(false); 
        } 

        ///  
        /// DB access done without a transaction in a newly opened connection
        /// 
        /// Helper to get database connection/command/store procedure parameters/etc
        internal PersistenceDBAccessor(DbResourceAllocator dbResourceAllocator, bool enableRetries) 
        {
            this.dbResourceAllocator = dbResourceAllocator; 
            this.dbRetry = new DbRetry(enableRetries); 
            DbConnection conn = null;
            short count = 0; 
            while(true)
            {
                try
                { 
                    WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService OpenConnection start: " + DateTime.UtcNow.ToString("G", System.Globalization.CultureInfo.InvariantCulture));
                    conn = this.dbResourceAllocator.OpenNewConnection(); 
                    WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService. OpenConnection end: " + DateTime.UtcNow.ToString("G", System.Globalization.CultureInfo.InvariantCulture)); 

                    if ((null == conn) || (ConnectionState.Open != conn.State)) 
                        throw new InvalidOperationException(ExecutionStringManager.InvalidConnection);
                    break;
                }
                catch(Exception e) 
                {
                    WorkflowTrace.Host.TraceEvent(TraceEventType.Error, 0, "SqlWorkflowPersistenceService caught exception from OpenConnection: " + e.ToString()); 
 
                    if (dbRetry.TryDoRetry(ref count))
                    { 
                        WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService retrying.");
                        continue;
                    }
                    throw; 
                }
            } 
            connection = conn; 
            needToCloseConnection = true;
        } 


        public void Dispose()
        { 
            if (needToCloseConnection)
            { 
                Debug.Assert(this.connection != null, "No connection to dispose"); 
                this.connection.Dispose();
            } 
        }

        #endregion Constructor/Disposer
 
        #region Public Methods Exposed for the Batch to call
 
        private object DbOwnerId(Guid ownerId) 
        {
            // Empty guid signals no lock, but the database uses null for that, so convert empty to null 
            if (ownerId == Guid.Empty)
                return null;
            return ownerId;
        } 

        public void InsertInstanceState(PendingWorkItem item, Guid ownerId, DateTime ownedUntil) 
        { 
            /* sproc params
            @uidInstanceID uniqueidentifier, 
            @state image,
            @status int,
            @artifacts image,
            @queueingState image, 
            @unlocked int,
            @blocked int, 
            @info ntext, 
            @ownerId uniqueidentifier,
            @ownedUntil datetime 
            @nextTimer datetime
            */
            DbCommand command = NewStoredProcCommand("InsertInstanceState");
            command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("@uidInstanceID", item.InstanceId)); 
            command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("@state", item.SerializedActivity));
            command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("@status", item.Status)); 
            command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("@unlocked", item.Unlocked)); 
            command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("@blocked", item.Blocked));
            command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("@info", item.Info)); 
            command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("@ownedUntil", ownedUntil == DateTime.MaxValue ? SqlDateTime.MaxValue : ownedUntil));
            command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("@ownerID", DbOwnerId(ownerId)));
            command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("@nextTimer", item.NextTimer));
            DbParameter p1 = this.dbResourceAllocator.NewDbParameter(); 
            p1.ParameterName = "@result";
            p1.DbType = DbType.Int32; 
            p1.Value = 0; 
            p1.Direction = ParameterDirection.InputOutput;
            command.Parameters.Add(p1); 
            //command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("@result", DbType.Int32, ParameterDirection.Output));
            DbParameter p2 = this.dbResourceAllocator.NewDbParameter();
            p2.ParameterName = "@currentOwnerID";
            p2.DbType = DbType.Guid; 
            p2.Value = Guid.Empty;
            p2.Direction = ParameterDirection.InputOutput; 
            command.Parameters.Add(p2); 
            //command.Parameters.Add(new DbParameter(this.dbResourceAllocator.NewDbParameter("@currentOwnerID", DbType.Guid, ParameterDirection.InputOutput));
#if DEBUG 
            InsertToDbMap(ownerId, connection.Database);
#endif
            WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService({0}): inserting instance: {1}, unlocking: {2} database: {3}", ownerId.ToString(), item.InstanceId.ToString(), item.Unlocked.ToString(), connection.Database);
            // 
            // Cannot retry locally here as we don't own the tx
            // Rely on external retries at the batch commit or workflow level (for tx scopes) 
            command.ExecuteNonQuery(); 
            CheckOwnershipResult(command);
        } 


        public void InsertCompletedScope(Guid instanceId, Guid scopeId, Byte[] state)
        { 
            /* sproc params
            @completedScopeID uniqueidentifier, 
            @state image 
            */
            DbCommand command = NewStoredProcCommand("InsertCompletedScope"); 
            command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("instanceID", instanceId));
            command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("completedScopeID", scopeId));
            command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("state", state));
            // 
            // Cannot retry locally here as we don't own the tx
            // Rely on external retries at the batch commit or workflow level (for tx scopes) 
            command.ExecuteNonQuery(); 
        }
 
        public void ActivationComplete(Guid instanceId, Guid ownerId)
        {
            /* sproc params
            @instanceID uniqueidentifier, 
            @ownerID uniqueidentifier,
            */ 
            DbCommand command = NewStoredProcCommand("UnlockInstanceState"); 
            command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("@uidInstanceID", instanceId));
            command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("@ownerID", DbOwnerId(ownerId))); 
#if DEBUG
            InsertToDbMap(ownerId, connection.Database);
#endif
            WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService({0}): unlocking instance: {1}, database: {2}", ownerId.ToString(), instanceId.ToString(), connection.Database); 
            command.ExecuteNonQuery();
        } 
 
        public IList RetrieveNonblockingInstanceStateIds(Guid ownerId, DateTime ownedUntil)
        { 
            List gs = null;
            DbDataReader dr = null;
            short count = 0;
            while (true) 
            {
                try 
                { 
                    //
                    // Check and reset the connection as needed before building the command 
                    if ((null == connection) || (ConnectionState.Open != connection.State))
                        ResetConnection();

                    DbCommand command = NewStoredProcCommand("RetrieveNonblockingInstanceStateIds"); 
                    command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("@ownedUntil", ownedUntil == DateTime.MaxValue ? SqlDateTime.MaxValue : ownedUntil));
                    command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("@ownerID", DbOwnerId(ownerId))); 
                    command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("@now", DateTime.UtcNow)); 

                    WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService.RetrieveNonblockingInstanceStateIds ExecuteReader start: " + DateTime.UtcNow.ToString("G", System.Globalization.CultureInfo.InvariantCulture)); 
                    dr = command.ExecuteReader(CommandBehavior.CloseConnection);
                    WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService.RetrieveNonblockingInstanceStateIds ExecuteReader end: " + DateTime.UtcNow.ToString("G", System.Globalization.CultureInfo.InvariantCulture));

                    gs = new List(); 
                    while (dr.Read())
                    { 
                        gs.Add(dr.GetGuid(0)); 
                    }
                    break; 
                }
                catch(Exception e)
                {
                    WorkflowTrace.Host.TraceEvent(TraceEventType.Error, 0, "SqlWorkflowPersistenceService.RetrieveNonblockingInstanceStateIds caught exception from ExecuteReader: " + e.ToString()); 

                    if (dbRetry.TryDoRetry(ref count)) 
                    { 
                        WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService.RetrieveNonblockingInstanceStateIds retrying.");
                        continue; 
                    }
                    throw;
                }
                finally 
                {
                    if (dr != null) 
                        dr.Close(); 
                }
            } 

            return gs;
        }
 
        public bool TryRetrieveANonblockingInstanceStateId(Guid ownerId, DateTime ownedUntil, out Guid instanceId)
        { 
            short count = 0; 
            while (true)
            { 
                try
                {
                    //
                    // Check and reset the connection as needed before building the command 
                    if ((null == connection) || (ConnectionState.Open != connection.State))
                        ResetConnection(); 
 
                    DbCommand command = NewStoredProcCommand("RetrieveANonblockingInstanceStateId");
                    command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("@ownedUntil", ownedUntil == DateTime.MaxValue ? SqlDateTime.MaxValue : ownedUntil)); 
                    command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("@ownerID", DbOwnerId(ownerId)));
                    DbParameter p2 = this.dbResourceAllocator.NewDbParameter();
                    p2.ParameterName = "@uidInstanceID";
                    p2.DbType = DbType.Guid; 
                    p2.Value = null;
                    p2.Direction = ParameterDirection.InputOutput; 
 
                    command.Parameters.Add(p2);
 
                    DbParameter found = this.dbResourceAllocator.NewDbParameter();
                    found.ParameterName = "@found";
                    found.DbType = DbType.Boolean;
                    found.Value = null; 
                    found.Direction = ParameterDirection.Output;
 
                    command.Parameters.Add(found); 

                    WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService.TryRetrieveANonblockingInstanceStateId ExecuteNonQuery start: " + DateTime.UtcNow.ToString("G", System.Globalization.CultureInfo.InvariantCulture)); 
                    command.ExecuteNonQuery();
                    WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService.TryRetrieveANonblockingInstanceStateId ExecuteNonQuery end: " + DateTime.UtcNow.ToString("G", System.Globalization.CultureInfo.InvariantCulture));

                    if ((null != found.Value) && ((bool)found.Value)) 
                    {
                        instanceId = (Guid)p2.Value; 
                        return true; 
                    }
                    else 
                    {
                        instanceId = Guid.Empty;
                        return false;
                    } 
                }
                catch(Exception e) 
                { 
                    WorkflowTrace.Host.TraceEvent(TraceEventType.Error, 0, "SqlWorkflowPersistenceService.TryRetrieveANonblockingInstanceStateId caught exception from ExecuteNonQuery: " + e.ToString());
 
                    if (dbRetry.TryDoRetry(ref count))
                    {
                        WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService.TryRetrieveANonblockingInstanceStateId retrying.");
                        continue; 
                    }
                    throw; 
                } 
            }
 
        }


        public IList RetrieveExpiredTimerIds(Guid ownerId, DateTime ownedUntil) 
        {
            List gs = null; 
            DbDataReader dr = null; 

            short count = 0; 
            while (true)
            {
                try
                { 
                    //
                    // Check and reset the connection as needed before building the command 
                    if ((null == connection) || (ConnectionState.Open != connection.State)) 
                        ResetConnection();
 
                    DbCommand command = NewStoredProcCommand("RetrieveExpiredTimerIds");
                    command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("@ownedUntil", ownedUntil == DateTime.MaxValue ? SqlDateTime.MaxValue : ownedUntil));
                    command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("@ownerID", DbOwnerId(ownerId)));
                    command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("@now", DateTime.UtcNow)); 

                    WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService.RetrieveExpiredTimerIds ExecuteReader start: " + DateTime.UtcNow.ToString("G", System.Globalization.CultureInfo.InvariantCulture)); 
                    dr = command.ExecuteReader(CommandBehavior.CloseConnection); 
                    WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService.RetrieveExpiredTimerIds ExecuteReader end: " + DateTime.UtcNow.ToString("G", System.Globalization.CultureInfo.InvariantCulture));
 
                    gs = new List();
                    while (dr.Read())
                    {
                        gs.Add(dr.GetGuid(0)); 
                    }
                    break; 
                } 
                catch(Exception e)
                { 
                    WorkflowTrace.Host.TraceEvent(TraceEventType.Error, 0, "SqlWorkflowPersistenceService.RetrieveExpiredTimerIds caught exception from ExecuteReader: " + e.ToString());

                    if (dbRetry.TryDoRetry(ref count))
                    { 
                        WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService.RetrieveExpiredTimerIds retrying.");
                        continue; 
                    } 
                    throw;
                } 
                finally
                {
                    if (dr != null)
                        dr.Close(); 
                }
            } 
 
            return gs;
        } 

        public Byte[] RetrieveInstanceState(Guid instanceStateId, Guid ownerId, DateTime timeout)
        {
            short count = 0; 
            byte[] state = null;
            while (true) 
            { 
                try
                { 
                    //
                    // Check and reset the connection as needed before building the command
                    if ((null == connection) || (ConnectionState.Open != connection.State))
                        ResetConnection(); 

                    DbCommand command = NewStoredProcCommand("RetrieveInstanceState"); 
                    command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("@uidInstanceID", instanceStateId)); 
                    command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("@ownerID", DbOwnerId(ownerId)));
                    command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("@ownedUntil", timeout == DateTime.MaxValue ? SqlDateTime.MaxValue : timeout)); 
                    DbParameter p1 = this.dbResourceAllocator.NewDbParameter();
                    p1.ParameterName = "@result";
                    p1.DbType = DbType.Int32;
                    p1.Value = 0; 
                    p1.Direction = ParameterDirection.InputOutput;
                    command.Parameters.Add(p1); 
                    //command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("@result", DbType.Int32, ParameterDirection.Output)); 
                    DbParameter p2 = this.dbResourceAllocator.NewDbParameter();
                    p2.ParameterName = "@currentOwnerID"; 
                    p2.DbType = DbType.Guid;
                    p2.Value = Guid.Empty;
                    p2.Direction = ParameterDirection.InputOutput;
                    command.Parameters.Add(p2); 
                    //command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("@currentOwnerID", DbType.Guid, ParameterDirection.InputOutput));
#if DEBUG 
                    InsertToDbMap(ownerId, connection.Database); 
#endif
                    WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService({0}): retreiving instance: {1}, database: {2}", ownerId.ToString(), instanceStateId.ToString(), connection.Database); 
                    state = RetrieveStateFromDB(command, true, instanceStateId);

                    break;
                } 
                catch (Exception e)
                { 
                    WorkflowTrace.Host.TraceEvent(TraceEventType.Error, 0, "SqlWorkflowPersistenceService.RetrieveInstanceState caught exception: " + e.ToString()); 

                    if (dbRetry.TryDoRetry(ref count)) 
                    {
                        WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService.RetrieveInstanceState retrying.");
                        continue;
                    } 
                    else if (e is RetryReadException)    // ### hardcoded retry to work around sql ADM64 read bug ###
                    { 
                        count++; 
                        if (count < 10)
                            continue; 
                        else
                            break;  // give up
                    }
                    throw; 
                }
            } 
 
            if (state == null || state.Length == 0)
            { 
                Exception e = new InvalidOperationException(string.Format(Thread.CurrentThread.CurrentCulture, ExecutionStringManager.InstanceNotFound, instanceStateId));
                e.Data["WorkflowNotFound"] = true;
                throw e;
            } 

            return state; 
        } 

        public Byte[] RetrieveCompletedScope(Guid scopeId) 
        {
            short count = 0;
            byte[] state = null;
            while (true) 
            {
                try 
                { 
                    //
                    // Check and reset the connection as needed before building the command 
                    if ((null == connection) || (ConnectionState.Open != connection.State))
                        ResetConnection();

                    DbCommand command = NewStoredProcCommand("RetrieveCompletedScope"); 
                    command.Parameters.Add(this.dbResourceAllocator.NewDbParameter("@completedScopeID", scopeId));
                    DbParameter p1 = this.dbResourceAllocator.NewDbParameter(); 
                    p1.ParameterName = "@result"; 
                    p1.DbType = DbType.Int32;
                    p1.Value = 0; 
                    p1.Direction = ParameterDirection.InputOutput;
                    command.Parameters.Add(p1);

                    state = RetrieveStateFromDB(command, false, WorkflowEnvironment.WorkflowInstanceId); 

                    break; 
                } 
                catch(Exception e)
                { 
                    WorkflowTrace.Host.TraceEvent(TraceEventType.Error, 0, "SqlWorkflowPersistenceService.RetrieveCompletedScope caught exception: " + e.ToString());

                    if (dbRetry.TryDoRetry(ref count))
                    { 
                        WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService.RetrieveCompletedScope retrying.");
                        continue; 
                    } 
                    else if (e is RetryReadException)    // ### hardcoded retry to work around sql ADM64 read bug ###
                    { 
                        count++;
                        if (count < 10)
                            continue;
                        else 
                            break;  // give up
                    } 
                    throw; 
                }
            } 

            if (state == null || state.Length == 0)
                throw new InvalidOperationException(
                    string.Format(Thread.CurrentThread.CurrentCulture, 
                    ExecutionStringManager.CompletedScopeNotFound, scopeId));
            return state; 
        } 

        #endregion 

        #region private DB accessing methods

        ///  
        /// This should only be called for non batch commits
        ///  
        ///  
        private DbConnection ResetConnection()
        { 
            if (null != localTransaction)
                throw new InvalidOperationException(ExecutionStringManager.InvalidOpConnectionReset);

            if (!needToCloseConnection) 
                throw new InvalidOperationException(ExecutionStringManager.InvalidOpConnectionNotLocal);
 
            if ((null != connection) && (ConnectionState.Closed != connection.State)) 
                connection.Close();
            connection.Dispose(); 

            connection = this.dbResourceAllocator.OpenNewConnection();

            return connection; 
        }
 
        ///  
        /// Returns a stored procedure type DBCommand object with current connection and transaction
        ///  
        /// 
        /// the command object
        private DbCommand NewStoredProcCommand(string commandText)
        { 
            DbCommand command = DbResourceAllocator.NewCommand(commandText, this.connection, this.localTransaction);
            command.CommandType = CommandType.StoredProcedure; 
 
            return command;
        } 

        private static void CheckOwnershipResult(DbCommand command)
        {
            DbParameter result = command.Parameters["@result"]; 
            if (result != null && result.Value != null && (int)result.Value == -2)   // -2 is an ownership conflict
            { 
                if (command.Parameters.Contains("@currentOwnerID")) 
                {
                    Guid currentOwnerId = Guid.Empty; 
                    if (command.Parameters["@currentOwnerID"].Value is System.Guid)
                        currentOwnerId = (Guid)command.Parameters["@currentOwnerID"].Value;
                    Guid myId = (Guid)command.Parameters["@ownerID"].Value;
                    Guid instId = (Guid)command.Parameters["@uidInstanceID"].Value; 
                    WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService({0}): owership violation with {1} on instance {2}", myId.ToString(), currentOwnerId.ToString(), instId);
                } 
                DbParameter instanceId = command.Parameters["@uidInstanceID"]; 
                throw new WorkflowOwnershipException((Guid)instanceId.Value);
            } 
        }

        /// 
        /// Helper to Public methods RetrieveInstanceState and RetrieveCompletedScope. 
        /// Retrieves an object from the DB by calling the specified stored procedure with specified stored proc params.
        ///  
        /// Contains the stored procedure setting to be used to query against the Database 
        /// an object to be casted to an activity
        ///     In case of RetrieveInstanceState, only running or suspended instances are returned and 
        ///     exception is thrown for completed/terminated/not-found instances
        /// 
        private static Byte[] RetrieveStateFromDB(DbCommand command, bool checkOwnership, Guid instanceId)
        { 
            DbDataReader dr = null;
            byte[] result = null; 
 
            try
            { 
                WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService.RetrieveStateFromDB {0} ExecuteReader start: {1}", instanceId, DateTime.UtcNow.ToString("G", System.Globalization.CultureInfo.InvariantCulture));
                dr = command.ExecuteReader();
                WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService.RetrieveStateFromDB {0} ExecuteReader end: {1}", instanceId, DateTime.UtcNow.ToString("G", System.Globalization.CultureInfo.InvariantCulture));
 
                if (dr.Read())
                { 
                    result = (byte[])dr.GetValue(0); 
                }
                else 
                {
                    DbParameter resultParam = command.Parameters["@result"];
                    if (resultParam == null || resultParam.Value == null)
                    { 
                        WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService.RetrieveStateFromDB Failed to read results {0}", instanceId);
                    } 
                    else if ((int)resultParam.Value > 0)    // found results but failed to read - sql bug - retry the query 
                    {
                        WorkflowTrace.Host.TraceEvent(TraceEventType.Error, 0, "SqlWorkflowPersistenceService.RetrieveStateFromDB Failed to read results {1}, @result == {0}", (int)resultParam.Value, instanceId); 
                        throw new RetryReadException();
                    }
                }
            } 
            finally
            { 
                if (dr != null) 
                    dr.Close();
            } 

            if (checkOwnership)
                CheckOwnershipResult(command);
 
            return result;
        } 
 
        #endregion private DB accessing methods
 

        internal IEnumerable RetrieveAllInstanceDescriptions()
        {
            List retval = new List(); 
            DbDataReader dr = null;
            try 
            { 
                DbCommand command = NewStoredProcCommand("RetrieveAllInstanceDescriptions");
                dr = command.ExecuteReader(CommandBehavior.CloseConnection); 
                while (dr.Read())
                {
                    retval.Add(new SqlPersistenceWorkflowInstanceDescription(
                        dr.GetGuid(0), 
                        (WorkflowStatus)dr.GetInt32(1),
                        dr.GetInt32(2) == 1 ? true : false, 
                        dr.GetString(3), 
                        (SqlDateTime)dr.GetDateTime(4)
                    )); 
                }
            }
            finally
            { 
                if (dr != null)
                    dr.Close(); 
            } 
            return retval;
        } 
    }
    #endregion

    public class SqlWorkflowPersistenceService : WorkflowPersistenceService, IPendingWork 
    {
        #region constants 
        // Configure parameters for this services 
        private const string InstanceOwnershipTimeoutSecondsToken = "OwnershipTimeoutSeconds";
        private const string UnloadOnIdleToken = "UnloadOnIdle"; 
        private const string EnableRetriesToken = "EnableRetries";

        #endregion constants
 
        private bool _enableRetries = false;
        private bool _ignoreCommonEnableRetries = false; 
        private DbResourceAllocator _dbResourceAllocator; 
        private WorkflowCommitWorkBatchService _transactionService;
        private Guid _serviceInstanceId = Guid.Empty; 
        private TimeSpan _ownershipDelta;
        private Boolean _unloadOnIdle;
        const string LoadingIntervalToken = "LoadIntervalSeconds";
        TimeSpan loadingInterval = new TimeSpan(0, 2, 0); 
        private TimeSpan maxLoadingInterval = new TimeSpan(365, 0, 0, 0, 0);
        SmartTimer loadingTimer; 
        object timerLock = new object(); 
        TimeSpan infinite = new TimeSpan(Timeout.Infinite);
        private static int _deadlock = 1205; 

        // Saved from constructor input to be used in service start initialization
        NameValueCollection configParameters;
        string unvalidatedConnectionString; 

        public Guid ServiceInstanceId 
        { 
            get
            { 
                return _serviceInstanceId;
            }
        }
 
        public TimeSpan LoadingInterval
        { 
            get { return loadingInterval; } 
        }
 
        public bool EnableRetries
        {
            get { return _enableRetries; }
            set 
            {
                _enableRetries = value; 
                _ignoreCommonEnableRetries = true; 
            }
        } 

        private DateTime OwnershipTimeout
        {
            get 
            {
                DateTime timeout; 
                if (_ownershipDelta == TimeSpan.MaxValue) 
                    timeout = DateTime.MaxValue;
                else 
                    timeout = DateTime.UtcNow + _ownershipDelta;
                return timeout;
            }
        } 

        public SqlWorkflowPersistenceService(string connectionString) 
        { 
            if (String.IsNullOrEmpty(connectionString))
                throw new ArgumentNullException("connectionString", ExecutionStringManager.MissingConnectionString); 

            this.unvalidatedConnectionString = connectionString;
        }
 
        public SqlWorkflowPersistenceService(NameValueCollection parameters)
        { 
            if (parameters == null) 
                throw new ArgumentNullException("parameters", ExecutionStringManager.MissingParameters);
 
            _ownershipDelta = TimeSpan.MaxValue;   // default is to never timeout
            if (parameters != null)
            {
                foreach (string key in parameters.Keys) 
                {
                    if (key.Equals(DbResourceAllocator.ConnectionStringToken, StringComparison.OrdinalIgnoreCase)) 
                    { 
                        // the resource allocator (below) will process the connection string
                    } 
                    else if (key.Equals(SqlWorkflowPersistenceService.InstanceOwnershipTimeoutSecondsToken, StringComparison.OrdinalIgnoreCase))
                    {
                        int seconds = Convert.ToInt32(parameters[SqlWorkflowPersistenceService.InstanceOwnershipTimeoutSecondsToken],System.Globalization.CultureInfo.CurrentCulture);
                        if ( seconds < 0 ) 
                            throw new ArgumentOutOfRangeException(InstanceOwnershipTimeoutSecondsToken, seconds, ExecutionStringManager.InvalidOwnershipTimeoutValue);
                        _ownershipDelta = new TimeSpan(0, 0, seconds); 
                        _serviceInstanceId = Guid.NewGuid(); 
                        continue;
                    } 
                    else if (key.Equals(SqlWorkflowPersistenceService.UnloadOnIdleToken, StringComparison.OrdinalIgnoreCase))
                    {
                        _unloadOnIdle = bool.Parse(parameters[key]);
                    } 
                    else if (key.Equals(LoadingIntervalToken, StringComparison.OrdinalIgnoreCase))
                    { 
                        int interval = int.Parse(parameters[key], CultureInfo.CurrentCulture); 
                        if (interval > 0)
                            this.loadingInterval = new TimeSpan(0, 0, interval); 
                        else
                            this.loadingInterval = TimeSpan.Zero;
                        if (this.loadingInterval > maxLoadingInterval)
                            throw new ArgumentOutOfRangeException(LoadingIntervalToken, this.LoadingInterval, ExecutionStringManager.LoadingIntervalTooLarge); 
                    }
                    else if (key.Equals(SqlWorkflowPersistenceService.EnableRetriesToken, StringComparison.OrdinalIgnoreCase)) 
                    { 
                        //
                        // We have a local value for enable retries 
                        _enableRetries = bool.Parse(parameters[key]);
                        _ignoreCommonEnableRetries = true;
                    }
                    else 
                    {
                        throw new ArgumentException( 
                            String.Format(Thread.CurrentThread.CurrentCulture, ExecutionStringManager.UnknownConfigurationParameter, key), "parameters"); 
                    }
                } 
            }

            this.configParameters = parameters;
        } 

        public SqlWorkflowPersistenceService(string connectionString, bool unloadOnIdle, TimeSpan instanceOwnershipDuration, TimeSpan loadingInterval) 
        { 
            if (String.IsNullOrEmpty(connectionString))
                throw new ArgumentNullException("connectionString", ExecutionStringManager.MissingConnectionString); 
            if (loadingInterval > maxLoadingInterval)
                throw new ArgumentOutOfRangeException("loadingInterval", loadingInterval, ExecutionStringManager.LoadingIntervalTooLarge);
            if (instanceOwnershipDuration < TimeSpan.Zero)
                throw new ArgumentOutOfRangeException("instanceOwnershipDuration", instanceOwnershipDuration, ExecutionStringManager.InvalidOwnershipTimeoutValue); 

            this._ownershipDelta = instanceOwnershipDuration; 
            this._unloadOnIdle = unloadOnIdle; 
            this.loadingInterval = loadingInterval;
            this.unvalidatedConnectionString = connectionString; 
            _serviceInstanceId = Guid.NewGuid();
        }

        #region WorkflowRuntimeService 

        override protected internal void Start() 
        { 
            WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService({1}): Starting, LoadInternalSeconds={0}", loadingInterval.TotalSeconds, _serviceInstanceId.ToString());
 
            _dbResourceAllocator = new DbResourceAllocator(this.Runtime, this.configParameters, this.unvalidatedConnectionString);

            // Check connection string mismatch if using SharedConnectionWorkflowTransactionService
            _transactionService = Runtime.GetService(); 
            _dbResourceAllocator.DetectSharedConnectionConflict(_transactionService);
 
            // 
            // If we didn't find a local value for enable retries
            // check in the common section 
            if ((!_ignoreCommonEnableRetries)&&(null != base.Runtime))
            {
                NameValueConfigurationCollection commonConfigurationParameters = base.Runtime.CommonParameters;
                if (commonConfigurationParameters != null) 
                {
                    // Then scan for connection string in the common configuration parameters section 
                    foreach (string key in commonConfigurationParameters.AllKeys) 
                    {
                        if (string.Compare(EnableRetriesToken, key, StringComparison.OrdinalIgnoreCase) == 0) 
                        {
                            _enableRetries = bool.Parse(commonConfigurationParameters[key].Value);
                            break;
                        } 
                    }
                } 
            } 

            base.Start(); 
        }

        protected override void OnStarted()
        { 
            if (loadingInterval > TimeSpan.Zero)
            { 
                lock (timerLock) 
                {
                    base.OnStarted(); 
                    loadingTimer = new SmartTimer(new TimerCallback(LoadWorkflowsWithExpiredTimers), null, loadingInterval, loadingInterval);
                }
            }
            RecoverRunningWorkflowInstances(); 
        }
 
        protected internal override void  Stop() 
        {
            WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService({0}): Stopping", _serviceInstanceId.ToString()); 
            lock (timerLock)
            {
                base.Stop();
                if (loadingTimer != null) 
                {
                    loadingTimer.Dispose(); 
                    loadingTimer = null; 
                }
            } 
        }

        #endregion WorkflowRuntimeService
 
        private void RecoverRunningWorkflowInstances()
        { 
            if (Guid.Empty == _serviceInstanceId) 
            {
                // 
                // Only one host, get all the ids in one go
                IList instanceIds = null;
                using (PersistenceDBAccessor persistenceDBAccessor = new PersistenceDBAccessor(_dbResourceAllocator, _enableRetries))
                { 
                    instanceIds = persistenceDBAccessor.RetrieveNonblockingInstanceStateIds(_serviceInstanceId, OwnershipTimeout);
                } 
                foreach (Guid instanceId in instanceIds) 
                {
                    try 
                    {
                        WorkflowInstance instance = Runtime.GetWorkflow(instanceId);
                        instance.Load();
                    } 
                    catch (Exception e)
                    { 
                        RaiseServicesExceptionNotHandledEvent(e, instanceId); 
                    }
                } 
            }
            else
            {
                using (PersistenceDBAccessor persistenceDBAccessor = new PersistenceDBAccessor(_dbResourceAllocator, _enableRetries)) 
                {
                    // 
                    // Load one at a time to avoid thrashing with other hosts 
                    Guid instanceId;
                    while (persistenceDBAccessor.TryRetrieveANonblockingInstanceStateId(_serviceInstanceId, OwnershipTimeout, out instanceId)) 
                    {
                        try
                        {
                            WorkflowInstance instance = Runtime.GetWorkflow(instanceId); 
                            instance.Load();
                        } 
                        catch (Exception e) 
                        {
                            RaiseServicesExceptionNotHandledEvent(e, instanceId); 
                        }
                    }
                }
            } 
        }
 
 
        private void LoadWorkflowsWithExpiredTimers(object ignored)
        { 
            lock (timerLock)
            {
                if (this.State == WorkflowRuntimeServiceState.Started)
                { 
                    IList ids = null;
                    try 
                    { 

                        ids = LoadExpiredTimerIds(); 
                    }
                    catch (Exception e)
                    {
                        RaiseServicesExceptionNotHandledEvent(e, Guid.Empty); 
                    }
                    if (ids != null) 
                    { 
                        foreach (Guid id in ids)
                        { 
                            WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService({1}): Loading instance with expired timers {0}", id, _serviceInstanceId.ToString());
                            try
                            {
                                Runtime.GetWorkflow(id).Load(); 
                            }
                            // Ignore cases where the workflow has been stolen out from under us 
                            catch (WorkflowOwnershipException) 
                            { }
                            catch (ObjectDisposedException) 
                            {
                                throw;
                            }
                            catch (InvalidOperationException ioe) 
                            {
                                if ( !ioe.Data.Contains("WorkflowNotFound")) 
                                    RaiseServicesExceptionNotHandledEvent(ioe, id); 
                            }
                            catch (Exception e) 
                            {
                                RaiseServicesExceptionNotHandledEvent(e, id);
                            }
                        } 
                    }
                } 
            } 
        }
 

        // uidInstanceID, status, blocked, info, nextTimer

        internal protected override void SaveWorkflowInstanceState(Activity rootActivity, bool unlock) 
        {
            if (rootActivity == null) 
                throw new ArgumentNullException("rootActivity"); 

            WorkflowStatus workflowStatus = WorkflowPersistenceService.GetWorkflowStatus(rootActivity); 
            bool isInstanceBlocked = WorkflowPersistenceService.GetIsBlocked(rootActivity);
            string instanceInfo = WorkflowPersistenceService.GetSuspendOrTerminateInfo(rootActivity);
            Guid contextGuid = (Guid)rootActivity.GetValue(Activity.ActivityContextGuidProperty);
 
            PendingWorkItem item = new PendingWorkItem();
            item.Type = PendingWorkItem.ItemType.Instance; 
            item.InstanceId = WorkflowEnvironment.WorkflowInstanceId; 
            if (workflowStatus != WorkflowStatus.Completed && workflowStatus != WorkflowStatus.Terminated)
                item.SerializedActivity = WorkflowPersistenceService.GetDefaultSerializedForm(rootActivity); 
            else
                item.SerializedActivity = new Byte[0];
            item.Status = (int)workflowStatus;
            item.Blocked = isInstanceBlocked ? 1 : 0; 
            item.Info = instanceInfo;
            item.StateId = contextGuid; 
            item.Unlocked = unlock; 
            TimerEventSubscriptionCollection timers = (TimerEventSubscriptionCollection)rootActivity.GetValue(TimerEventSubscriptionCollection.TimerCollectionProperty);
            Debug.Assert(timers != null, "TimerEventSubscriptionCollection should never be null, but it is"); 
            TimerEventSubscription sub = timers.Peek();
            item.NextTimer = sub == null ? SqlDateTime.MaxValue : sub.ExpiresAt;
            if (item.Info == null)
                item.Info = ""; 

            WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService({4}):Committing instance {0}, Blocked={1}, Unlocked={2}, NextTimer={3}", contextGuid.ToString(), item.Blocked, item.Unlocked, item.NextTimer.Value.ToLocalTime(), _serviceInstanceId.ToString()); 
 
            WorkflowEnvironment.WorkBatch.Add(this, item);
        } 

        internal protected override void UnlockWorkflowInstanceState(Activity rootActivity)
        {
            PendingWorkItem item = new PendingWorkItem(); 
            item.Type = PendingWorkItem.ItemType.ActivationComplete;
            item.InstanceId = WorkflowEnvironment.WorkflowInstanceId; 
            WorkflowEnvironment.WorkBatch.Add(this, item); 
            WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService({0}):Unlocking instance {1}", _serviceInstanceId.ToString(), item.InstanceId.ToString());
        } 

        internal protected override Activity LoadWorkflowInstanceState(Guid id)
        {
            using (PersistenceDBAccessor persistenceDBAccessor = new PersistenceDBAccessor(_dbResourceAllocator, _enableRetries)) 
            {
                WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SqlWorkflowPersistenceService({0}):Loading instance {1}", _serviceInstanceId.ToString(), id.ToString()); 
                byte[] state = persistenceDBAccessor.RetrieveInstanceState(id, _serviceInstanceId, OwnershipTimeout); 
                return WorkflowPersistenceService.RestoreFromDefaultSerializedForm(state, null);
            } 

        }

        public IList LoadExpiredTimerWorkflowIds() 
        {
            if (State == WorkflowRuntimeServiceState.Started) 
            { 
                return LoadExpiredTimerIds();
            } 
            else
            {
                throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, ExecutionStringManager.WorkflowRuntimeNotStarted));
            } 
        }
 
        private IList LoadExpiredTimerIds() 
        {
            using (PersistenceDBAccessor persistenceDBAccessor = new PersistenceDBAccessor(_dbResourceAllocator, _enableRetries)) 
            {
                return persistenceDBAccessor.RetrieveExpiredTimerIds(_serviceInstanceId, OwnershipTimeout);
            }
        } 

        internal protected override void SaveCompletedContextActivity(Activity completedScopeActivity) 
        { 
            PendingWorkItem item = new PendingWorkItem();
            item.Type = PendingWorkItem.ItemType.CompletedScope; 
            item.SerializedActivity = WorkflowPersistenceService.GetDefaultSerializedForm(completedScopeActivity);
            item.InstanceId = WorkflowEnvironment.WorkflowInstanceId;
            item.StateId = ((ActivityExecutionContextInfo)completedScopeActivity.GetValue(Activity.ActivityExecutionContextInfoProperty)).ContextGuid;
 
            WorkflowEnvironment.WorkBatch.Add(this, item);
        } 
 
        internal protected override Activity LoadCompletedContextActivity(Guid id, Activity outerActivity)
        { 
            using (PersistenceDBAccessor persistenceDBAccessor = new PersistenceDBAccessor(_dbResourceAllocator,_enableRetries))
            {
                byte[] state = persistenceDBAccessor.RetrieveCompletedScope(id);
                return WorkflowPersistenceService.RestoreFromDefaultSerializedForm(state, outerActivity); 
            }
        } 
 
        internal protected override bool UnloadOnIdle(Activity activity)
        { 
            return _unloadOnIdle;
        }

        public IEnumerable GetAllWorkflows() 
        {
            if (State == WorkflowRuntimeServiceState.Started) 
            { 
                using (PersistenceDBAccessor persistenceDBAccessor = new PersistenceDBAccessor(_dbResourceAllocator,_enableRetries))
                { 
                    return persistenceDBAccessor.RetrieveAllInstanceDescriptions();
                }
            }
            else 
            {
                throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, ExecutionStringManager.WorkflowRuntimeNotStarted)); 
            } 
        }
 
        #region IPendingWork methods

        bool IPendingWork.MustCommit(ICollection items)
        { 
            return true;
        } 
 
        /// 
        /// Commmit the work items using the transaction 
        /// 
        /// 
        /// 
        void IPendingWork.Commit(System.Transactions.Transaction transaction, ICollection items) 
        {
            PersistenceDBAccessor persistenceDBAccessor = null; 
            try 
            {
                persistenceDBAccessor = new PersistenceDBAccessor(_dbResourceAllocator, transaction, _transactionService); 
                foreach (PendingWorkItem item in items)
                {
                    switch (item.Type)
                    { 
                        case PendingWorkItem.ItemType.Instance:
                            persistenceDBAccessor.InsertInstanceState(item, _serviceInstanceId, OwnershipTimeout); 
                            break; 

                        case PendingWorkItem.ItemType.CompletedScope: 
                            persistenceDBAccessor.InsertCompletedScope(item.InstanceId, item.StateId, item.SerializedActivity);
                            break;

                        case PendingWorkItem.ItemType.ActivationComplete: 
                            persistenceDBAccessor.ActivationComplete(item.InstanceId, _serviceInstanceId);
                            break; 
 
                        default:
                            Debug.Assert(false, "Committing unknown pending work item type in SqlPersistenceService.Commit()"); 
                            break;
                    }

                } 
            }
            catch (SqlException se) 
            { 
                WorkflowTrace.Runtime.TraceEvent(TraceEventType.Error, 0, "SqlWorkflowPersistenceService({1})Exception thrown while persisting instance: {0}", se.Message, _serviceInstanceId.ToString());
                WorkflowTrace.Runtime.TraceEvent(TraceEventType.Error, 0, "stacktrace : {0}", se.StackTrace); 

                if (se.Number == _deadlock)
                {
                    PersistenceException pe = new PersistenceException(se.Message, se); 
                    throw pe;
                } 
                else 
                {
                    throw; 
                }

            }
            catch (Exception e) 
            {
                WorkflowTrace.Runtime.TraceEvent(TraceEventType.Error, 0, "SqlWorkflowPersistenceService({1}): Exception thrown while persisting instance: {0}", e.Message, _serviceInstanceId.ToString()); 
                WorkflowTrace.Runtime.TraceEvent(TraceEventType.Error, 0, "stacktrace : {0}", e.StackTrace); 
                throw e;
            } 
            finally
            {
                if (persistenceDBAccessor != null)
                    persistenceDBAccessor.Dispose(); 
            }
        } 
 

        ///  
        /// Perform necesssary cleanup. Called when the scope
        /// has finished processing this batch of work items
        /// 
        ///  
        /// 
        void IPendingWork.Complete(bool succeeded, ICollection items) 
        { 
            if(loadingTimer != null && succeeded)
            { 
                foreach (PendingWorkItem item in items)
                {
                    if (item.Type.Equals(PendingWorkItem.ItemType.Instance))
                    { 
                        loadingTimer.Update((DateTime)item.NextTimer);
                    } 
                } 
            }
        } 

        #endregion IPendingWork Methods

    } 

    internal class SmartTimer : IDisposable 
    { 
        private object locker = new object();
        private Timer timer; 
        private DateTime next;
        private bool nextChanged;
        private TimeSpan period;
        private TimerCallback callback; 
        private TimeSpan minUpdate = new TimeSpan(0, 0, 5);
        private TimeSpan infinite = new TimeSpan(Timeout.Infinite); 
 
        public SmartTimer(TimerCallback callback, object state, TimeSpan due, TimeSpan period)
        { 
            this.period = period;
            this.callback = callback;
            this.next = DateTime.UtcNow + due;
            this.timer = new Timer(HandleCallback, state, due, infinite); 
        }
 
        public void Update(DateTime newNext) 
        {
            if (newNext < next && (next - DateTime.UtcNow) > minUpdate) 
            {
                lock (locker)
                {
                    if (newNext < next && (next - DateTime.UtcNow) > minUpdate && timer != null) 
                    {
                        next = newNext; 
                        nextChanged = true; 
                        TimeSpan when = next - DateTime.UtcNow;
                        if(when < TimeSpan.Zero) 
                            when = TimeSpan.Zero;
                        timer.Change(when, infinite);
                    }
                } 
            }
        } 
 
        private void HandleCallback(object state)
        { 
            try
            {
                callback(state);
            } 
            finally
            { 
                lock (locker) 
                {
                    if (timer != null) 
                    {
                        if (!nextChanged)
                            next = DateTime.UtcNow + period;
                        else 
                            nextChanged = false;
                        TimeSpan when = next - DateTime.UtcNow; 
                        if (when < TimeSpan.Zero) 
                            when = TimeSpan.Zero;
                        timer.Change(when, infinite); 
                    }
                }
            }
        } 

 
        public void Dispose() 
        {
            lock (locker) 
            {
                if (timer != null)
                {
                    timer.Dispose(); 
                    timer = null;
                } 
            } 
        }
    } 
}


// 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