Imported Upstream version 4.0.0~alpha1

Former-commit-id: 806294f5ded97629b74c85c09952f2a74fe182d9
This commit is contained in:
Jo Shields
2015-04-07 09:35:12 +01:00
parent 283343f570
commit 3c1f479b9d
22469 changed files with 2931443 additions and 869343 deletions

View File

@@ -0,0 +1,380 @@
//------------------------------------------------------------------------------
// <copyright file="DbResourceAllocator.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
#region Using directives
using System;
using System.Collections.Specialized;
using System.Configuration;
using System.Data;
using System.Data.Common;
using System.Data.OleDb;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Globalization;
using System.Text.RegularExpressions;
using System.Transactions;
using System.Threading;
#endregion
namespace System.Workflow.Runtime.Hosting
{
/// <summary>
/// Local database providers we support
/// </summary>
internal enum Provider
{
SqlClient = 0,
OleDB = 1
}
/// <summary>
/// Internal Database access abstraction to
/// - abstract the derived Out-of-box SharedConnectionInfo from all DB hosting services
/// - provide uniform connection string management
/// - and support different database providers
/// </summary>
internal sealed class DbResourceAllocator
{
const string EnlistFalseToken = ";Enlist=false";
internal const string ConnectionStringToken = "ConnectionString";
string connString;
Provider localProvider;
/// <summary>
/// Initialize the object by getting the connection string from the parameter or
/// out of the configuration settings
/// </summary>
/// <param name="runtime"></param>
/// <param name="parameters"></param>
/// <param name="connectionString"></param>
internal DbResourceAllocator(
WorkflowRuntime runtime,
NameValueCollection parameters,
string connectionString)
{
// If connection string not specified in input, search the config sections
if (String.IsNullOrEmpty(connectionString))
{
if (parameters != null)
{
// First search in this service's parameters
foreach (string key in parameters.AllKeys)
{
if (string.Compare(ConnectionStringToken, key, StringComparison.OrdinalIgnoreCase) == 0)
{
connectionString = parameters[ConnectionStringToken];
break;
}
}
}
if (String.IsNullOrEmpty(connectionString) && (runtime != null))
{
NameValueConfigurationCollection commonConfigurationParameters = 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(ConnectionStringToken, key, StringComparison.OrdinalIgnoreCase) == 0)
{
connectionString = commonConfigurationParameters[ConnectionStringToken].Value;
break;
}
}
}
}
// If no connectionString parsed out of the params, inner layer throws
// System.ArgumentNullException: Connection string cannot be null or empty
// Parameter name: connectionString
// But this API caller does not have connectionString param.
// So throw ArgumentException with the original message.
if (String.IsNullOrEmpty(connectionString))
throw new ArgumentNullException(ConnectionStringToken, ExecutionStringManager.MissingConnectionString);
}
Init(connectionString);
}
#region Accessors
internal string ConnectionString
{
get { return this.connString; }
}
#endregion Accessors
#region Internal Methods
/// <summary>
/// Disallow the hosting service to have different connection string if using SharedConnectionWorkflowTransactionService
/// Should be called after all hosting services are added to the WorkflowRuntime
/// </summary>
/// <param name="transactionService"></param>
internal void DetectSharedConnectionConflict(WorkflowCommitWorkBatchService transactionService)
{
SharedConnectionWorkflowCommitWorkBatchService sharedConnectionTransactionService = transactionService as SharedConnectionWorkflowCommitWorkBatchService;
if (sharedConnectionTransactionService != null)
{
if (String.Compare(sharedConnectionTransactionService.ConnectionString, this.connString, StringComparison.Ordinal) != 0)
throw new ArgumentException(String.Format(CultureInfo.CurrentCulture,
ExecutionStringManager.SharedConnectionStringSpecificationConflict, this.connString, sharedConnectionTransactionService.ConnectionString));
}
}
#region Get a connection
internal DbConnection OpenNewConnection()
{
// Always disallow AutoEnlist since we enlist explicitly when necessary
return OpenNewConnection(true);
}
internal DbConnection OpenNewConnectionNoEnlist()
{
return OpenNewConnection(true);
}
internal DbConnection OpenNewConnection(bool disallowEnlist)
{
DbConnection connection = null;
string connectionStr = this.connString;
if (disallowEnlist)
connectionStr += DbResourceAllocator.EnlistFalseToken;
if (this.localProvider == Provider.SqlClient)
connection = new SqlConnection(connectionStr);
else
connection = new OleDbConnection(connectionStr);
connection.Open();
return connection;
}
/// <summary>
/// Gets a connection enlisted to the transaction.
/// If the transaction already has a connection attached to it, we return that,
/// otherwise we create a new connection and enlist to the transaction
/// </summary>
/// <param name="transaction"></param>
/// <param name="isNewConnection">output if we created a connection</param>
/// <returns></returns>
internal DbConnection GetEnlistedConnection(WorkflowCommitWorkBatchService txSvc, Transaction transaction, out bool isNewConnection)
{
DbConnection connection;
SharedConnectionInfo connectionInfo = GetConnectionInfo(txSvc, transaction);
if (connectionInfo != null)
{
connection = connectionInfo.DBConnection;
Debug.Assert((connection != null), "null connection");
Debug.Assert((connection.State == System.Data.ConnectionState.Open),
"Invalid connection state " + connection.State + " for connection " + connection);
isNewConnection = false;
}
else
{
connection = this.OpenNewConnection();
connection.EnlistTransaction(transaction);
isNewConnection = true;
}
return connection;
}
#endregion Get a connection
#region Get Local Transaction
internal static DbTransaction GetLocalTransaction(WorkflowCommitWorkBatchService txSvc, Transaction transaction)
{
DbTransaction localTransaction = null;
SharedConnectionInfo connectionInfo = GetConnectionInfo(txSvc, transaction);
if (connectionInfo != null)
localTransaction = connectionInfo.DBTransaction;
return localTransaction;
}
#endregion Get Local Transaction
#region Get a command object for querying
internal DbCommand NewCommand()
{
DbConnection dbConnection = OpenNewConnection();
return DbResourceAllocator.NewCommand(dbConnection);
}
internal static DbCommand NewCommand(DbConnection dbConnection)
{
return NewCommand(null, dbConnection, null);
}
internal static DbCommand NewCommand(string commandText, DbConnection dbConnection, DbTransaction transaction)
{
DbCommand command = dbConnection.CreateCommand();
command.CommandText = commandText;
command.Transaction = transaction;
return command;
}
#endregion Get a command object for querying
#region build a command parameter object for a stored procedure
internal DbParameter NewDbParameter()
{
return NewDbParameter(null, null);
}
internal DbParameter NewDbParameter(string parameterName, DbType type)
{
if (this.localProvider == Provider.SqlClient)
{
if (type == DbType.Int64)
return new SqlParameter(parameterName, SqlDbType.BigInt);
else
return new SqlParameter(parameterName, type);
}
else
{
if (type == DbType.Int64)
return new OleDbParameter(parameterName, OleDbType.BigInt);
else
return new OleDbParameter(parameterName, type);
}
}
internal DbParameter NewDbParameter(string parameterName, DbType type, ParameterDirection direction)
{
DbParameter parameter = NewDbParameter(parameterName, type);
parameter.Direction = direction;
return parameter;
}
internal DbParameter NewDbParameter(string parameterName, object value)
{
if (this.localProvider == Provider.SqlClient)
return new SqlParameter(parameterName, value);
else
return new OleDbParameter(parameterName, value);
}
#endregion build a command parameter object for a stored procedure
#endregion Public Methods
#region Private Helpers
private void Init(string connectionStr)
{
SetConnectionString(connectionStr);
try
{
// Open a connection to see if it's a valid connection string
using (DbConnection connection = this.OpenNewConnection(false))
{
}
}
catch (Exception e)
{
throw new ArgumentException(ExecutionStringManager.InvalidDbConnection, "connectionString", e);
}
// OLEDB connection pooling causes this exception in ExecuteInsertWorkflowInstance
// "Cannot start more transactions on this session."
// Disable pooling to avoid dirty connections.
if (this.localProvider == Provider.OleDB)
this.connString = String.Concat(this.connString, ";OLE DB Services=-4");
}
private void SetConnectionString(string connectionString)
{
if (String.IsNullOrEmpty(connectionString) || String.IsNullOrEmpty(connectionString.Trim()))
throw new ArgumentNullException("connectionString", ExecutionStringManager.MissingConnectionString);
DbConnectionStringBuilder dcsb = new DbConnectionStringBuilder();
dcsb.ConnectionString = connectionString;
// Don't allow the client to specify an auto-enlist value since we decide whether to participate in a transaction
// (enlist for writing and not for reading).
if (dcsb.ContainsKey("enlist"))
{
throw new ArgumentException(ExecutionStringManager.InvalidEnlist);
}
this.connString = connectionString;
//
// We only support sqlclient, sql is the only data store our OOB services talk to.
localProvider = Provider.SqlClient;
}
/*
private void SetLocalProvider(string connectionString)
{
// Assume caller already validated the connection string
MatchCollection providers = Regex.Matches(connectionString, @"(^|;)\s*provider\s*=[^;$]*(;|$)", RegexOptions.IgnoreCase);
// Cannot use DbConnectionStringBuilder because it selects the last provider, not the first one, by itself.
// A legal Sql connection string allows for multiple provider specification and
// selects the first provider
if (providers.Count > 0)
{
// Check if the first one matches "sqloledb" or "sqloledb.<digit>"
if (Regex.IsMatch(providers[0].Value, @"provider\s*=\s*sqloledb(\.\d+)?\s*(;|$)", RegexOptions.IgnoreCase))
{
this.localProvider = Provider.OleDB;
}
else
{
// We don't support other providers
throw new ArgumentException(String.Format(CultureInfo.CurrentCulture,ExecutionStringManager.UnsupportedSqlProvider, providers[0].Value));
}
}
else
{
// SqlClient provider requires no provider keyword specified in connection string
this.localProvider = Provider.SqlClient;
}
}
*/
private static SharedConnectionInfo GetConnectionInfo(WorkflowCommitWorkBatchService txSvc, Transaction transaction)
{
SharedConnectionInfo connectionInfo = null;
SharedConnectionWorkflowCommitWorkBatchService scTxSvc = txSvc as SharedConnectionWorkflowCommitWorkBatchService;
if (scTxSvc != null)
{
connectionInfo = scTxSvc.GetConnectionInfo(transaction);
// The transaction service can't find entry if the transaction has been completed.
// be sure to propate the error so durable services can cast to appropriate exception
if (connectionInfo == null)
throw new ArgumentException(
String.Format(CultureInfo.CurrentCulture, ExecutionStringManager.InvalidTransaction));
}
return connectionInfo;
}
#endregion Private Helpers
}
}

