我正在使用Windows Workflow并在一个正常的C#类文件中托管一个进程主机,该文件由WCF类(托管在IIS中)使用。主机不作为服务托管,它是普通类。我已经阅读了很多文章并查看了许多示例并相应地创建了我的课程。它似乎在单个用户测试情况下完美地工作,但偶尔在多用户情况下失败。如果我走在正确的轨道上或做出根本错误的事情,你能告诉我吗?感谢。
以下是我的代码:
public sealed class PurchaseRequisitionProcessHost : IPurchaseRequisitionProcessHost
{
public event PurchaseRequisitionWfIdleEventHandler PurchaseRequisitionWf_Idle;
public delegate void PurchaseRequisitionWfIdleEventHandler(PurchaseRequisitionWorkflowApplicationIdleEventArgs e);
private static volatile PurchaseRequisitionProcessHost _instance = null;
private static object syncRoot = new Object();
private AutoResetEvent instanceUnloaded = null;
public static PurchaseRequisitionProcessHost Instance
{
get
{
if(_instance == null)
{
lock(syncRoot)
{
if(_instance == null)
{
_instance = new PurchaseRequisitionProcessHost();
}
}
}
return _instance;
}
}
private PurchaseRequisitionProcessHost()
{
}
protected void OnPurchaseRequisitionWf_Idle(PurchaseRequisitionWorkflowApplicationIdleEventArgs e)
{
// Make a temporary copy of the event to avoid possibility of
// a race condition if the last subscriber unsubscribes
// immediately after the null check and before the event is raised.
PurchaseRequisitionWfIdleEventHandler handler = PurchaseRequisitionWf_Idle;
// Event will be null if there are no subscribers
if(handler != null)
{
// Use the () operator to raise the event.
handler(e);
}
}
// executed when instance goes idle
public void OnIdle(WorkflowApplicationIdleEventArgs e)
{
PurchaseRequisitionWorkflowApplicationIdleEventArgs args = new PurchaseRequisitionWorkflowApplicationIdleEventArgs();
if(e.Bookmarks != null && e.Bookmarks.Any())
{
args.BookMarkName = e.Bookmarks[0].BookmarkName;
string prNumber = args.BookMarkName.Substring(args.BookMarkName.IndexOf("_") + 1);
prNumber = prNumber.Substring(0, prNumber.IndexOf("_"));
args.PrNumber = prNumber;
}
args.InstanceId = e.InstanceId;
PurchaseRequisitionWf_Idle(args);
}
public PersistableIdleAction OnIdleAndPersistable(WorkflowApplicationIdleEventArgs e)
{
return PersistableIdleAction.Unload;
}
public void OnWorkFlowUnload(WorkflowApplicationEventArgs e)
{
CleanUpSubscribedEvents();
instanceUnloaded.Set();
}
//// executed when instance has completed
public void OnWorkflowCompleted(WorkflowApplicationCompletedEventArgs e)
{
CleanUpSubscribedEvents();
instanceUnloaded.Set();
}
//// executed when instance has aborted
public void OnWorkflowAborted(WorkflowApplicationAbortedEventArgs e)
{
CleanUpSubscribedEvents();
instanceUnloaded.Set();
}
// executed when unhandled exception
public UnhandledExceptionAction OnWorkflowUnhandledException(WorkflowApplicationUnhandledExceptionEventArgs e)
{
Helpers.Common.Common.logger.Error(string.Format("Unhandled WorkflowException for instance {0}", e.InstanceId), e.UnhandledException);
CleanUpSubscribedEvents();
instanceUnloaded.Set();
return UnhandledExceptionAction.Terminate;
}
private void CleanUpSubscribedEvents()
{
if(PurchaseRequisitionWf_Idle != null)
{
Delegate[] clientList = PurchaseRequisitionWf_Idle.GetInvocationList();
if(clientList != null && clientList.Any())
{
foreach(Delegate d in clientList)
{
PurchaseRequisitionWf_Idle -= (d as PurchaseRequisitionWfIdleEventHandler);
}
}
}
}
#region IPurchaseRequisitionProcessHost Members
public WorkflowApplication CreateAndRun(PurchaseRequisition pr, string uploadsPath)
{
return CreateAndRun(pr, uploadsPath, null);
}
// creates a workflow application, binds parameters, links extensions and run it
public WorkflowApplication CreateAndRun(PurchaseRequisition pr, string uploadsPath, PurchaseRequisitionWfIdleEventHandler idleDelegate)
{
WorkflowApplication instance = null;
try
{
instanceUnloaded = new AutoResetEvent(false);
using(var instanceStore = new DisposableStore())
{
// input parameters for the WF program
IDictionary<string, object> inputs = new Dictionary<string, object>();
inputs.Add("PurchaseRequisition", pr);
inputs.Add("UploadsPath", uploadsPath);
instance = new WorkflowApplication(new PurchaseRequisitionProcessWorkflow(), inputs);
instance.InstanceStore = instanceStore.Store;
// Add the custom tracking participant
instance.Extensions.Add(new CustomTrackingParticipant());
instance.PersistableIdle += OnIdleAndPersistable;
instance.Completed += OnWorkflowCompleted;
instance.Unloaded += OnWorkFlowUnload;
instance.Aborted += OnWorkflowAborted;
instance.OnUnhandledException += OnWorkflowUnhandledException;
instance.Idle += OnIdle;
if(idleDelegate != null)
{
PurchaseRequisitionWf_Idle -= idleDelegate;
PurchaseRequisitionWf_Idle += idleDelegate;
}
// continue executing this instance
instance.Run();
instanceUnloaded.WaitOne();
}
}
catch(Exception ex)
{
Helpers.Common.Common.logger.Error(ex.Message, ex);
}
return instance;
}
public BookmarkResumptionResult SubmitApprovalDecision(Guid instanceId, string prNumber, string approvalSource, bool approved)
{
return SubmitApprovalDecision(instanceId, prNumber, approvalSource, approved, null);
}
public BookmarkResumptionResult SubmitApprovalDecision(Guid instanceId, string prNumber, string approvalSource, bool approved, PurchaseRequisitionWfIdleEventHandler idleDelegate)
{
BookmarkResumptionResult brr = BookmarkResumptionResult.NotReady;
WorkflowApplication instance = this.LoadInstance(instanceId, idleDelegate);
string bookmarkName = string.Format("Pr_{0}_waitingForApprovalFrom_{1}", prNumber, approvalSource);
if(instance != null && instance.GetBookmarks().Count > 0)
{
brr = instance.ResumeBookmark(bookmarkName, approved);
}
instanceUnloaded.WaitOne();
return brr;
}
public BookmarkResumptionResult ReceiptPrGoods(Guid instanceId, PurchaseRequisitionReceipt pr)
{
return ReceiptPrGoods(instanceId, pr, null);
}
public BookmarkResumptionResult ReceiptPrGoods(Guid instanceId, PurchaseRequisitionReceipt pr, PurchaseRequisitionWfIdleEventHandler idleDelegate)
{
BookmarkResumptionResult brr = BookmarkResumptionResult.NotReady;
WorkflowApplication instance = this.LoadInstance(instanceId, idleDelegate);
string bookmarkName = string.Format("Pr_{0}_waitingForReceiptOfGoods", pr.PrNumber);
if(instance != null && instance.GetBookmarks().Count > 0)
{
brr = instance.ResumeBookmark(bookmarkName, pr);
}
instanceUnloaded.WaitOne();
return brr;
}
public void UnsubscribePurchaseRequisitionWfIdleEvent(PurchaseRequisitionWfIdleEventHandler idleDelegate)
{
PurchaseRequisitionWf_Idle -= idleDelegate;
}
#endregion
#region IProcessHost Members
// returns true if the instance is waiting (has pending bookmarks)
public bool IsInstanceWaiting(Guid instanceId)
{
bool isWaiting = false;
WorkflowApplication instance = LoadInstance(instanceId);
if(instance != null)
{
isWaiting = instance.GetBookmarks().Count > 0;
}
return isWaiting;
}
public WorkflowApplication LoadInstance(Guid instanceId)
{
return LoadInstance(instanceId, null);
}
// load and resume a workflow instance. If the instance is in memory,
// will return the version from memory. If not, will load it from the
// persistent store
public WorkflowApplication LoadInstance(Guid instanceId, PurchaseRequisitionWfIdleEventHandler idleDelegate)
{
WorkflowApplication instance = null;
try
{
instanceUnloaded = new AutoResetEvent(false);
using(var instanceStore = new DisposableStore())
{
instance = new WorkflowApplication(new PurchaseRequisitionProcessWorkflow());
WorkflowApplicationInstance wfAppInstance = WorkflowApplication.GetInstance(instanceId, instanceStore.Store);
// Add the custom tracking participant
instance.Extensions.Add(new CustomTrackingParticipant());
instance.PersistableIdle += OnIdleAndPersistable;
instance.Completed += OnWorkflowCompleted;
instance.Unloaded += OnWorkFlowUnload;
instance.Aborted += OnWorkflowAborted;
instance.OnUnhandledException += OnWorkflowUnhandledException;
instance.Idle += OnIdle;
if(idleDelegate != null)
{
PurchaseRequisitionWf_Idle -= idleDelegate;
PurchaseRequisitionWf_Idle += idleDelegate;
}
try
{
instance.Load(wfAppInstance);
}
catch(InstanceOwnerException)
{
}
}
}
catch(Exception ex)
{
Helpers.Common.Common.logger.Error(ex.Message, ex);
}
return instance;
}
#endregion
}
public class PurchaseRequisitionWorkflowApplicationIdleEventArgs : EventArgs
{
public string PrNumber
{
get;
set;
}
public Guid InstanceId
{
get;
set;
}
public string BookMarkName
{
get;
set;
}
}
public class DisposableStore : IDisposable
{
private string connectionString = Properties.Settings.Default.SqlPersistenceStoreDbConnectionString;
private SqlWorkflowInstanceStore _Store = null;
private InstanceHandle _handle = null;
public SqlWorkflowInstanceStore Store
{
get
{
if(_Store == null)
{
_Store = new SqlWorkflowInstanceStore(connectionString);
_Store.HostLockRenewalPeriod = TimeSpan.FromSeconds(5);
_Store.MaxConnectionRetries = 25;
_Store.RunnableInstancesDetectionPeriod = TimeSpan.FromSeconds(10);
_Store.InstanceCompletionAction = InstanceCompletionAction.DeleteAll;
_Store.InstanceLockedExceptionAction = InstanceLockedExceptionAction.BasicRetry;
_handle = _Store.CreateInstanceHandle();
CreateWorkflowOwnerCommand createOwnerCmd = new CreateWorkflowOwnerCommand();
InstanceView view = _Store.Execute(_handle, createOwnerCmd, TimeSpan.FromSeconds(5));
_Store.DefaultInstanceOwner = view.InstanceOwner;
}
return _Store;
}
}
public void Dispose()
{
_handle.Free();
_handle = _Store.CreateInstanceHandle(_Store.DefaultInstanceOwner);
try
{
_Store.Execute(_handle, new DeleteWorkflowOwnerCommand(), TimeSpan.FromSeconds(10));
}
catch(InstancePersistenceCommandException ex)
{
Helpers.Common.Common.logger.Info(ex.Message);
}
catch(InstanceOwnerException ex)
{
Helpers.Common.Common.logger.Info(ex.Message);
}
catch(OperationCanceledException ex)
{
Helpers.Common.Common.logger.Info(ex.Message);
}
catch(Exception ex)
{
Helpers.Common.Common.logger.Info(ex.Message);
}
finally
{
_handle.Free();
_Store.DefaultInstanceOwner = null;
_handle = null;
_Store = null;
}
}
}