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

发布于 2024-08-23 07:27:30 字数 5878 浏览 11 评论 0原文

所以我有这个要求来启动在运行时提供给我的活动。为了实现这一点,我设置了一个 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>

……除了重复使用“工作流”之外,它非常简单。事实上,它只是一个带有 ReceiveSequence 和[当前]一个名为 InvokeActivity 的自定义 Activity。稍后再讲。

Receive Activity 接受自定义类型,

[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 ());
    }
}

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

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

是的,所以运行时提供的“动态”Activity 不是 InvokeActivity 元数据的成员。 Google 搜索并发现。无法弄清楚如何指定 InArgument 到元数据缓存,所以我的第二个问题自然是如何解决这个问题?以这种方式使用 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 的跟踪和可能的持久性设施。所以我的第三个也是最后一个问题是,就我想做的事情而言[即启动从客户端应用程序入站的任意工作流程]这是首选方法吗?

好的,预先感谢您的时间和考虑:)

so I have this requirement to kick off Activities provided to me at run-time. To facilitate this, I have set up a WorkflowService that receives Activities as Xaml, hydrates them, and kicks them off.

Sounds simple enough ...

... this is my WorkflowService in Xaml

<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>

... which, except for repetitive use of "Workflow" is pretty straight forward. In fact, it's just a Sequence with a Receive and [currently] a custom Activity called InvokeActivity. Get to that in a bit.

Receive Activity accepts a custom type,

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

which contains a string whose contents are to be interpreted as Xaml. You can see the VB expression that then converts this Xaml to an Activity and passes it on.

Now this second bit, the custom InvokeActivity is where I have questions.

First question:

1) given an arbitrary task, provided at runtime [as described above] is it possible to kick off this Activity using Activities that ship with WF4RC, out of the box? I'm fairly new, and thought I did a good job going through the API and existing documentation, but may as well ask :)

Second:

2) my first attempt at implementing a custom InvokeActivity looked like this

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 ());
    }
}

Which attempts to schedule the specified Activity within the current context. Unfortunately, however, this fails. When I attempt to schedule said Activity, the runtime returns with the following exception

The provided activity was not part of this workflow definition when its metadata was being processed. The problematic activity named 'DynamicActivity' was provided by the activity named 'InvokeActivity'.

Right, so the "dynamic" Activity provided at runtime is not a member of InvokeActivitys metadata. Googled and came across this. Couldn't sort out how to specify an InArgument<Activity> to metadata cache, so my second question is, naturally, how does one address this issue? Is it ill advised to use context.ScheduleActivity (...) in this manner?

Third and final,

3) I have settled on this [simpler] solution for the time being,

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);
    }

}

Which simply invokes specified task synchronously within its own runtime instance thingy [use of WF4 vernacular is certainly questionable]. Eventually, I would like to tap into WF's tracking and possibly persistance facilities. So my third and final question is, in terms of what I would like to do [ie kick off arbitrary workflows inbound from client applications] is this the preferred method?

Alright, thanks in advance for your time and consideration :)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

孤者何惧 2024-08-30 07:27:30

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

工作流 4.0 只允许您在开始执行之前安排属于树的一部分的子级。

该规则可能存在,因为如此构建的树可由多个工作流实例重用。如果实例 A 在实例 B 仍在运行时修改树,结果会让运行时团队非常头疼。

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

The provided activity was not part of this workflow definition when its metadata was being >processed. The problematic activity named 'DynamicActivity' was provided by the activity >named 'InvokeActivity'.

Workflow 4.0 only allows you to schedule children which were part of the tree before you started execution.

This rule probably exists because the tree so built is re-usable by multiple worklfow instances. If instance A were to modify the tree while instance B is still running, the results would give the runtime team horrible headaches.

In practice this means the only way you can do what you want with scheduling the child dynamically at runtime is to kick off an entirely new workflow (and optionally wait for that to complete).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文