View File

@@ -0,0 +1,87 @@
#region Imports
using System;
using System.Diagnostics;
using System.Collections;
using System.Collections.Generic;
using System.Xml;
using System.Text;
using System.IO;
using System.ComponentModel.Design;
using System.ComponentModel.Design.Serialization;
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Compiler;
using System.Workflow.ComponentModel.Serialization;
using System.Workflow.ComponentModel.Design;
using System.Workflow.Runtime;
#endregion
namespace System.Workflow.Runtime.Hosting
{
[Obsolete("The System.Workflow.* types are deprecated. Instead, please use the new types from System.Activities.*")]
public class DefaultWorkflowLoaderService : WorkflowLoaderService
{
protected internal override Activity CreateInstance(Type workflowType)
{
if (workflowType == null)
throw new ArgumentNullException("workflowType");
if (!typeof(Activity).IsAssignableFrom(workflowType))
throw new ArgumentException(ExecutionStringManager.TypeMustImplementRootActivity);
if (workflowType.GetConstructor(System.Type.EmptyTypes) == null)
throw new ArgumentException(ExecutionStringManager.TypeMustHavePublicDefaultConstructor);
return Activator.CreateInstance(workflowType) as Activity;
}
// This function will create a new root activity definition tree by deserializing the xoml and the rules file.
protected internal override Activity CreateInstance(XmlReader workflowDefinitionReader, XmlReader rulesReader)
{
if (workflowDefinitionReader == null)
throw new ArgumentNullException("workflowDefinitionReader");
Activity root = null;
ValidationErrorCollection errors = new ValidationErrorCollection();
ServiceContainer serviceContainer = new ServiceContainer();
ITypeProvider typeProvider = this.Runtime.GetService<ITypeProvider>();
if (typeProvider != null)
serviceContainer.AddService(typeof(ITypeProvider), typeProvider);
DesignerSerializationManager manager = new DesignerSerializationManager(serviceContainer);
try
{
using (manager.CreateSession())
{
WorkflowMarkupSerializationManager xomlSerializationManager = new WorkflowMarkupSerializationManager(manager);
root = new WorkflowMarkupSerializer().Deserialize(xomlSerializationManager, workflowDefinitionReader) as Activity;
if (root != null && rulesReader != null)
{
object rules = new WorkflowMarkupSerializer().Deserialize(xomlSerializationManager, rulesReader);
root.SetValue(ConditionTypeConverter.DeclarativeConditionDynamicProp, rules);
}
foreach (object error in manager.Errors)
{
if (error is WorkflowMarkupSerializationException)
errors.Add(new ValidationError(((WorkflowMarkupSerializationException)error).Message, ErrorNumbers.Error_SerializationError));
else
errors.Add(new ValidationError(error.ToString(), ErrorNumbers.Error_SerializationError));
}
}
}
catch (Exception e)
{
errors.Add(new ValidationError(e.Message, ErrorNumbers.Error_SerializationError));
}
if (errors.HasErrors)
throw new WorkflowValidationFailedException(ExecutionStringManager.WorkflowValidationFailure, errors);
return root;
}
}
}

View File

