そのため、実行時に提供されたアクティビティを開始する必要があります。これを容易にするために、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>
...「ワークフロー」を繰り返し使用することを除けば、これは非常に簡単です。実際、これはただのSequence
with であり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」という名前のアクティビティによって提供されました。
そうです、実行時に提供される「動的」アクティビティはInvokeActivity
s メタデータのメンバーではありません。グーグルでこれに出くわしました。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 番目で最後の質問は、私がやりたいこと [つまり、クライアント アプリケーションから受信する任意のワークフローを開始する] に関して、これが望ましい方法ですか?
わかりました、あなたの時間と考慮を前もってありがとう:)