поэтому у меня есть это требование, чтобы начать действия, предоставленные мне во время выполнения. Чтобы облегчить это, я настроил WorkflowService, который получает Activity как Xaml, гидратирует их и запускает их.
Звучит достаточно просто ...
... это мой WorkflowService в 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>
... что, за исключением повторного использования "Workflow", довольно просто. Фактически, это просто Sequence
с Receive
и [в настоящее время] пользовательским действием под названием 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 ());
}
}
Который пытается запланировать указанное действие в текущем контексте. К сожалению, однако, это не удается. Когда я пытаюсь запланировать указанное действие, среда выполнения возвращается со следующим исключением
Указанное действие не было частью этого определения рабочего процесса, когда обрабатывались его метаданные. Проблемное действие с именем «DynamicActivity» было предоставлено действием с именем «InvokeActivity».
Правильно, поэтому "динамическое" действие, предоставляемое во время выполнения, не является членом метаданных InvokeActivity
. Гуглил и наткнулся на это . Не могу разобраться, как указать 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 Итак, мой третий и последний вопрос: с точки зрения того, что я хотел бы сделать [т.е. запустить произвольные рабочие процессы, входящие из клиентских приложений], это предпочтительный метод?
Хорошо, заранее спасибо за ваше время и внимание :) 1054 *