@@ -0,0 +1,386 @@
using System;
using System.Collections;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.Threading;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Workflow.Runtime;
using System.Globalization;
namespace System.Workflow.Runtime.Hosting
{
[Obsolete("The System.Workflow.* types are deprecated. Instead, please use the new types from System.Activities.*")]
public class DefaultWorkflowSchedulerService : WorkflowSchedulerService
{
// next two fields controlled by locking the timerQueue
private KeyedPriorityQueue<Guid, CallbackInfo, DateTime> timerQueue = new KeyedPriorityQueue<Guid, CallbackInfo, DateTime>();
private Timer callbackTimer;
private TimerCallback timerCallback;
private const string MAX_SIMULTANEOUS_WORKFLOWS_KEY = "maxSimultaneousWorkflows";
private const int DEFAULT_MAX_SIMULTANEOUS_WORKFLOWS = 5;
private static TimeSpan infinite = new TimeSpan(Timeout.Infinite);
private readonly int maxSimultaneousWorkflows; // Maximum number of work items allowed in ThreadPool queue
private static TimeSpan fiveMinutes = new TimeSpan(0, 5, 0);
// next three fields controlled by locking the waitingQueue
private int numCurrentWorkers;
private Queue<WorkItem> waitingQueue; // Queue for extra items waiting to be allowed into thread pool
private volatile bool running = false;
private IList<PerformanceCounter> queueCounters; // expose internal queue length
private static int DefaultThreadCount
{
get
{
return Environment.ProcessorCount == 1
? DEFAULT_MAX_SIMULTANEOUS_WORKFLOWS
: (int)(DEFAULT_MAX_SIMULTANEOUS_WORKFLOWS * Environment.ProcessorCount * .8);
}
}
public DefaultWorkflowSchedulerService()
: this(DefaultThreadCount)
{
}
public DefaultWorkflowSchedulerService(int maxSimultaneousWorkflows)
: base()
{
if (maxSimultaneousWorkflows < 1)
throw new ArgumentOutOfRangeException(MAX_SIMULTANEOUS_WORKFLOWS_KEY, maxSimultaneousWorkflows, String.Empty);
this.maxSimultaneousWorkflows = maxSimultaneousWorkflows;
init();
}
public DefaultWorkflowSchedulerService(NameValueCollection parameters)
: base()
{
if (parameters == null)
throw new ArgumentNullException("parameters");
maxSimultaneousWorkflows = DefaultThreadCount;
foreach (string key in parameters.Keys)
{
if (key == null)
throw new ArgumentException(String.Format(Thread.CurrentThread.CurrentCulture, ExecutionStringManager.UnknownConfigurationParameter, "null"));
string p = parameters[key];
if (!key.Equals(MAX_SIMULTANEOUS_WORKFLOWS_KEY, StringComparison.OrdinalIgnoreCase))
throw new ArgumentException(String.Format(Thread.CurrentThread.CurrentCulture, ExecutionStringManager.UnknownConfigurationParameter, key));
if (!int.TryParse(p, System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.CurrentCulture, out maxSimultaneousWorkflows))
throw new FormatException(MAX_SIMULTANEOUS_WORKFLOWS_KEY);
}
if (maxSimultaneousWorkflows < 1)
throw new ArgumentOutOfRangeException(MAX_SIMULTANEOUS_WORKFLOWS_KEY, maxSimultaneousWorkflows, String.Empty);
init();
}
private void init()
{
timerCallback = new TimerCallback(OnTimerCallback);
timerQueue.FirstElementChanged += OnFirstElementChanged;
waitingQueue = new Queue<WorkItem>();
}
public int MaxSimultaneousWorkflows
{
get { return maxSimultaneousWorkflows; }
}
internal protected override void Schedule(WaitCallback callback, Guid workflowInstanceId)
{
WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "Scheduling work for instance {0}", workflowInstanceId);
if (callback == null)
throw new ArgumentNullException("callback");
if (workflowInstanceId == Guid.Empty)
throw new ArgumentException(String.Format(CultureInfo.CurrentUICulture, ExecutionStringManager.CantBeEmptyGuid, "workflowInstanceId"));
// Add the work item to our internal queue and signal the ProcessQueue thread
EnqueueWorkItem(new WorkItem(callback, workflowInstanceId));
}
internal protected override void Schedule(WaitCallback callback, Guid workflowInstanceId, DateTime whenUtc, Guid timerId)
{
WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "Scheduling work for instance {0} on timer ID {1} in {2}", workflowInstanceId, timerId, (whenUtc - DateTime.UtcNow));
if (callback == null)
throw new ArgumentNullException("callback");
if (timerId == Guid.Empty)
throw new ArgumentException(String.Format(CultureInfo.CurrentUICulture, ExecutionStringManager.CantBeEmptyGuid, "timerId"));
if (workflowInstanceId == Guid.Empty)
throw new ArgumentException(String.Format(CultureInfo.CurrentUICulture, ExecutionStringManager.CantBeEmptyGuid, "workflowInstanceId"));
CallbackInfo ci = new CallbackInfo(this, callback, workflowInstanceId, whenUtc);
lock (timerQueue)
{
timerQueue.Enqueue(timerId, ci, whenUtc);
}
}
internal protected override void Cancel(Guid timerId)
{
WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "Cancelling work with timer ID {0}", timerId);
if (timerId == Guid.Empty)
throw new ArgumentException(String.Format(CultureInfo.CurrentUICulture, ExecutionStringManager.CantBeEmptyGuid, "timerId"), "timerId");
lock (timerQueue)
{
timerQueue.Remove(timerId);
}
}
override protected void OnStarted()
{
lock (timerQueue)
{
base.OnStarted();
CallbackInfo ci = timerQueue.Peek();
if (ci != null)
callbackTimer = CreateTimerCallback(ci);
running = true;
}
lock (waitingQueue)
{
int nToStart = Math.Min(maxSimultaneousWorkflows, waitingQueue.Count);
for (int i = 0; i < nToStart; i++)
{
if (ThreadPool.QueueUserWorkItem(QueueWorkerProcess))
{
numCurrentWorkers++;
}
}
}
if (queueCounters == null && this.Runtime.PerformanceCounterManager != null)
{
queueCounters = this.Runtime.PerformanceCounterManager.CreateCounters(ExecutionStringManager.PerformanceCounterWorkflowsWaitingName);
}
}
protected internal override void Stop()
{
lock (timerQueue)
{
base.Stop();
if (callbackTimer != null)
{
callbackTimer.Dispose();
callbackTimer = null;
}
running = false;
}
lock (waitingQueue)
{
while (numCurrentWorkers > 0)
{
Monitor.Wait(waitingQueue);
}
}
}
private void OnFirstElementChanged(object source, KeyedPriorityQueueHeadChangedEventArgs<CallbackInfo> e)
{
// timerQueue must have been locked by operation that caused this event to fire
if (callbackTimer != null)
{
callbackTimer.Dispose();
callbackTimer = null;
}
if (e.NewFirstElement != null && this.State == WorkflowRuntimeServiceState.Started)
{
callbackTimer = CreateTimerCallback(e.NewFirstElement);
}
}
private void OnTimerCallback(object ignored)
{
//Make sure activity ID comes out of Threadpool are initialized to null.
Trace.CorrelationManager.ActivityId = Guid.Empty;
CallbackInfo ci = null;
bool fire = false;
try
{
lock (timerQueue)
{
if (State == WorkflowRuntimeServiceState.Started)
{
ci = timerQueue.Peek();
if (ci != null)
{
if (ci.IsExpired)
{
WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "Timeout occured for timer for instance {0}", ci.State);
timerQueue.Dequeue();
fire = true;
}
else
{
callbackTimer = CreateTimerCallback(ci);
}
}
}
}
if (fire && ci != null)
ci.Callback(ci.State);
}
// Ignore cases where the workflow has been stolen out from under us
catch (WorkflowOwnershipException)
{ }
catch (ThreadAbortException e)
{
WorkflowTrace.Host.TraceEvent(TraceEventType.Error, 0, "Timeout for instance, {0} threw exception {1}", ci == null ? null : ci.State, e.Message);
RaiseServicesExceptionNotHandledEvent(e, (Guid)ci.State);
throw;
}
catch (Exception e)
{
WorkflowTrace.Host.TraceEvent(TraceEventType.Error, 0, "Timeout for instance, {0} threw exception {1}", ci == null ? null : ci.State, e.Message);
RaiseServicesExceptionNotHandledEvent(e, (Guid)ci.State);
}
}
private Timer CreateTimerCallback(CallbackInfo info)
{
DateTime now = DateTime.UtcNow;
TimeSpan span = (info.When > now) ? info.When - now : TimeSpan.Zero;
if (span > fiveMinutes) // never let more than five minutes go by without checking
span = fiveMinutes;
return new Timer(timerCallback, info.State, span, infinite);
}
private void EnqueueWorkItem(WorkItem workItem)
{
lock (waitingQueue)
{
waitingQueue.Enqueue(workItem);
if (running && numCurrentWorkers < maxSimultaneousWorkflows)
{
if (ThreadPool.QueueUserWorkItem(this.QueueWorkerProcess))
{
numCurrentWorkers++;
}
}
}
if (queueCounters != null)
{
foreach (PerformanceCounter p in queueCounters)
{
p.RawValue = waitingQueue.Count;
}
}
}
private void QueueWorkerProcess(object state /*unused*/)
{
//Make sure activity ID comes out of Threadpool are initialized to null.
Trace.CorrelationManager.ActivityId = Guid.Empty;
while (true)
{
WorkItem workItem;
lock (waitingQueue)
{
if (waitingQueue.Count == 0 || !running)
{
numCurrentWorkers--;
Monitor.Pulse(waitingQueue);
return;
}
workItem = waitingQueue.Dequeue();
}
if (queueCounters != null)
{
foreach (PerformanceCounter p in queueCounters)
{
p.RawValue = waitingQueue.Count;
}
}
workItem.Invoke(this);
}
}
internal class WorkItem
{
private WaitCallback callback;
private object state;
public WorkItem(WaitCallback callback, object state)
{
this.callback = callback;
this.state = state;
}
public WaitCallback Callback
{
get { return callback; }
}
public void Invoke(WorkflowSchedulerService service)
{
try
{
WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "Running workflow {0}", state);
Callback(state);
}
catch (Exception e)
{
if (WorkflowExecutor.IsIrrecoverableException(e))
{
throw;
}
else
{
service.RaiseExceptionNotHandledEvent(e, (Guid)state);
}
}
}
}
internal class CallbackInfo
{
WaitCallback callback;
object state;
DateTime when;
WorkflowSchedulerService service;
public CallbackInfo(WorkflowSchedulerService service, WaitCallback callback, object state, DateTime when)
{
this.service = service;
this.callback = callback;
this.state = state;
this.when = when;
}
public DateTime When
{
get { return when; }
}
public bool IsExpired
{
get { return DateTime.UtcNow >= when; }
}
public object State
{
get { return state; }
}
public WaitCallback Callback
{
get { return callback; }
}
}
}
}

View File

