在Workflow 4 RC中调度运行时指定的Activity

时间:2010-03-03 20:59:45

标签: workflow-foundation workflow-foundation-4 workflow-activity

所以我有这个要求启动在运行时提供给我的活动。为了实现这一点,我设置了一个WorkflowService,它接收活动作为Xaml,给它们加水,并将它们踢掉。

听起来很简单......

...这是我在Xaml中的WorkflowService

<Activity 
    x:Class="Workflow.Services.WorkflowService.WorkflowService" 
    ...
    xmlns:local1="clr-namespace:Workflow.Activities" >
  <Sequence sap:VirtualizedContainerService.HintSize="277,272">
    <Sequence.Variables>
      <Variable x:TypeArguments="local:Workflow" Name="Workflow" />
    </Sequence.Variables>
    <sap:WorkflowViewStateService.ViewState>
      <scg3:Dictionary x:TypeArguments="x:String, x:Object">
        <x:Boolean x:Key="IsExpanded">True</x:Boolean>
      </scg3:Dictionary>
    </sap:WorkflowViewStateService.ViewState>
    <p:Receive CanCreateInstance="True" DisplayName="ReceiveSubmitWorkflow" sap:VirtualizedContainerService.HintSize="255,86" OperationName="SubmitWorkflow" ServiceContractName="IWorkflowService">
      <p:ReceiveParametersContent>
        <OutArgument x:TypeArguments="local:Workflow" x:Key="workflow">[Workflow]</OutArgument>
      </p:ReceiveParametersContent>
    </p:Receive>
    <local1:InvokeActivity Activity="[ActivityXamlServices.Load(New System.IO.StringReader(Workflow.Xaml))]" sap:VirtualizedContainerService.HintSize="255,22" />
  </Sequence>
</Activity>

......除了重复使用“工作流程”之外,这是非常简单的。实际上,它只是一个SequenceReceive和[当前]自定义的活动名为InvokeActivity。稍等一下。

Receive活动接受自定义类型

[DataContract]
public class Workflow
{
    [DataMember]
    public string Xaml { get; set; }
}

包含一个字符串,其内容将被解释为Xaml。您可以看到VB表达式,然后将此Xaml转换为Activity并将其传递。

现在第二位,自定义InvokeActivity是我有疑问的地方。

第一个问题:

1)给定一个任意的任务,在运行时提供[如上所述]是否可以使用WF4RC附带的活动启动此活动,开箱即用?我是相当新的,并认为我做了很好的工作,通过API和现有文档,但也可以问:)

第二

2)我第一次尝试实现自定义InvokeActivity看起来像这样

public sealed class InvokeActivity : NativeActivity
{
    private static readonly ILog _log = 
        LogManager.GetLogger (typeof (InvokeActivity));

    public InArgument<Activity> Activity { get; set; }

    public InvokeActivity ()
    {
        _log.DebugFormat ("Instantiated.");
    }

    protected override void Execute (NativeActivityContext context)
    {
        Activity activity = Activity.Get (context);

        _log.DebugFormat ("Scheduling activity [{0}]...", activity.DisplayName);

        // throws exception to lack of metadata! :(
        ActivityInstance instance = 
            context.ScheduleActivity (activity, OnComplete, OnFault);

        _log.DebugFormat (
            "Scheduled activity [{0}] with instance id [{1}].", 
            activity.DisplayName, 
            instance.Id);
    }

    protected override void CacheMetadata (NativeActivityMetadata metadata)
    {
        // how does one add InArgument<T> to metadata? not easily
        // is my first guess
        base.CacheMetadata (metadata);
    }

    // private methods

    private void OnComplete (
        NativeActivityContext context, 
        ActivityInstance instance)
    {
        _log.DebugFormat (
            "Scheduled activity [{0}] with instance id [{1}] has [{2}].",
            instance.Activity.DisplayName, 
            instance.Id, 
            instance.State);
    }

    private void OnFault (
        NativeActivityFaultContext context, 
        Exception exception, 
        ActivityInstance instance)
    {
        _log.ErrorFormat (
@"Scheduled activity [{0}] with instance id [{1}] has faulted in state [{2}] 
{3}", 
            instance.Activity.DisplayName, 
            instance.Id, 
            instance.State, 
            exception.ToStringFullStackTrace ());
    }
}

尝试在当前上下文中安排指定的Activity。不幸的是,这失败了。当我尝试安排所述活动时,运行时返回以下异常

  

在处理元数据时,提供的活动不是此工作流定义的一部分。名为“DynamicActivity”的有问题的活动由名为“InvokeActivity”的活动提供。

是的,因此在运行时提供的“动态”活动不是InvokeActivity元数据的成员。 Googled并遇到this。无法理清如何指定InArgument<Activity>元数据缓存,所以我的第二个问题当然是如何解决这个问题?不建议以这种方式使用context.ScheduleActivity (...)吗?

第三也是最后一次,

3)我暂时已经解决了这个[更简单]的解决方案,

public sealed class InvokeActivity : NativeActivity
{
    private static readonly ILog _log = 
        LogManager.GetLogger (typeof (InvokeActivity));

    public InArgument<Activity> Activity { get; set; }

    public InvokeActivity ()
    {
        _log.DebugFormat ("Instantiated.");
    }

    protected override void Execute (NativeActivityContext context)
    {
        Activity activity = Activity.Get (context);

        _log.DebugFormat ("Invoking activity [{0}] ...", activity.DisplayName);

        // synchronous execution ... a little less than ideal, this
        // seems heavy handed, and not entirely semantic-equivalent
        // to what i want. i really want to invoke this runtime
        // activity as if it were one of my own, not a separate
        // process - wrong mentality?
        WorkflowInvoker.Invoke (activity);

        _log.DebugFormat ("Invoked activity [{0}].", activity.DisplayName);
    }

}

它只是在自己的运行时实例中同步调用指定的任务[使用WF4白话肯定是有问题的]。最后,我想利用WF的跟踪和可能的持久性设施。所以我的第三个也是最后一个问题是,就我想做的事情而言[即启动从客户端应用程序入站的任意工作流程]这是首选方法吗?

好的,提前感谢你的时间和考虑:)

1 个答案:

答案 0 :(得分:2)

  

在处理元数据时,提供的活动不属于此工作流程定义的一部分。名为“DynamicActivity”的有问题的活动由名为“InvokeActivity”的活动&gt;提供。

Workflow 4.0仅允许您在开始执行之前安排属于树的子项。

此规则可能存在,因为如此构建的树可由多个工作站实例重用。如果实例A在实例B仍在运行时修改树,则结果会给运行时团队带来可怕的麻烦。

实际上,这意味着您可以在运行时动态调度子项,从而实现所需的唯一方法是启动全新的工作流程(并可选择等待完成)。

相关问题