3

そのため、実行時に提供されたアクティビティを開始する必要があります。これを容易にするために、Activity を Xaml として受け取り、ハイドレートし、開始する WorkflowService をセットアップしました。

シンプルに聞こえます...

... これは私の 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>

...「ワークフロー」を繰り返し使用することを除けば、これは非常に簡単です。実際、これはただのSequencewith でありReceive、[現在] と呼ばれるカスタム アクティビティInvokeActivityです。それについては少し後で説明します。

Receiveアクティビティはカスタム タイプを受け入れます。

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

これには、内容が Xaml として解釈される文字列が含まれます。この Xaml をアクティビティに変換して渡す VB 式を確認できます。

さて、この 2 番目のビットですが、カスタムInvokeActivityについて質問があります。

最初の質問:

1) [上記のように] 実行時に提供される任意のタスクが与えられた場合、WF4RC に同梱されているアクティビティを使用して、すぐにこのアクティビティを開始することは可能ですか? 私はかなり新しいので、API と既存のドキュメントをよく調べたと思いますが、質問することもできます :)

2番:

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」という名前のアクティビティによって提供されました。

そうです、実行時に提供される「動的」アクティビティはInvokeActivitys メタデータのメンバーではありません。グーグルでこれに出くわしました。to メタデータ キャッシュを指定する方法を整理できなかったInArgument<Activity>ので、私の 2 番目の質問は、当然のことながら、この問題にどのように対処するかということです。このように使用することはお勧めできcontext.ScheduleActivity (...)ませんか?

3番目で最後の、

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 の追跡機能と、場合によっては永続化機能を利用したいと考えています。3 番目で最後の質問は、私がやりたいこと [つまり、クライアント アプリケーションから受信する任意のワークフローを開始する] に関して、これが望ましい方法ですか?

わかりました、あなたの時間と考慮を前もってありがとう:)

4

1 に答える 1

3

提供されたアクティビティは、そのメタデータが処理されていたときに、このワークフロー定義の一部ではありませんでした。「DynamicActivity」という名前の問題のあるアクティビティは、「InvokeActivity」という名前のアクティビティによって提供されました。

ワークフロー 4.0 では、実行を開始する前にツリーの一部であった子のみをスケジュールできます。

このように構築されたツリーは、複数のワークフロー インスタンスで再利用できるため、このルールが存在する可能性があります。インスタンス B がまだ実行されている間にインスタンス A がツリーを変更した場合、その結果はランタイム チームにひどい頭痛の種を与えることになります。

実際には、これは、実行時に子を動的にスケジュールすることで必要なことを実行できる唯一の方法は、まったく新しいワークフローを開始することです (必要に応じて、それが完了するのを待ちます)。

于 2010-04-30T18:24:47.637 に答える