@@ -0,0 +1,123 @@
//------------------------------------------------------------------------------
// <copyright file="DefaultWorkflowTransactionService.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
using System;
using System.Transactions;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Configuration;
namespace System.Workflow.Runtime.Hosting
{
/// <summary> A simple TransactionService that creates
/// <c>System.Transactions.CommittableTransaction</c>.</summary>
[Obsolete("The System.Workflow.* types are deprecated. Instead, please use the new types from System.Activities.*")]
public class DefaultWorkflowCommitWorkBatchService : WorkflowCommitWorkBatchService
{
private bool _enableRetries = false;
private bool _ignoreCommonEnableRetries = false;
public DefaultWorkflowCommitWorkBatchService()
{
}
public DefaultWorkflowCommitWorkBatchService(NameValueCollection parameters)
{
if (parameters == null)
throw new ArgumentNullException("parameters", ExecutionStringManager.MissingParameters);
if (parameters.Count > 0)
{
foreach (string key in parameters.Keys)
{
if (0 == string.Compare("EnableRetries", key, StringComparison.OrdinalIgnoreCase))
{
_enableRetries = bool.Parse(parameters[key]);
_ignoreCommonEnableRetries = true;
}
}
}
}
public bool EnableRetries
{
get { return _enableRetries; }
set
{
_enableRetries = value;
_ignoreCommonEnableRetries = true;
}
}
protected internal override void Start()
{
WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "DefaultWorkflowCommitWorkBatchService: Starting");
//
// 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("EnableRetries", key, StringComparison.OrdinalIgnoreCase) == 0)
{
_enableRetries = bool.Parse(commonConfigurationParameters[key].Value);
break;
}
}
}
}
base.Start();
}
protected override void OnStopped()
{
WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "DefaultWorkflowCommitWorkBatchService: Stopping");
base.OnStopped();
}
internal protected override void CommitWorkBatch(CommitWorkBatchCallback commitWorkBatchCallback)
{
DbRetry dbRetry = new DbRetry(_enableRetries);
short retryCounter = 0;
while (true)
{
if (null != Transaction.Current)
{
//
// Can't retry as we don't own the tx
// Set the counter to only allow one iteration
retryCounter = dbRetry.MaxRetries;
}
try
{
base.CommitWorkBatch(commitWorkBatchCallback);
break;
}
catch (Exception e)
{
WorkflowTrace.Host.TraceEvent(TraceEventType.Error, 0, "DefaultWorkflowCommitWorkBatchService caught exception from commitWorkBatchCallback: " + e.ToString());
if (dbRetry.TryDoRetry(ref retryCounter))
{
WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "DefaultWorkflowCommitWorkBatchService retrying commitWorkBatchCallback (retry attempt " + retryCounter.ToString(System.Globalization.CultureInfo.InvariantCulture) + ")");
continue;
}
else
throw;
}
}
}
}
}

View File

@@ -0,0 +1,156 @@
//------------------------------------------------------------------------------
// <copyright file="LocalTransaction.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
#region Using directives
using System;
using System.Diagnostics;
using System.Transactions;
using System.Data;
using System.Data.Common;
using System.Threading;
#endregion
namespace System.Workflow.Runtime.Hosting
{
/// <summary>
/// This class wraps a local transaction (DbTransaction) by implementing IPromotableSinglePhaseNotification:
/// It instantiate a DbTransaction and never allows promotion to a DTC transaction.
/// </summary>
internal sealed class LocalTransaction : IPromotableSinglePhaseNotification
{
readonly DbTransaction transaction;
System.Data.Common.DbConnection connection;
ManualResetEvent handle;
object syncRoot = new Object();
#region Constructor
/// <summary>
/// Wraps a local transaction inside
/// </summary>
/// <param name="dbHelper"></param>
internal LocalTransaction(DbResourceAllocator dbHelper, ManualResetEvent handle)
{
if (null == handle)
throw new ArgumentNullException("handle");
// Open a connection that specifically does not auto-enlist ("Enlist=false" in connection string)
// to prevent auto-promotion of the transaction
this.connection = dbHelper.OpenNewConnectionNoEnlist();
this.transaction = this.connection.BeginTransaction();
this.handle = handle;
}
#endregion Constructor
#region Accessors
public System.Data.Common.DbConnection Connection
{
get { return this.connection; }
}
public DbTransaction Transaction
{
get { return this.transaction; }
}
#endregion Accessors
#region IPromotableSinglePhaseNotification Members
public void Initialize()
{
}
public void SinglePhaseCommit(SinglePhaseEnlistment en)
{
if (en == null)
throw new ArgumentNullException("en");
//
// Wait until IPendingWork members have completed (WinOE bugs 17580 and 13395)
try
{
handle.WaitOne();
}
catch (ObjectDisposedException)
{
// If an ObjectDisposedException is thrown because
// the WaitHandle has already closed, nothing to worry
// about. Move on.
}
lock (syncRoot)
{
try
{
this.transaction.Commit();
en.Committed();
}
catch (Exception e)
{
en.Aborted(e);
}
finally
{
if ((null != connection) && (ConnectionState.Closed != connection.State))
{
connection.Close();
connection = null;
}
}
}
}
public void Rollback(SinglePhaseEnlistment en)
{
if (en == null)
throw new ArgumentNullException("en");
//
// Wait until IPendingWork members have completed (WinOE bugs 17580 and 13395)
try
{
handle.WaitOne();
}
catch (ObjectDisposedException)
{
// If an ObjectDisposedException is thrown because
// the WaitHandle has already closed, nothing to worry
// about. Move on.
}
// DbTransaction.Dispose will call Rollback if the DbTransaction is not Zombied.
// Not safe to call DbTransaction.Rollback here as the the underlining
// DbTransaction may have already been Rolled back
lock (syncRoot)
{
if (null != transaction)
transaction.Dispose();
if ((null != connection) && (ConnectionState.Closed != connection.State))
{
connection.Close();
connection = null;
}
en.Aborted();
}
}
public byte[] Promote()
{
throw new TransactionPromotionException(
ExecutionStringManager.PromotionNotSupported);
}
#endregion IPromotableSinglePhaseNotification Members
}
}

View File

