Планирование активности во время выполнения в Workflow 4 RC - PullRequest
3 голосов
/ 03 марта 2010

поэтому у меня есть это требование, чтобы начать действия, предоставленные мне во время выполнения. Чтобы облегчить это, я настроил 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 *

1 Ответ

2 голосов
/ 30 апреля 2010

Предоставленное действие не было частью этого определения рабочего процесса, когда его метаданные> обрабатывались. Проблемное действие с именем «DynamicActivity» было предоставлено действием> с именем «InvokeActivity».

Workflow 4.0 позволяет планировать только дочерние элементы, которые были частью дерева, до того, как вы начали выполнение.

Это правило, вероятно, существует, потому что построенное таким образом дерево может использоваться несколькими экземплярами worklfow. Если бы экземпляр A изменил дерево, а экземпляр B все еще работал, результаты вызвали бы у команды времени выполнения ужасные головные боли.

На практике это означает, что единственный способ сделать то, что вы хотите с помощью динамического планирования дочернего процесса во время выполнения, - запустить совершенно новый рабочий процесс (и, необязательно, дождаться его завершения).

...