0

我努力实现以下目标:长时间运行WF 4模式

  1. 启动工作流可以去运行很长一段时间,其中包括自定义活动;
  2. 这些自定义活动将遵循创建书签并等待其简历的模式;
  3. 返回开始的工作流ID并将其存储在某个地方;
  4. 在稍后的时间点,从其存储的ID加载工作流;
  5. 检查工作流是否已完成,如果没有,检查是否有任何被阻止的书签;
  6. 恢复这些书签,从而导致相关活动完成,最终导致整个工作流程完成。

一个简单的例子将是:审阅文档,要么批准它或拒绝它的工作流。将为此创建一个工作流程,个人将被通知,并且只要他们想要,他们可以通过批准或拒绝审查来提供他们的反馈。

我以Andrew Zhu的代码为例,我的代码是http://xhinker.com/post/WF4.aspx

的问题,我已经是:

  1. 当使用像我描述了一个自定义活动,一旦开始了工作流程中,WaitForRunnableInstance将无限期地等待,所以我从来没有通知我的工作流程已经开始,并持续到数据库;
  2. 工作流程确实存放,并具有列BlockingBookmarks设置为我的自定义活动的ID,ExecutionStatus设置为空闲,IsInitialized设置为1,IsSuspended设置为0,IsCompleted设置为0,IsReadyToRun设置为0

我已经开始讨论微软论坛,可以在http://social.msdn.microsoft.com/Forums/en-US/wfprerelease/thread/6262874d-3493-4be1-bd05-b990307e1875/看到并获得一些反馈,但有些东西仍然是不正确的。

对此的任何想法?针对具有自定义活动的长时间运行工作流程的任

谢谢!

+0

有没有什么不能承载这些工作流程为通过IIS服务的一个原因? –

+0

Mike: 谢谢。你会怎么看?我会得到什么好处?这可能是一种可能性,我不明白为什么,但另一方面,我也不明白为什么。 :-) –

回答

5

这通常是长时间运行的工作流的最小示例,它等待控制台上的用户输入。 (此代码永远不会执行,把它作为唯一的一个例子)

/// Activity that waits on bookmark for 
/// someone to send it some text 
/// 
public sealed class ReadLine: NativeActivity<string> 
{ 
    [RequiredArgument] 
    public InArgument<string> BookmarkName { get; set; } 

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

    protected override void Execute(NativeActivityContext context) 
    { 
     context.CreateBookmark(
      BookmarkName.Get(context), 
      new BookmarkCallback(OnReadComplete)); 
    } 

    void OnReadComplete(NativeActivityContext context, Bookmark bookmark, object state) 
    { 
     context.SetValue(base.Result, state as string); 
    } 
} 

/// Program that uses ReadLine activity's bookmark to persist 
/// workflow and waits for user input to resume it 
/// 
public class Program 
{ 
    static InstanceStore InstanceStore; 
    static Activity Activity = GetExampleActivity(); 
    static AutoResetEvent unloadEvent = new AutoResetEvent(false); 
    static Guid WfId; 
    static WorkflowApplication WfApp; 

    const string READ_LINE_BOOKMARK = "ReadLineBookMark"; 

    static void Main() 
    { 
     CreateInstanceStore(); 
     CreateWorkflowApp(); 

     // Start workflow application and wait for input 
     StartAndUnload(); 

     //Get user input and send it to ReadLine bookmark reviving workflow 
     GetInputAndComplete(); 
    } 

    static void StartAndUnload() 
    { 
     WfApp.Run(); 
     WfId = app.Id; 
     // !! Workflow will go idle on bookmark, no need to call Unload() 
     unloadEvent.WaitOne(); 
    } 

    static void GetInputAndComplete() 
    { 
     var input = Console.ReadLine(); 

     // We've the text input, let's resume this thing 
     WfApp.Load(WfId); 
     WfApp.ResumeBookmark(READ_LINE_BOOKMARK, input); 

     unloadEvent.WaitOne(); 
    } 

    static void CreateInstanceStore() 
    { 
     InstanceStore = new SqlWorkflowInstanceStore("connection string"); 

     var handle = InstanceStore.CreateInstanceHandle();    
     var view = InstanceStore.Execute(
      handle, 
      new CreateWorkflowOwnerCommand(), 
      TimeSpan.FromSeconds(5));    

     handle.Free(); 

     InstanceStore.DefaultInstanceOwner = view.InstanceOwner; 
    } 

    static void CreateWorkflowApp() 
    { 
     WfApp = new WorkflowApplication(Activity) 
     { 
      InstanceStore = InstanceStore, 
     }; 

     WfApp.PersistableIdle = (e) => { return PersistableIdleAction.Unload; } 
     WfApp.Unloaded = (e) => 
     { 
      Console.WriteLine("WF App Unloaded\n"); 
      unloadEvent.Set(); 
     }; 
     WfApp.Completed = (e) => 
     { 
      Console.WriteLine("\nWF App Ended: {0}.", e.CompletionState); 
     }; 
    } 

    static Activity GetExampleActivity() 
    { 
     var response = new Variable<string>(); 

     return return new Sequence() 
     { 
      Variables = { response }, 
      Activities = 
      { 
       new WriteLine() 
       { 
        Text = new InArgument<string>("Type some word:") 
       }, 
       new ReadLine() 
       { 
        BookmarkName = READ_LINE_BOOKMARK, 
        Result = new OutArgument<string>(response) 
       }, 
       new WriteLine() 
       { 
        Text = new InArgument<string>((context) => "You've typed: " + response.Get(context)) 
       } 
      } 
     }; 
    } 

话虽这么说,请考虑使用IIS和AppFabric的,你会不会后悔。 AppFabric有六次点击,在WF中实现了两个必须要做的痛苦事情:持久性和监控。如果您选择此路径,您不会永远不需要编写代码。

将您的工作流程部署为WCF应用程序,并将其作为任何其他WCF合同进行调用。你有OperationContracts这是接收活动(那些谁等待并坚持,如果它需要太长时间)和相应的发送活动(谁返回给客户端)。你甚至有他们之间的相关性的概念。AppFabric负责恢复工作流,只是传递一个先前初始化的相关句柄。

AppFabric为您提供了一个配置用户界面来配置持久性存储,监控和其他选项,如闲置和/或保持之前的时间。

您可以直观的有功/无/暂停工作流程,监测数据,等等,等等

+0

Jota: 非常感谢!我不得不稍微修改你的代码(在GetInputAndComplete上创建一个新的WorkflowApplication,否则会抱怨)。这与我的代码非常相似,除了我正在等待工作流在创建时进入运行状态。 –