@@ -0,0 +1,381 @@
using System;
using System.Threading;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Collections.ObjectModel;
using System.Workflow.Runtime;
using System.Diagnostics;
using System.Globalization;
namespace System.Workflow.Runtime.Hosting
{
[Obsolete("The System.Workflow.* types are deprecated. Instead, please use the new types from System.Activities.*")]
public class ManualWorkflowSchedulerService : WorkflowSchedulerService
{
private class CallbackInfo
{
WaitCallback callback;
Guid instanceId;
Guid timerId;
DateTime when;
public CallbackInfo(WaitCallback callback, Guid instanceId, Guid timerId, DateTime when)
{
this.callback = callback;
this.when = when;
this.instanceId = instanceId;
this.timerId = timerId;
}
public DateTime When
{
get { return when; }
}
public bool IsExpired
{
get { return DateTime.UtcNow >= when; }
}
public Guid InstanceId { get { return instanceId; } }
public Guid TimerId { get { return timerId; } }
public WaitCallback Callback
{
get { return callback; }
}
}
private KeyedPriorityQueue<Guid, CallbackInfo, DateTime> pendingScheduleRequests = new KeyedPriorityQueue<Guid, CallbackInfo, DateTime>();
private Dictionary<Guid, DefaultWorkflowSchedulerService.WorkItem> scheduleRequests = new Dictionary<Guid, DefaultWorkflowSchedulerService.WorkItem>();
private object locker = new Object();
private Timer callbackTimer;
private readonly TimerCallback timerCallback; // non-null indicates that active timers are enabled
private volatile bool threadRunning; // indicates that the timer thread is running
private static TimeSpan infinite = new TimeSpan(Timeout.Infinite);
private IList<PerformanceCounter> queueCounters;
private static TimeSpan fiveMinutes = new TimeSpan(0, 5, 0);
private const string USE_ACTIVE_TIMERS_KEY = "UseActiveTimers";
// Note that pendingScheduleRequests are keyed by instance ID under the assertion that there is at most one outstanding
// timer for any given instance ID. To support cancellation, and additional map is kept of timerID-to-instanceID so that
// we can find the appropriate pending given a timer ID
public ManualWorkflowSchedulerService()
{
}
public ManualWorkflowSchedulerService(bool useActiveTimers)
{
if (useActiveTimers)
{
timerCallback = new TimerCallback(OnTimerCallback);
pendingScheduleRequests.FirstElementChanged += OnFirstElementChanged;
WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "ManualWorkflowSchedulerService: started with active timers");
}
}
public ManualWorkflowSchedulerService(NameValueCollection parameters)
{
if (parameters == null)
throw new ArgumentNullException("parameters");
foreach (string key in parameters.Keys)
{
if (key == null)
throw new ArgumentException(String.Format(Thread.CurrentThread.CurrentCulture, ExecutionStringManager.UnknownConfigurationParameter, "null"));
string p = parameters[key];
if (!key.Equals(USE_ACTIVE_TIMERS_KEY, StringComparison.OrdinalIgnoreCase))
throw new ArgumentException(String.Format(Thread.CurrentThread.CurrentCulture, ExecutionStringManager.UnknownConfigurationParameter, key));
bool useActiveTimers;
if (!bool.TryParse(p, out useActiveTimers))
throw new FormatException(USE_ACTIVE_TIMERS_KEY);
if (useActiveTimers)
{
timerCallback = new TimerCallback(OnTimerCallback);
pendingScheduleRequests.FirstElementChanged += OnFirstElementChanged;
WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "ManualWorkflowSchedulerService: Started with active timers");
}
}
}
internal protected override void Schedule(WaitCallback callback, Guid workflowInstanceId)
{
if (callback == null)
throw new ArgumentNullException("callback");
if (workflowInstanceId.Equals(Guid.Empty))
throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, ExecutionStringManager.CantBeEmptyGuid, "workflowInstanceId"));
lock (locker)
{
WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "ManualWorkflowSchedulerService: Schedule workflow {0}", workflowInstanceId);
if (!scheduleRequests.ContainsKey(workflowInstanceId))
scheduleRequests.Add(workflowInstanceId, new DefaultWorkflowSchedulerService.WorkItem(callback, workflowInstanceId));
}
if (queueCounters != null)
{
foreach (PerformanceCounter p in queueCounters)
{
p.RawValue = scheduleRequests.Count;
}
}
}
internal protected override void Schedule(WaitCallback callback, Guid workflowInstanceId, DateTime whenUtc, Guid timerId)
{
if (callback == null)
throw new ArgumentNullException("callback");
if (workflowInstanceId.Equals(Guid.Empty))
throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, ExecutionStringManager.CantBeEmptyGuid, "workflowInstanceId"));
if (timerId.Equals(Guid.Empty))
throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, ExecutionStringManager.CantBeEmptyGuid, "timerId"));
lock (locker)
{
WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "ManualWorkflowSchedulerService: Schedule timer {0} for workflow {1} at {2}", timerId, workflowInstanceId, whenUtc);
pendingScheduleRequests.Enqueue(timerId, new CallbackInfo(callback, workflowInstanceId, timerId, whenUtc), whenUtc);
}
}
internal protected override void Cancel(Guid timerId)
{
if (timerId.Equals(Guid.Empty))
throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, ExecutionStringManager.CantBeEmptyGuid, "timerId"));
lock (locker)
{
WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "ManualWorkflowSchedulerService: Cancel timer {0}", timerId);
pendingScheduleRequests.Remove(timerId);
}
}
private bool RunOne(Guid workflowInstanceId)
{
bool retval = false;
DefaultWorkflowSchedulerService.WorkItem cs = null;
lock (locker)
{
if (scheduleRequests.ContainsKey(workflowInstanceId))
{
cs = scheduleRequests[workflowInstanceId];
scheduleRequests.Remove(workflowInstanceId);
}
}
try
{
if (cs != null)
{
WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "ManualWorkflowSchedulerService: Executing {0}", workflowInstanceId);
if (queueCounters != null)
{
foreach (PerformanceCounter p in queueCounters)
{
p.RawValue = scheduleRequests.Count;
}
}
cs.Invoke(this);
retval = true;
}
}
catch (Exception e)
{
RaiseServicesExceptionNotHandledEvent(e, workflowInstanceId);
}
return retval;
}
private bool HasExpiredTimer(Guid workflowInstanceId, out Guid timerId)
{
lock (locker)
{
CallbackInfo ci = pendingScheduleRequests.FindByPriority(DateTime.UtcNow,
delegate(CallbackInfo c) { return c.InstanceId == workflowInstanceId; });
if (ci != null)
{
timerId = ci.TimerId;
return true;
}
}
timerId = Guid.Empty;
return false;
}
private bool ProcessTimer(Guid workflowInstanceId)
{
bool retval = false;
CallbackInfo cs = null;
Guid timerId = Guid.Empty;
lock (locker)
{
Guid expTimerId;
if (HasExpiredTimer(workflowInstanceId, out expTimerId))
{
cs = pendingScheduleRequests.Remove(expTimerId);
}
}
try
{
if (cs != null)
{
WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "ManualWorkflowSchedulerService: Processing timer {0}", timerId);
cs.Callback(cs.InstanceId);
retval = true;
}
}
catch (Exception e)
{
RaiseServicesExceptionNotHandledEvent(e, workflowInstanceId);
}
return retval;
}
private bool CanRun(Guid workflowInstanceId)
{
bool retval = false;
lock (locker)
{
Guid timerId;
retval = scheduleRequests.ContainsKey(workflowInstanceId) || HasExpiredTimer(workflowInstanceId, out timerId);
WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "ManualWorkflowSchedulerService: CanRun is {0}", retval);
}
return retval;
}
public bool RunWorkflow(Guid workflowInstanceId)
{
if (workflowInstanceId.Equals(Guid.Empty))
throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, ExecutionStringManager.CantBeEmptyGuid, "workflowInstanceId"));
WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "ManualWorkflowSchedulerService: Running workflow {0}", workflowInstanceId);
bool retval = false; // return true if we do any work at all
while (CanRun(workflowInstanceId))
{
if (RunOne(workflowInstanceId) || ProcessTimer(workflowInstanceId))
retval = true; // did some work, try again
else
break; // no work done this iteration
}
return retval;
}
private Timer CreateTimerCallback(CallbackInfo info)
{
DateTime now = DateTime.UtcNow;
TimeSpan span = (info.When > now) ? info.When - now : TimeSpan.Zero;
// never let more than five minutes go by without checking
if (span > fiveMinutes)
{
span = fiveMinutes;
}
return new Timer(timerCallback, info.InstanceId, span, infinite);
}
override protected void OnStarted()
{
base.OnStarted();
if (this.timerCallback != null)
{
lock (locker)
{
CallbackInfo ci = pendingScheduleRequests.Peek();
if (ci != null)
callbackTimer = CreateTimerCallback(ci);
}
}
lock (locker)
{
if (queueCounters == null && this.Runtime.PerformanceCounterManager != null)
{
queueCounters = this.Runtime.PerformanceCounterManager.CreateCounters(ExecutionStringManager.PerformanceCounterWorkflowsWaitingName);
}
}
}
protected internal override void Stop()
{
base.Stop();
if (this.timerCallback != null)
{
lock (locker)
{
if (callbackTimer != null)
{
callbackTimer.Dispose();
callbackTimer = null;
}
}
}
}
private void OnTimerCallback(object ignored)
{
CallbackInfo ci = null;
try
{
lock (locker)
{
if (State.Equals(WorkflowRuntimeServiceState.Started))
{
ci = pendingScheduleRequests.Peek();
if (ci != null)
{
if (ci.IsExpired)
{
WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "Timeout occured for timer for instance {0}", ci.InstanceId);
threadRunning = true;
pendingScheduleRequests.Dequeue();
}
else
{
callbackTimer = CreateTimerCallback(ci);
}
}
}
}
if (threadRunning)
{
ci.Callback(ci.InstanceId); // delivers the timer message
RunWorkflow(ci.InstanceId);
}
}
catch (ThreadAbortException e)
{
WorkflowTrace.Host.TraceEvent(TraceEventType.Error, 0, "Timeout for instance, {0} threw exception {1}", ci == null ? Guid.Empty : ci.InstanceId, e.Message);
RaiseServicesExceptionNotHandledEvent(e, ci.InstanceId);
throw;
}
catch (Exception e)
{
WorkflowTrace.Host.TraceEvent(TraceEventType.Error, 0, "Timeout for instance, {0} threw exception {1}", ci == null ? Guid.Empty : ci.InstanceId, e.Message);
RaiseServicesExceptionNotHandledEvent(e, ci.InstanceId);
}
finally
{
lock (locker)
{
if (threadRunning)
{
threadRunning = false;
ci = pendingScheduleRequests.Peek();
if (ci != null)
callbackTimer = CreateTimerCallback(ci);
}
}
}
}
private void OnFirstElementChanged(object source, KeyedPriorityQueueHeadChangedEventArgs<CallbackInfo> e)
{
lock (locker)
{
if (threadRunning)
return; // ignore when a timer thread is already processing a timer request
if (callbackTimer != null)
{
callbackTimer.Dispose();
callbackTimer = null;
}
if (e.NewFirstElement != null && this.State == WorkflowRuntimeServiceState.Started)
{
callbackTimer = CreateTimerCallback(e.NewFirstElement);
}
}
}
}
}

View File

