//*****************************************************************************
// Description.....WCF WorkflowInvoker
//
// Author..........Roman Kiss, rkiss@pathcom.com
// Copyright � 2005 ATZ Consulting Inc. (see included license.rtf file)
//
// Date Created: 04/04/06
//
// Date Modified By Description
//-----------------------------------------------------------------------------
// 04/04/06 Roman Kiss Initial Revision
// 07/07/06 Roman Kiss migrating to JuneCTP
//*****************************************************************************
//
#region References
using System;
using System.Threading;
using System.Configuration;
using System.Collections.Specialized;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Transactions;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Configuration;
using System.Reflection;
using System.Workflow.Runtime;
using System.Workflow.Activities;
using System.Workflow.Runtime.Hosting;
//
#endregion
namespace RKiss.WorkflowInvoker
{
#region Workflow Local Service - Contract
[ExternalDataExchange]
public interface IInvokerLocalService
{
// to workflow
event EventHandler<RequestEventArgs> Request;
// from workflow
void RaiseResponseEvent(Guid workflowInstanceId, object response);
}
#endregion
#region ExternalDataEventArgs
[Serializable]
public class RequestEventArgs : ExternalDataEventArgs
{
private object _request;
public RequestEventArgs(Guid InstanceId, object request)
: base(InstanceId)
{
this._request = request;
}
public object Request
{
get { return _request; }
set { _request = value; }
}
}
[Serializable]
public class ResponseEventArgs : ExternalDataEventArgs
{
private object _response;
public ResponseEventArgs(Guid InstanceId, object response)
: base(InstanceId)
{
this._response = response;
}
public object Response
{
get { return _response; }
set { _response = value; }
}
}
#endregion
#region Workflow Local Service - Implementation
public class InvokerLocalService : IInvokerLocalService
{
private WorkflowRuntime _workflowRuntime;
private ManualWorkflowSchedulerService _scheduler;
public WorkflowRuntime WorkflowRuntime { get { return _workflowRuntime; } }
public ManualWorkflowSchedulerService Scheduler { get { return _scheduler; } }
public InvokerLocalService(WorkflowRuntime workflowRuntime)
{
if (workflowRuntime == null)
throw new ArgumentNullException("workflowRuntime");
this._workflowRuntime = workflowRuntime;
this._scheduler = workflowRuntime.GetService<ManualWorkflowSchedulerService>();
}
#region IInvokerLocalService Members - to workflow
public event EventHandler<RequestEventArgs> Request;
public void RaiseRequestEvent(Guid workflowInstanceId, object request)
{
// Create the EventArgs for this event
RequestEventArgs e = new RequestEventArgs(workflowInstanceId, request);
// Raise the event
if (this.Request != null)
{
if (this.Scheduler == null)
{
this.Request(null, e);
}
else
{
this.Scheduler.RunWorkflow(workflowInstanceId);
this.Request(null, e);
this.Scheduler.RunWorkflow(workflowInstanceId);
}
}
}
#endregion
#region IInvokerLocalService Members - from workflow
public event EventHandler<ResponseEventArgs> Response;
public void RaiseResponseEvent(Guid workflowInstanceId, object response)
{
// Create the EventArgs for this event
ResponseEventArgs e = new ResponseEventArgs(workflowInstanceId, response);
// Raise the event
if (this.Response != null)
{
this.Response(null, e);
}
}
#endregion
}
#endregion
#region WorkflowInvoker
public class WorkflowInvoker
{
#region private Constructor
private WorkflowInvoker()
{
}
#endregion
#region Create
public static Invoker Create(Type workflowType)
{
return Create(workflowType, null, Guid.NewGuid());
}
public static Invoker Create(Type workflowType, HybridDictionary context)
{
return Create(workflowType, context, Guid.NewGuid());
}
public static Invoker Create(Type workflowType, HybridDictionary context, Guid instanceId)
{
// from current operation to the workflow
Dictionary<string, object> namedArgumentValues = new Dictionary<string, object>();
if (context == null)
context = new HybridDictionary();
context.Add("IncomingMessageHeaders", OperationContext.Current.IncomingMessageHeaders);
context.Add("IncomingMessageProperties", OperationContext.Current.IncomingMessageProperties);
context.Add("TransactionDependentClone", Transaction.Current == null ?
null : Transaction.Current.DependentClone(DependentCloneOption.BlockCommitUntilComplete));
namedArgumentValues.Add("_Context", context);
// Find the custom WCF Extension on the Service Host by it's type
WFServiceHostExtension extension = OperationContext.Current.Host.Extensions.Find<WFServiceHostExtension>();
// Get a reference to the WorkflowRuntime
WorkflowRuntime workflowRuntime = extension.WorkflowRuntime;
// Get a reference to the ExternalDataExchangeService from the WorkflowRuntime
ExternalDataExchangeService dataExchangeService = extension.ExternalDataExchangeService;
// Get a reference to the WSTransferLocalService
InvokerLocalService invokerLocalService = (InvokerLocalService)dataExchangeService.GetService(typeof(InvokerLocalService));
// If the InvokerLocalService is not registered with the runtime, then create an instance now and add it.
if (invokerLocalService == null)
{
invokerLocalService = new InvokerLocalService(workflowRuntime);
dataExchangeService.AddService(invokerLocalService);
}
// create instance of the workflow
WorkflowInstance instance = workflowRuntime.CreateWorkflow(workflowType, namedArgumentValues, instanceId);
instance.Start();
// hook to the WCF
Invoker invoker = new Invoker(invokerLocalService, instance);
return invoker;
}
#endregion
#region RequestResponse
public static TResponse RequestResponse<TResponse>(Type workflowType, object request) where TResponse : class
{
return RequestResponse<TResponse>(workflowType, request, null, int.MaxValue/1000);
}
public static TResponse RequestResponse<TResponse>(Type workflowType, object request, HybridDictionary context) where TResponse : class
{
return RequestResponse<TResponse>(workflowType, request, context, int.MaxValue/1000);
}
public static TResponse RequestResponse<TResponse>(Type workflowType, object request, HybridDictionary context, int secondsTimeOut) where TResponse : class
{
// create workflow invoker
Invoker invoker = Create(workflowType, context);
// pass request object to the workflow
invoker.RaiseRequestEvent(request);
// wait for response from workflow
invoker.WaitForResponse(secondsTimeOut);
// response
return invoker.GetResponse<TResponse>();
}
#endregion
}
#endregion
#region Invoker
public class Invoker
{
#region Private
AutoResetEvent _waitForResponse = new AutoResetEvent(false);
WorkflowTerminatedEventArgs _e;
InvokerLocalService _localservice;
ResponseEventArgs _response;
WorkflowInstance _instance;
#endregion
public Invoker(InvokerLocalService localservice, WorkflowInstance instance)
{
_instance = instance;
_localservice = localservice;
// response handler
_localservice.Response += new EventHandler<ResponseEventArgs>(localservice_FireResponse);
// exception handler
_localservice.WorkflowRuntime.WorkflowTerminated += new EventHandler<WorkflowTerminatedEventArgs>(OnWorkflowTerminated);
}
public WorkflowInstance WorkflowInstance
{
get { return _instance; }
}
public void OnWorkflowTerminated(object sender, WorkflowTerminatedEventArgs e)
{
if (_instance.InstanceId == e.WorkflowInstance.InstanceId)
{
this._e = e;
_waitForResponse.Set();
_localservice.WorkflowRuntime.WorkflowTerminated -= new EventHandler<WorkflowTerminatedEventArgs>(OnWorkflowTerminated);
}
}
public void WaitForResponse(int secondsTimeOut)
{
bool retval = _waitForResponse.WaitOne(secondsTimeOut * 1000, false);
if (_e != null)
throw this._e.Exception;
if (retval == false)
throw new FaultException("The workflow timeout expired");
}
public WorkflowTerminatedEventArgs WorkflowTerminatedEventArgs
{
get { return _e; }
}
public void localservice_FireResponse(object sender, ResponseEventArgs e)
{
if (_instance.InstanceId == e.InstanceId)
{
_response = e;
_waitForResponse.Set();
_localservice.Response -= new EventHandler<ResponseEventArgs>(localservice_FireResponse);
}
}
public void RaiseRequestEvent(object request)
{
_localservice.RaiseRequestEvent(_instance.InstanceId, request);
}
public T GetResponse<T>() where T : class
{
return (T)_response.Response;
}
}
#endregion
}