C# 如何在WF 4.0中编写长时间运行的活动来调用web服务

C# 如何在WF 4.0中编写长时间运行的活动来调用web服务,c#,workflow-foundation-4,workflow-foundation,C#,Workflow Foundation 4,Workflow Foundation,我创建了一个活动,它执行一个web请求并将结果存储到数据库中。我发现对于这些长时间运行的活动,我应该编写一些不同的代码,这样工作流引擎线程就不会被阻塞 public sealed class WebSaveActivity : NativeActivity { protected override void Execute(NativeActivityContext context) { GetAndSave(); // This takes 1 hour to a

我创建了一个活动,它执行一个web请求并将结果存储到数据库中。我发现对于这些长时间运行的活动,我应该编写一些不同的代码,这样工作流引擎线程就不会被阻塞

public sealed class WebSaveActivity : NativeActivity
{
    protected override void Execute(NativeActivityContext context)
    {
       GetAndSave(); // This takes 1 hour to accomplish.
    }
}

我应该如何重写此活动以满足长时间运行活动的要求

您可以在现有流程中生成一个线程,例如,如果需要,您的工作流的其余部分将继续运行。不过,请确保首先了解多线程和线程同步的含义。 或者,您可以查看或类似的组件,将整个作业卸载到不同的流程中

编辑:


根据您的评论,您可以研究基于任务的异步模式(TAP):,这将为您提供一个很好的编写代码的模型,在等待长时间运行的操作的结果,直到它返回之前,它将继续处理可以完成的事情。但是,我不确定这是否能满足您的所有需求。在Windows工作流基础中,您可能需要查看一些形式的.

。这个场景是使用WF的持久性特征闪耀的地方。它允许您将工作流实例持久化到数据库,以便完成一些长时间运行的操作。一旦完成,第二个线程或进程可以重新水合工作流实例并允许其继续

首先,为工作流应用程序指定一个工作流实例存储。Microsoft提供了您可以使用的SQL脚本,并提供了您可以在SQL Server上运行的SQL脚本

namespace MySolution.MyWorkflowApp
{
    using System.Activities;
    using System.Activities.DurableInstancing;
    using System.Activities.Statements;
    using System.Threading;

    internal static class Program
    {
        internal static void Main(string[] args)
        {
            var autoResetEvent = new AutoResetEvent(false);
            var workflowApp = new WorkflowApplication(new Sequence());
            workflowApp.InstanceStore = new SqlWorkflowInstanceStore("server=mySqlServer;initial catalog=myWfDb;...");
            workflowApp.Completed += e => autoResetEvent.Set();
            workflowApp.Unloaded += e => autoResetEvent.Set();
            workflowApp.Aborted += e => autoResetEvent.Set();
            workflowApp.Run();
            autoResetEvent.WaitOne();
        }
    }
}
您的活动将启动实际执行保存操作的辅助进程/线程。有多种方法可以做到这一点:

  • 在次线程上
  • 通过异步调用web方法,该方法实际上完成了执行保存操作的繁重工作
您的活动如下所示:

public sealed class WebSaveActivity : NativeActivity
{
    public InArgument<MyBigObject> ObjectToSave { get; set; }

    protected override bool CanInduceIdle
    {
        get
        {
            // This notifies the WF engine that the activity can be unloaded / persisted to an instance store.
            return true;
        }
    }

    protected override void Execute(NativeActivityContext context)
    {
        var currentBigObject = this.ObjectToSave.Get(context);
        currentBigObject.WorkflowInstanceId = context.WorkflowInstanceId;
        StartSaveOperationAsync(this.ObjectToSave.Get(context)); // This method should offload the actual save process to a thread or even a web method, then return immediately.

        // This tells the WF engine that the workflow instance can be suspended and persisted to the instance store.
        context.CreateBookmark("MySaveOperation", AfterSaveCompletesCallback);
    }

    private void AfterSaveCompletesCallback(NativeActivityContext context, Bookmark bookmark, object value)
    {
        // Do more things after the save completes.
        var saved = (bool) value;
        if (saved)
        {
            // yay!
        }
        else
        {
            // boo!!!
        }
    }
}
在第二个过程中,我可以轮询队列以获取新的保存请求,并重新水合持久化工作流实例,以便在保存操作完成后继续。假设以下方法位于不同的控制台应用程序中:

internal static void PollQueue()
{
    var targetQueue = new MessageQueue(@".\private$\pendingSaveOperations");
    while (true)
    {
        // This waits for a message to arrive on the queue.
        var message = targetQueue.Receive();
        var myObjectToSave = message.Body as MyBigObject;

        // Perform the long running save operation
        LongRunningSave(myObjectToSave);

        // Once the save operation finishes, you can resume the associated workflow.
        var autoResetEvent = new AutoResetEvent(false);
        var workflowApp = new WorkflowApplication(new Sequence());
        workflowApp.InstanceStore = new SqlWorkflowInstanceStore("server=mySqlServer;initial catalog=myWfDb;...");
        workflowApp.Completed += e => autoResetEvent.Set();
        workflowApp.Unloaded += e => autoResetEvent.Set();
        workflowApp.Aborted += e => autoResetEvent.Set();

        // I'm assuming the object to save has a field somewhere that refers the workflow instance that's running it.
        workflowApp.Load(myObjectToSave.WorkflowInstanceId);
        workflowApp.ResumeBookmark("LongSaveOperation", true); // The 'true' parameter is just our way of saying the save completed successfully. You can use any object type you desire here.
        autoResetEvent.WaitOne();
    }
}

private static void LongRunningSave(object myObjectToSave)
{
    throw new NotImplementedException();
}

public class MyBigObject 
{
    public Guid WorkflowInstanceId { get; set; } = Guid.NewGuid();
}

现在,长时间运行的保存操作将不会妨碍工作流引擎,它将通过不在内存中长时间保存工作流实例来更有效地利用系统资源。

否,我希望工作流暂停(或进入空闲模式),直到
WebSave
完成,因此,在接下来的活动中,我可以使用结果。感谢您的解决方案。但我的问题是,我无法访问工作流服务器代码,我希望处理活动中的所有代码,而不是通过更改类似于
WorkflowApplication
internal static void PollQueue()
{
    var targetQueue = new MessageQueue(@".\private$\pendingSaveOperations");
    while (true)
    {
        // This waits for a message to arrive on the queue.
        var message = targetQueue.Receive();
        var myObjectToSave = message.Body as MyBigObject;

        // Perform the long running save operation
        LongRunningSave(myObjectToSave);

        // Once the save operation finishes, you can resume the associated workflow.
        var autoResetEvent = new AutoResetEvent(false);
        var workflowApp = new WorkflowApplication(new Sequence());
        workflowApp.InstanceStore = new SqlWorkflowInstanceStore("server=mySqlServer;initial catalog=myWfDb;...");
        workflowApp.Completed += e => autoResetEvent.Set();
        workflowApp.Unloaded += e => autoResetEvent.Set();
        workflowApp.Aborted += e => autoResetEvent.Set();

        // I'm assuming the object to save has a field somewhere that refers the workflow instance that's running it.
        workflowApp.Load(myObjectToSave.WorkflowInstanceId);
        workflowApp.ResumeBookmark("LongSaveOperation", true); // The 'true' parameter is just our way of saying the save completed successfully. You can use any object type you desire here.
        autoResetEvent.WaitOne();
    }
}

private static void LongRunningSave(object myObjectToSave)
{
    throw new NotImplementedException();
}

public class MyBigObject 
{
    public Guid WorkflowInstanceId { get; set; } = Guid.NewGuid();
}