@@ -0,0 +1,26 @@
using System;
using System.Runtime.Serialization;
using System.Security.Permissions;
using System.Collections.Generic;
using System.Globalization;
using System.Threading;
using System.Workflow;
using System.Workflow.Runtime;
using System.Workflow.ComponentModel;
namespace System.Workflow.Runtime.Hosting
{
#region Runtime Exceptions
[Serializable]
[Obsolete("The System.Workflow.* types are deprecated. Instead, please use the new types from System.Activities.*")]
public class PersistenceException : SystemException
{
public PersistenceException() : base(string.Format(CultureInfo.CurrentCulture, ExecutionStringManager.PersistenceException)) { }
public PersistenceException(string message) : base(message) { }
public PersistenceException(string message, Exception innerException) : base(message, innerException) { }
[SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)]
protected PersistenceException(SerializationInfo info, StreamingContext context) : base(info, context) { }
}
#endregion
}

View File

@@ -0,0 +1,113 @@
//------------------------------------------------------------------------------
// <copyright file="SharedConnectionInfo.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
#region Using directives
using System;
using System.Diagnostics;
using System.Transactions;
using System.Data.Common;
using System.Threading;
#endregion
namespace System.Workflow.Runtime.Hosting
{
/// <summary>
/// This class keeps the following associated with a Transaction
/// - a connection that participates in the transaction.
/// - an optional local transaction (DbTransaction) generated from the single-phase-committed Transaction.
/// The connection and the local transaction are passed around to different host components to
/// do transacted DB work using the shared connection.
/// </summary>
internal sealed class SharedConnectionInfo : IDisposable
{
readonly DbConnection connection;
readonly DbTransaction localTransaction;
private bool disposed;
private ManualResetEvent handle;
#region Constructor
/// <summary>
/// Instantiate an opened connection enlisted to the Transaction
/// if promotable is false, the Transaction wraps a local
/// transaction inside and can never be promoted
/// </summary>
/// <param name="dbResourceAllocator"></param>
/// <param name="transaction"></param>
/// <param name="wantPromotable"></param>
internal SharedConnectionInfo(
DbResourceAllocator dbResourceAllocator,
Transaction transaction,
bool wantPromotable,
ManualResetEvent handle)
{
Debug.Assert((transaction != null), "Null Transaction!");
if (null == handle)
throw new ArgumentNullException("handle");
this.handle = handle;
if (wantPromotable)
{
// Enlist a newly opened connection to this regular Transaction
this.connection = dbResourceAllocator.OpenNewConnection();
this.connection.EnlistTransaction(transaction);
}
else
{
// Make this transaction no longer promotable by attaching our
// IPromotableSinglePhaseNotification implementation (LocalTranscaction)
// and track the DbConnection and DbTransaction associated with the LocalTranscaction
LocalTransaction localTransaction = new LocalTransaction(dbResourceAllocator, handle);
transaction.EnlistPromotableSinglePhase(localTransaction);
this.connection = localTransaction.Connection;
this.localTransaction = localTransaction.Transaction;
}
}
#endregion Constructor
#region Accessors
internal DbConnection DBConnection
{
get { return this.connection; }
}
internal DbTransaction DBTransaction
{
get { return this.localTransaction; }
}
#endregion Accessors
#region IDisposable Members
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (!this.disposed)
{
//
// If we're using a LocalTransaction it will close the connection
// in it's IPromotableSinglePhaseNotification methods
if ((this.localTransaction == null) && (null != connection))
this.connection.Dispose();
}
this.disposed = true;
}
#endregion
}
}

View File

@@ -0,0 +1,295 @@
#pragma warning disable 1634, 1691
//------------------------------------------------------------------------------
// <copyright file="SharedConnectionWorkflowTransactionService.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
#region Using directives
using System;
using System.Transactions;
using System.Diagnostics;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration;
using System.Threading;
#endregion
namespace System.Workflow.Runtime.Hosting
{
[Obsolete("The System.Workflow.* types are deprecated. Instead, please use the new types from System.Activities.*")]
public class SharedConnectionWorkflowCommitWorkBatchService : WorkflowCommitWorkBatchService
{
private DbResourceAllocator dbResourceAllocator;
private IDictionary<Transaction, SharedConnectionInfo> transactionConnectionTable;
private object tableSyncObject = new object();
// Saved from constructor input to be used in service start initialization
private NameValueCollection configParameters;
string unvalidatedConnectionString;
private bool _enableRetries = false;
private bool _ignoreCommonEnableRetries = false;
/// <summary>
/// Enables the adding of this service programmatically.
/// </summary>
/// <param name="runtime"></param>
/// <param name="connectionString"></param>
public SharedConnectionWorkflowCommitWorkBatchService(string connectionString)
{
if (String.IsNullOrEmpty(connectionString))
throw new ArgumentNullException("connectionString", ExecutionStringManager.MissingConnectionString);
this.unvalidatedConnectionString = connectionString;
}
/// <summary>
/// Enables adding of this service from a config file.
/// Get the connection string from the runtime common parameter section or the particular service parameter section
/// of the configuration file, and instantiate a DbResourceAllocator object.
/// </summary>
/// <param name="runtime"></param>
/// <param name="parameters"></param>
public SharedConnectionWorkflowCommitWorkBatchService(NameValueCollection parameters)
{
if (parameters == null)
throw new ArgumentNullException("parameters", ExecutionStringManager.MissingParameters);
if (parameters.Count > 0)
{
foreach (string key in parameters.Keys)
{
if (0 == string.Compare("EnableRetries", key, StringComparison.OrdinalIgnoreCase))
{
_enableRetries = bool.Parse(parameters[key]);
_ignoreCommonEnableRetries = true;
}
}
}
this.configParameters = parameters;
}
#region Accessors
internal string ConnectionString
{
get
{
#pragma warning disable 56503
if (this.dbResourceAllocator == null)
{
// Other hosting services may try to get the connection string during their initialization phase
// (in WorkflowRuntimeService.Start) to dectect string mismatch conflict
if (this.Runtime == null)
throw new InvalidOperationException(ExecutionStringManager.WorkflowRuntimeNotStarted);
this.dbResourceAllocator = new DbResourceAllocator(this.Runtime, this.configParameters, this.unvalidatedConnectionString);
}
#pragma warning restore 56503
return this.dbResourceAllocator.ConnectionString;
}
}
public bool EnableRetries
{
get { return _enableRetries; }
set
{
_enableRetries = value;
_ignoreCommonEnableRetries = true;
}
}
#endregion Accessors
#region WorkflowRuntimeService
override protected internal void Start()
{
WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SharedConnectionWorkflowCommitWorkBatchService: Starting");
this.dbResourceAllocator = new DbResourceAllocator(this.Runtime, this.configParameters, this.unvalidatedConnectionString);
if (this.transactionConnectionTable == null)
this.transactionConnectionTable = new Dictionary<Transaction, SharedConnectionInfo>();
//
// 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("EnableRetries", key, StringComparison.OrdinalIgnoreCase) == 0)
{
_enableRetries = bool.Parse(commonConfigurationParameters[key].Value);
break;
}
}
}
}
base.Start();
}
protected override void OnStopped()
{
WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SharedConnectionWorkflowCommitWorkBatchService: Stopping");
foreach (KeyValuePair<Transaction, SharedConnectionInfo> kvp in this.transactionConnectionTable)
{
kvp.Value.Dispose();
WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "Removing transaction " + kvp.Key.GetHashCode());
}
this.transactionConnectionTable.Clear();
this.dbResourceAllocator = null;
base.OnStopped();
}
#endregion Public Methods
#region Public Methods
protected internal override void CommitWorkBatch(WorkflowCommitWorkBatchService.CommitWorkBatchCallback commitWorkBatchCallback)
{
//
// Disable retries by default, reset to allow retries below if we own the tx
DbRetry dbRetry = new DbRetry(_enableRetries);
short retryCounter = dbRetry.MaxRetries;
while (true)
{
//
// When using LocalTransaction handle block access to the connection
// in the transaction event handlers until all IPendingWork members have completed
ManualResetEvent handle = new ManualResetEvent(false);
Transaction tx = null;
SharedConnectionInfo connectionInfo = null;
try
{
if (null == Transaction.Current)
{
//
// It's OK to retry here as we own the tx
retryCounter = 0;
//
// Create a local, non promotable transaction that we share with our OOB services
tx = new CommittableTransaction();
connectionInfo = new SharedConnectionInfo(this.dbResourceAllocator, tx, false, handle);
}
else
{
//
// Can't retry as we don't own the tx
// Create a dependent transaction and don't restrict promotion.
tx = Transaction.Current.DependentClone(DependentCloneOption.BlockCommitUntilComplete);
connectionInfo = new SharedConnectionInfo(this.dbResourceAllocator, tx, true, handle);
}
AddToConnectionInfoTable(tx, connectionInfo);
using (TransactionScope ts = new TransactionScope(tx))
{
try
{
commitWorkBatchCallback();
ts.Complete();
}
finally
{
RemoveConnectionFromInfoTable(tx);
//
// Unblock transaction event handlers
handle.Set();
}
}
CommittableTransaction committableTransaction = tx as CommittableTransaction;
if (committableTransaction != null)
committableTransaction.Commit();
DependentTransaction dependentTransaction = tx as DependentTransaction;
if (dependentTransaction != null)
dependentTransaction.Complete();
break;
}
catch (Exception e)
{
tx.Rollback();
WorkflowTrace.Host.TraceEvent(TraceEventType.Error, 0, "SharedConnectionWorkflowCommitWorkBatchService caught exception from commitWorkBatchCallback: " + e.ToString());
if (dbRetry.TryDoRetry(ref retryCounter))
{
WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "SharedConnectionWorkflowCommitWorkBatchService retrying commitWorkBatchCallback (retry attempt " + retryCounter.ToString(System.Globalization.CultureInfo.InvariantCulture) + ")");
continue;
}
else
throw;
}
finally
{
handle.Close();
if (tx != null)
{
tx.Dispose();
}
}
}
}
/// <summary>
/// Get the SharedConnectionInfo object from the hashtable keyed by the transaction
/// </summary>
/// <param name="transaction"></param>
/// <returns></returns>
internal SharedConnectionInfo GetConnectionInfo(Transaction transaction)
{
return LookupConnectionInfoTable(transaction);
}
#endregion Public Methods
#region Private Methods
private void RemoveConnectionFromInfoTable(Transaction transaction)
{
lock (this.tableSyncObject)
{
SharedConnectionInfo connectionInfo;
WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "TransactionCompleted " + transaction.GetHashCode());
if (transactionConnectionTable.TryGetValue(transaction, out connectionInfo))
{
connectionInfo.Dispose();
this.transactionConnectionTable.Remove(transaction);
}
else
{
WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "TransactionCompleted " + transaction.GetHashCode() +
" not found in table of count " + this.transactionConnectionTable.Count);
}
}
}
private void AddToConnectionInfoTable(Transaction transaction, SharedConnectionInfo connectionInfo)
{
lock (this.tableSyncObject)
{
this.transactionConnectionTable.Add(transaction, connectionInfo);
WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "AddToConnectionInfoTable " + transaction.GetHashCode() +
" in table of count " + this.transactionConnectionTable.Count);
}
}
private SharedConnectionInfo LookupConnectionInfoTable(Transaction transaction)
{
lock (this.tableSyncObject)
{
return transactionConnectionTable[transaction];
}
}
#endregion Private Methods
}
}

