C# 使用TPL处理WF 4.0长时间运行的活动

C# 使用TPL处理WF 4.0长时间运行的活动,c#,task-parallel-library,workflow-foundation-4,workflow-foundation,C#,Task Parallel Library,Workflow Foundation 4,Workflow Foundation,我创建了一个活动,它执行一个web请求并将结果存储到数据库中。通常这个过程需要1个小时,这会导致工作流引擎行为异常。我发现对于这些长时间运行的活动,我应该编写一些不同的代码,这样工作流引擎线程就不会被阻塞 研究一些关于编写长期运行的活动的博客,我明白我应该使用Bookmark概念。但是我没有使用TPL和Task找到任何解决方案 对于使用Tasks处理长时间运行的活动,此代码正确吗 public sealed class WebSaveActivity : NativeActivity {

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

研究一些关于编写长期运行的活动的博客,我明白我应该使用
Bookmark
概念。但是我没有使用TPL和
Task
找到任何解决方案

对于使用
Task
s处理长时间运行的活动,此代码正确吗

public sealed class WebSaveActivity : NativeActivity
{
    protected override void Execute(NativeActivityContext context)
    {
        context.CreateBookmark("websave", (activityContext, bookmark, value) =>
        {

        });

        Task.Factory.StartNew(() =>
        {
            GetAndSave(); // This takes 1 hour to accomplish.
            context.RemoveBookmark("websave");
        });

    }

    protected override bool CanInduceIdle 
    {
        get
        {
            return true;
        }
    }
}

不,这不是使用书签的方式。当工作流必须等待来自外部流程的输入时,使用书签

例如:我有一个文档审批工作流,在某个时间点,该工作流必须等待人工审阅者对文档进行确认。调用
ResumeBookmark
时,工作流将被闲置并由运行时再次激活,而不是将工作流实例保留在内存中

在您的情况下,您的工作流不能处于空闲状态,因为它有一个在其上下文中运行的操作。该操作是您的任务,顺便说一句,它是一个火灾和遗忘任务,因此WF无法处理关键故障

现在,对于一个可能的解决方案,您可以考虑让另一个进程调用<代码> GETANDATION 方法,并使该过程最终调用WF上的 ReuMeBooMeks/Cuff>,这样工作流可以被运行时闲置。该流程甚至可以是承载工作流的同一流程

有关示例,请参见。只需想象一下,您的长时间运行的任务将被执行,而不是等待人工在控制台中输入某些内容

您没有指定活动之后的内容,但请注意,当书签恢复时,可以将数据返回到工作流。因此,
GetAndSave
的任何结果,即使只是一个错误代码,也可以用来决定如何进一步执行工作流中的其他活动

希望这对你们有意义,你们也看到了我试图概述的可能的解决方案

编辑 关于在WF中使用任务或异步/等待的简要说明。没有任何方法可以覆盖返回的任务,因此您必须使用
.Wait()
.Result
使其阻塞,或者将其忘掉。因为如果您不能等待它们,那么在工作流执行过程中会发生不好的事情,因为其他活动可能会在使用任务的活动完成其工作之前启动

在开发WF运行时,任务的整个概念还很年轻,因此WF运行时不适合它们

编辑2: 示例实现(基于)

您的活动将几乎为空:

public sealed class TriggerDownload : NativeActivity<string>
{
    [RequiredArgument]
    public InArgument<string> BookmarkName { get; set; }

    protected override void Execute(NativeActivityContext context)
    {
        // Create a Bookmark and wait for it to be resumed.
        context.CreateBookmark(BookmarkName.Get(context),
            new BookmarkCallback(OnResumeBookmark));
    }

    protected override bool CanInduceIdle
    {
        get { return true; }
    }

