WorkflowApplication的Process Host的最佳结构 - WF 4.5

时间:2014-07-09 05:32:53

标签: c# workflow-foundation-4

我正在使用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;
        }
    }
}

0 个答案:

没有答案