View File

@@ -0,0 +1,50 @@
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
{
[Obsolete("The System.Workflow.* types are deprecated. Instead, please use the new types from System.Activities.*")]
public class SqlPersistenceWorkflowInstanceDescription
{
private Guid workflowInstanceId;
private WorkflowStatus status;
private bool isBlocked;
private string suspendOrTerminateDescription;
private SqlDateTime nextTimerExpiration;
internal SqlPersistenceWorkflowInstanceDescription(Guid workflowInstanceId, WorkflowStatus status, bool isBlocked, string suspendOrTerminateDescription, SqlDateTime nextTimerExpiration)
{
this.workflowInstanceId = workflowInstanceId;
this.status = status;
this.isBlocked = isBlocked;
this.suspendOrTerminateDescription = suspendOrTerminateDescription;
this.nextTimerExpiration = nextTimerExpiration;
}
public Guid WorkflowInstanceId { get { return workflowInstanceId; } }
public WorkflowStatus Status { get { return status; } }
public bool IsBlocked { get { return isBlocked; } }
public string SuspendOrTerminateDescription { get { return suspendOrTerminateDescription; } }
public SqlDateTime NextTimerExpiration { get { return nextTimerExpiration; } }
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,29 @@
#region Imports
using System;
using System.Diagnostics;
using System.Collections;
using System.Collections.Generic;
using System.Xml;
using System.Text;
using System.IO;
using System.ComponentModel.Design;
using System.ComponentModel.Design.Serialization;
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Compiler;
using System.Workflow.ComponentModel.Serialization;
using System.Workflow.ComponentModel.Design;
using System.Workflow.Runtime;
#endregion
namespace System.Workflow.Runtime.Hosting
{
[Obsolete("The System.Workflow.* types are deprecated. Instead, please use the new types from System.Activities.*")]
public abstract class WorkflowLoaderService : WorkflowRuntimeService
{
protected internal abstract Activity CreateInstance(Type workflowType);
protected internal abstract Activity CreateInstance(XmlReader workflowDefinitionReader, XmlReader rulesReader);
}
}

View File

@@ -0,0 +1,110 @@
//------------------------------------------------------------------------------
// <copyright file="StatePersistenceService.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
using System;
using System.IO;
using System.IO.Compression;
using System.Workflow.Runtime;
using System.Workflow.ComponentModel;
using System.Diagnostics;
namespace System.Workflow.Runtime.Hosting
{
/// <summary> Service for saving engine state. </summary>
[Obsolete("The System.Workflow.* types are deprecated. Instead, please use the new types from System.Activities.*")]
public abstract class WorkflowPersistenceService : WorkflowRuntimeService
{
/// <summary> Saves the state of a workflow instance. </summary>
/// <param name="state"> The workflow instance state to save </param>
internal protected abstract void SaveWorkflowInstanceState(Activity rootActivity, bool unlock);
/// <summary></summary>
/// <param name="state"></param>
internal protected abstract void UnlockWorkflowInstanceState(Activity rootActivity);
/// <summary> Loads the state of a workflow instance. </summary>
/// <param name="instanceId"> The unique ID of the instance to load </param>
/// <returns> The workflow instance state</returns>
internal protected abstract Activity LoadWorkflowInstanceState(Guid instanceId);
/// <summary> Saves the state of a completed scope. </summary>
/// <param name="completedScopeState"> The completed scope to save </param>
internal protected abstract void SaveCompletedContextActivity(Activity activity);
/// <summary> Loads the state of a completed scope </summary>
/// <param name="scopeId"> The unique identifier of the completed scope </param>
/// <returns> The completed scope or null </returns>
internal protected abstract Activity LoadCompletedContextActivity(Guid scopeId, Activity outerActivity);
/// <summary></summary>
/// <param name="activity"></param>
/// <returns>The value of the "UnloadOnIdle" flag</returns>
internal protected abstract bool UnloadOnIdle(Activity activity);
static protected byte[] GetDefaultSerializedForm(Activity activity)
{
DateTime startTime = DateTime.Now;
Byte[] result;
Debug.Assert(activity != null, "Null activity");
using (MemoryStream stream = new MemoryStream(10240))
{
stream.Position = 0;
activity.Save(stream);
using (MemoryStream compressedStream = new MemoryStream((int)stream.Length))
{
using (GZipStream gzs = new GZipStream(compressedStream, CompressionMode.Compress, true))
{
gzs.Write(stream.GetBuffer(), 0, (int)stream.Length);
}
ActivityExecutionContextInfo executionContextInfo = (ActivityExecutionContextInfo)activity.GetValue(Activity.ActivityExecutionContextInfoProperty);
TimeSpan timeElapsed = DateTime.Now - startTime;
WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0,
"Serialized a {0} with id {1} to length {2}. Took {3}.",
executionContextInfo, executionContextInfo.ContextGuid, compressedStream.Length, timeElapsed);
result = compressedStream.GetBuffer();
Array.Resize<Byte>(ref result, Convert.ToInt32(compressedStream.Length));
}
}
return result;
}
static protected Activity RestoreFromDefaultSerializedForm(Byte[] activityBytes, Activity outerActivity)
{
DateTime startTime = DateTime.Now;
Activity state;
MemoryStream stream = new MemoryStream(activityBytes);
stream.Position = 0;
using (GZipStream gzs = new GZipStream(stream, CompressionMode.Decompress, true))
{
state = Activity.Load(gzs, outerActivity);
}
Debug.Assert(state != null, "invalid state recovered");
TimeSpan timeElapsed = DateTime.Now - startTime;
WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0,
"Deserialized a {0} to length {1}. Took {2}.",
state, stream.Length, timeElapsed);
return state;
}
static protected internal bool GetIsBlocked(Activity rootActivity)
{
return (bool)rootActivity.GetValue(WorkflowExecutor.IsBlockedProperty);
}
static protected internal string GetSuspendOrTerminateInfo(Activity rootActivity)
{
return (string)rootActivity.GetValue(WorkflowExecutor.SuspendOrTerminateInfoProperty);
}
static protected internal WorkflowStatus GetWorkflowStatus(Activity rootActivity)
{
return (WorkflowStatus)rootActivity.GetValue(WorkflowExecutor.WorkflowStatusProperty);
}
}
}