    public void OnResumeBookmark(NativeActivityContext context, Bookmark bookmark, object obj)
    {
       // When the Bookmark is resumed, assign its value to
       // the Result argument. (This depends on whether you have a result on your GetData method like a string with a result code or something)
       Result.Set(context, (string)obj);
    }
}
公共密封类触发器下载:NativeActivity
{
[必需参数]
公共InArgument书签名称{get;set;}
受保护的覆盖无效执行(NativeActivityContext上下文)
{
//创建书签并等待其恢复。
CreateBookmark(BookmarkName.Get(context),
新书签回调(OnResumeBookmark));
}
受保护的覆盖布尔Caninducidel
{
获取{return true;}
}
public void OnResumeBookmark(NativeActivityContext上下文、书签书签、对象对象)
{
//恢复书签后,将其值指定给
//Result参数。(这取决于GetData方法上是否有结果,如带有结果代码的字符串或其他内容)
Result.Set(上下文,(字符串)obj);
}
}
它向工作流运行时发出信号,指示工作流可以空闲以及如何恢复

现在,对于工作流运行时配置:

WorkflowApplication wfApp = new WorkflowApplication(<Your WF>);

// Workflow lifecycle events omitted except idle.
AutoResetEvent idleEvent = new AutoResetEvent(false);


wfApp.Idle = delegate(WorkflowApplicationIdleEventArgs e)
{
    idleEvent.Set();
};

// Run the workflow.
wfApp.Run();

// Wait for the workflow to go idle before starting the download
idleEvent.WaitOne();

// Start the download and resume the bookmark when finished.
var result = await Task.Run(() => GetAndSave());
BookmarkResumptionResult result = wfApp.ResumeBookmark(new Bookmark("GetData"), result);

// Possible BookmarkResumptionResult values:
// Success, NotFound, or NotReady
Console.WriteLine("BookmarkResumptionResult: {0}", result);
WorkflowApplication wfApp=新的WorkflowApplication();
//工作流生命周期事件被省略,空闲事件除外。
AutoResetEvent idleEvent=新的AutoResetEvent(false);
wfApp.Idle=委托(WorkflowApplicationIdleEventArgs e)
{
idleEvent.Set();
};
//运行工作流。
wfApp.Run();
//在开始下载之前,请等待工作流处于空闲状态
idleEvent.WaitOne();
//开始下载,完成后继续书签。
var result=wait Task.Run(()=>GetAndSave());
BookmarkResumptionResult=wfApp.ResumeBookmark(新书签(“GetData”),结果);
//可能的书签ResumptionResult值:
//成功、未发现或未准备好
WriteLine(“BookmarkResumptionResult:{0}”,result);

我刚才在这里看到了你的相关问题:

另一种实现活动的方法是作为
AsyncCodeActivity

namespace MyLibrary.Activities
{
    using System;
    using System.Activities;

    public sealed class MyActivity : AsyncCodeActivity
    {
        protected override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state)
        {
            var delegateToLongOperation = new Func<bool>(this.LongRunningSave);
            context.UserState = delegateToLongOperation;
            return delegateToLongOperation.BeginInvoke(callback, state);
        }

        protected override void EndExecute(AsyncCodeActivityContext context, IAsyncResult result)
        {
            var longOperationDelegate = (Func<bool>) context.UserState;
            var longOperationResult = longOperationDelegate.EndInvoke(result);

            // Can continue your activity logic here.
        }

        private bool LongRunningSave()
        {
            // Logic to perform the save.
            return true;
        }
    }
}
名称空间MyLibrary.Activities
{
使用制度;
使用系统活动;
公共密封类MyActivity:AsyncCodeActivity
{
受保护的重写IAsyncResult BeginExecute(AsyncCodeActivityContext上下文、AsyncCallback回调、对象状态)
{
var delegateToLongOperation=new Func(this.LongRunningSave);
context.UserState=delegateToLongOperation;
返回delegateToLongOperation.BeginInvoke(回调,状态);
}
受保护的覆盖无效EndExecute(AsyncCodeActivityContext上下文,IAsyncResult结果)
{
var longOperationDelegate=(Func)context.UserState;
var longOperationResult=longOperationDelegate.EndInvoke(结果);
//您可以在此处继续您的活动逻辑。
}
private bool LongRunningSave()
{
//执行保存的逻辑。
返回true;
}
}
}

工作流实例驻留在内存中,但至少工作流运行时可以处理它的正常调度任务,而没有一个线程被长时间运行的进程占用。

<代码> StestBuy < /C> >应谨慎使用,考虑使用<代码>运行< /代码>谢谢您的回答,那么您怎么办?