View File

@@ -0,0 +1,102 @@
// <copyright file="WorkflowRuntimeService.cs" company="Microsoft">Copyright (c) Microsoft Corporation. All rights reserved.</copyright>
using System;
using System.Globalization;
using System.Workflow.Runtime;
namespace System.Workflow.Runtime.Hosting
{
[Obsolete("The System.Workflow.* types are deprecated. Instead, please use the new types from System.Activities.*")]
public enum WorkflowRuntimeServiceState
{
Stopped,
Starting,
Started,
Stopping
}
[Obsolete("The System.Workflow.* types are deprecated. Instead, please use the new types from System.Activities.*")]
abstract public class WorkflowRuntimeService
{
private WorkflowRuntime _runtime;
private WorkflowRuntimeServiceState state = WorkflowRuntimeServiceState.Stopped;
protected WorkflowRuntime Runtime
{
get
{
return _runtime;
}
}
internal void SetRuntime(WorkflowRuntime runtime)
{
if (runtime == null && _runtime != null)
{
_runtime.Started -= this.HandleStarted;
_runtime.Stopped -= this.HandleStopped;
}
_runtime = runtime;
if (runtime != null)
{
_runtime.Started += this.HandleStarted;
_runtime.Stopped += this.HandleStopped;
}
}
protected void RaiseServicesExceptionNotHandledEvent(Exception exception, Guid instanceId)
{
Runtime.RaiseServicesExceptionNotHandledEvent(exception, instanceId);
}
internal void RaiseExceptionNotHandledEvent(Exception exception, Guid instanceId)
{
Runtime.RaiseServicesExceptionNotHandledEvent(exception, instanceId);
}
protected WorkflowRuntimeServiceState State
{
get { return state; }
}
virtual internal protected void Start()
{
if (_runtime == null)
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, ExecutionStringManager.ServiceNotAddedToRuntime, this.GetType().Name));
if (state.Equals(WorkflowRuntimeServiceState.Started))
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, ExecutionStringManager.ServiceAlreadyStarted, this.GetType().Name));
state = WorkflowRuntimeServiceState.Starting;
}
virtual internal protected void Stop()
{
if (_runtime == null)
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, ExecutionStringManager.ServiceNotAddedToRuntime, this.GetType().Name));
if (state.Equals(WorkflowRuntimeServiceState.Stopped))
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, ExecutionStringManager.ServiceNotStarted, this.GetType().Name));
state = WorkflowRuntimeServiceState.Stopping;
}
virtual protected void OnStarted()
{ }
virtual protected void OnStopped()
{ }
private void HandleStarted(object source, WorkflowRuntimeEventArgs e)
{
state = WorkflowRuntimeServiceState.Started;
this.OnStarted();
}
private void HandleStopped(object source, WorkflowRuntimeEventArgs e)
{
state = WorkflowRuntimeServiceState.Stopped;
this.OnStopped();
}
}
}

View File

@@ -0,0 +1,13 @@
using System;
using System.Threading;
namespace System.Workflow.Runtime.Hosting
{
[Obsolete("The System.Workflow.* types are deprecated. Instead, please use the new types from System.Activities.*")]
public abstract class WorkflowSchedulerService : WorkflowRuntimeService
{
internal protected abstract void Schedule(WaitCallback callback, Guid workflowInstanceId);
internal protected abstract void Schedule(WaitCallback callback, Guid workflowInstanceId, DateTime whenUtc, Guid timerId);
internal protected abstract void Cancel(Guid timerId);
}
}

View File

@@ -0,0 +1,30 @@
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------
using System.Workflow.ComponentModel;
using System.Workflow.Runtime;
using System.Threading;
namespace System.Workflow.Runtime.Hosting
{
class WorkflowTimerService : WorkflowRuntimeService, ITimerService
{
public WorkflowTimerService()
: base()
{
}
public void ScheduleTimer(WaitCallback callback, Guid workflowInstanceId, DateTime whenUtc, Guid timerId)
{
WorkflowSchedulerService schedulerService = this.Runtime.GetService(typeof(WorkflowSchedulerService)) as WorkflowSchedulerService;
schedulerService.Schedule(callback, workflowInstanceId, whenUtc, timerId);
}
public void CancelTimer(Guid timerId)
{
WorkflowSchedulerService schedulerService = this.Runtime.GetService(typeof(WorkflowSchedulerService)) as WorkflowSchedulerService;
schedulerService.Cancel(timerId);
}
}
}

View File

@@ -0,0 +1,61 @@
//------------------------------------------------------------------------------
// <copyright file="WorkflowTransactionService.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
#region Using directives
using System;
using System.Transactions;
#endregion
namespace System.Workflow.Runtime.Hosting
{
[Obsolete("The System.Workflow.* types are deprecated. Instead, please use the new types from System.Activities.*")]
public abstract class WorkflowCommitWorkBatchService : WorkflowRuntimeService
{
public delegate void CommitWorkBatchCallback();
virtual internal protected void CommitWorkBatch(CommitWorkBatchCallback commitWorkBatchCallback)
{
Transaction tx = null;
if (null == Transaction.Current)
tx = new CommittableTransaction();
else
tx = Transaction.Current.DependentClone(DependentCloneOption.BlockCommitUntilComplete);
try
{
using (TransactionScope ts = new TransactionScope(tx))
{
commitWorkBatchCallback();
ts.Complete();
}
CommittableTransaction committableTransaction = tx as CommittableTransaction;
if (committableTransaction != null)
committableTransaction.Commit();
DependentTransaction dependentTransaction = tx as DependentTransaction;
if (dependentTransaction != null)
dependentTransaction.Complete();
}
catch (Exception e)
{
tx.Rollback(e);
throw;
}
finally
{
if (tx != null)
{
tx.Dispose();
}
}
}
}
}

View File

@@ -0,0 +1,96 @@
/*******************************************************************************
// Copyright (C) 2000-2001 Microsoft Corporation. All rights reserved.
//
// CONTENTS
// Workflow Web Hosting Module.
// DESCRIPTION
// Implementation of Workflow Web Host Module.
// REVISIONS
// Date Ver By Remarks
// ~~~~~~~~~~ ~~~ ~~~~~~~~ ~~~~~~~~~~~~~~
// 02/22/05 1.0 [....] Implementation.
* ****************************************************************************/
#region Using directives
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Web;
using System.Collections.Specialized;
using System.Threading;
#endregion
namespace System.Workflow.Runtime.Hosting
{
/// <summary>
/// Cookie based rotuing module implementation
/// </summary>
[Obsolete("The System.Workflow.* types are deprecated. Instead, please use the new types from System.Activities.*")]
public sealed class WorkflowWebHostingModule : IHttpModule
{
HttpApplication currentApplication;
public WorkflowWebHostingModule()
{
WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "Workflow Web Hosting Module Created");
}
/// <summary>
/// IHttpModule.Init()
/// </summary>
/// <param name="application"></param>
void IHttpModule.Init(HttpApplication application)
{
WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "Workflow Web Hosting Module Initialized");
this.currentApplication = application;
//Listen for Acquire and ReleaseRequestState event
application.ReleaseRequestState += this.OnReleaseRequestState;
application.AcquireRequestState += this.OnAcquireRequestState;
}
void IHttpModule.Dispose()
{
}
void OnAcquireRequestState(Object sender, EventArgs e)
{
//Performs Cookie based routing.
WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "WebHost Module Routing Begin");
HttpCookie routingCookie = HttpContext.Current.Request.Cookies.Get("WF_WorkflowInstanceId");
if (routingCookie != null)
{
HttpContext.Current.Items.Add("__WorkflowInstanceId__", new Guid(routingCookie.Value));
}
//else no routing information found, it could be activation request or non workflow based request.
}
void OnReleaseRequestState(Object sender, EventArgs e)
{
//Saves cookie back to client.
HttpCookie cookie = HttpContext.Current.Request.Cookies.Get("WF_WorkflowInstanceId");
if (cookie == null)
{
cookie = new HttpCookie("WF_WorkflowInstanceId");
Object workflowInstanceId = HttpContext.Current.Items["__WorkflowInstanceId__"];
if (workflowInstanceId != null)
{
cookie.Value = workflowInstanceId.ToString();
HttpContext.Current.Response.Cookies.Add(cookie);
}
}
}
}
}