Как передать параметры CodeActivity в кодовой последовательности NativeActivity

Я пытаюсь заставить рабочие процессы Windows работать, и я немного запутался.

У меня заработал один рабочий процесс, но теперь я пытаюсь сделать что-то более сложное: запустить рабочий процесс, где каждое действие само по себе содержит рабочий процесс. (Представьте, что основная программа запускает действия «Ввод, логика и вывод», а затем у каждого из них есть дополнительные действия, такие как «подсказка пользователю, получение ввода и т. д.»).

У меня все работало нормально, пример отсюда (http://msdn.microsoft.com/en-us/magazine/gg535667.aspx), когда я не передаю никаких параметров из основной программы в действия. Мой вопрос в том, как именно «Переменные» и «metadata.SetVariablesCollection» работают в NativeActivity и как мне получить параметры для действий низкого уровня?

Это то, что я сейчас пытаюсь:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Activities;
using System.Collections.ObjectModel;
using System.Activities.Statements;

namespace Project1
{
    internal class MainProgram
    {
        internal static void Main(string[] args)
        {
            try
            {
                var act = new SimpleSequence();

                act.Activities.Add((Activity)(new WriteSomeText()));
                act.Activities.Add((Activity)(new WriteSomeText()));
                act.Activities.Add((Activity)(new WriteSomeText()));

                act.Variables.Add(new Variable<string> ("stringArg", "TEXT"));

                WorkflowInvoker.Invoke(act);
            }
            catch (Exception ex)
            {
                System.Console.WriteLine("EXCEPTION: {0}", ex);
            }
        }

        public class WriteSomeText : CodeActivity
        {
            [RequiredArgument]
            public InArgument<string> stringArg { get; set; }

            protected override void Execute(CodeActivityContext context)
            {
                string output = context.GetValue(stringArg);
                System.Console.WriteLine(output);
            }

        }

        public class SimpleSequence : NativeActivity
        {
            Collection<Activity> activities;
            Collection<Variable> variables;

            Variable<int> current = new Variable<int> { Default = 0 };

            public Collection<Activity> Activities
            {
                get
                {
                    if (this.activities == null)
                        this.activities = new Collection<Activity>();

                    return this.activities;
                }
                set
                {
                    this.activities = value;
                }
            }

            public Collection<Variable> Variables
            {
                get
                {
                    if (this.variables == null)
                        this.variables = new Collection<Variable>();

                    return this.variables;
                }
                set
                {
                    this.variables = value;
                }
            }

            protected override void CacheMetadata(NativeActivityMetadata metadata)
            {
                metadata.SetChildrenCollection(this.activities);
                metadata.SetVariablesCollection(this.variables);
                metadata.AddImplementationVariable(this.current);
            }

            protected override void Execute(NativeActivityContext context)
            {
                if (this.Activities.Count > 0)
                    context.ScheduleActivity(this.Activities[0], onChildComplete);
            }

            void onChildComplete(NativeActivityContext context, ActivityInstance completed)
            {
                int currentExecutingActivity = this.current.Get(context);
                int next = currentExecutingActivity + 1;

                if (next < this.Activities.Count)
                {
                    context.ScheduleActivity(this.Activities[next], this.onChildComplete);

                    this.current.Set(context, next);
                }
            }
        }
    }
}

Это приводит к следующему исключению:

EXCEPTION: System.Activities.InvalidWorkflowException: The following errors were encountered while processing the workflow tree:
'WriteSomeText': Value for a required activity argument 'stringArg' was not supplied.
'WriteSomeText': Value for a required activity argument 'stringArg' was not supplied.
'WriteSomeText': Value for a required activity argument 'stringArg' was not supplied.
   at System.Activities.Validation.ActivityValidationServices.ThrowIfViolationsExist(IList`1 validationErrors)
   at System.Activities.Hosting.WorkflowInstance.ValidateWorkflow(WorkflowInstanceExtensionManager extensionManager)
   at System.Activities.Hosting.WorkflowInstance.RegisterExtensionManager(WorkflowInstanceExtensionManager extensionManager)
   at System.Activities.WorkflowApplication.EnsureInitialized()
   at System.Activities.WorkflowApplication.RunInstance(WorkflowApplication instance)
   at System.Activities.WorkflowApplication.Invoke(Activity activity, IDictionary`2 inputs, WorkflowInstanceExtensionManager extensions, TimeSpan timeout)
   at System.Activities.WorkflowInvoker.Invoke(Activity workflow, TimeSpan timeout, WorkflowInstanceExtensionManager extensions)
   at System.Activities.WorkflowInvoker.Invoke(Activity workflow)
   at Project1.MainProgram.Main(String[] args) in c:\users\user\documents\visual studio 2010\Projects\ModelingProject1\Project1\MainProgram.cs:line 25

Я знаю, я передаю только 1 параметр, но исключение все равно говорит, что мне не хватает 3 параметров. Я что-то упускаю из виду, как это сделать правильно.


person Curtor    schedule 06.09.2011    source источник


Ответы (1)


Вы правильно объявляете stringArg как InArgument, но не передаете ему никакого значения при вызове внутри SimpleSequence.

Вы можете передать что-то с помощью конструктора при создании всего действия, например:

public class WriteSomeText : CodeActivity
{
    [RequiredArgument]
    public InArgument<string> stringArg { get; set; }

    public WriteSomeText(string stringArg)
    {
        this.stringArg = stringArg;
    }

    protected override void Execute(CodeActivityContext context
    {
        string output = context.GetValue(stringArg);
        System.Console.WriteLine(output);
    }
}

// Calling the activity like this:

internal static void Main(string[] args)
{
    var act = new SimpleSequence()
    {
        Activities =
        {
            new WriteSomeText("hello"),
            new WriteSomeText("world"),
            new WriteSomeText("!")
        }
    };

    WorkflowInvoker.Invoke(act);

    Console.WriteLine("Press any key to exit");
    Console.ReadKey();
}

Также обратите внимание, что рекомендуется использовать конструктор для инициализации коллекций:

public SimpleSequence()
{
    activities = new Collection<Activity>();
    variables = new Collection<Variable>();
}

Этот способ еще более интуитивно понятен для инициализации активности:

var act = new SimpleSequence()
{
    Activities =
    {
        new WriteSomeText("hello"),
        new WriteSomeText("world"),
        new WriteSomeText("!")
    },
    Variables =
    {
        new Variable<int>("myNewIntVar", 10),
        // ....
    }
};

РЕДАКТИРОВАТЬ:

Есть еще несколько способов подойти к проблеме. Это ваш лучший друг в мире WF4.

Проверьте WF\Basic\CustomActivities\Code-Bodied, чтобы узнать, что нужно для этого конкретного случая.

person Joao    schedule 06.09.2011
comment
Вы также можете жестко закодировать stringArg, когда следующим действием в вашей SimpleSequence является WriteSomeText, но это далеко не лучшая практика. Жестко запрограммировано — тому подтверждение. - person Joao; 06.09.2011
comment
Спасибо за это, но тогда возникает мой вопрос: в чем смысл переменной Variables в SimpleSequence? Каково предполагаемое использование этого, если все действия кода получают свои параметры во время построения? - person Curtor; 06.09.2011
comment
Вы должны смотреть на Variable‹T› как на локальную переменную в обычном методе, используемом для управления и хранения данных временно. Когда вы создаете пользовательское действие, это похоже на предоставление метода, который будет использоваться. Вы не хотите, чтобы пользователь касался локальных переменных внутри вашего метода (используете ли вы обычный язык программирования или сам WF4). Любые входные данные для того же метода передаются через InArgument, который соответствует объявлению аргумента в методе. - person Joao; 06.09.2011
comment
Ваш SimpleSequence или нативный Sequence, если на то пошло, содержит Collection‹Variable›, которую пользователь может использовать при разработке собственной деятельности с помощью дизайнера или кода, но, опять же, для хранить и обрабатывать данные для их конкретной деятельности, а не для вашей пользовательской. Если вы хотите, чтобы ваша пользовательская активность была динамичной в соответствии с внешними переменными, вы используете InArguments. Надеюсь, я ясно выразился. - person Joao; 06.09.2011
comment
В итоге вы должны смотреть на WF4 как на способ писать код через код. Это простой способ выразить это, но в некотором смысле это правда. Вы предоставляете настраиваемые действия (методы) для вызова пользователем. Некоторые NativeActivities являются держателями (например, Sequence и парой других), чтобы пользователь мог делать в них то, что он хочет. - person Joao; 06.09.2011
comment
Большое спасибо за вклад. Я нахожусь в процессе анализа и пробую некоторые из предложенных вами вещей, но отзывы очень ценятся, несмотря ни на что. У меня были проблемы с поиском действительно хорошей документации или примеров такого рода вещей. - person Curtor; 06.09.2011
comment
Еще одна вещь, с которой я столкнулся, пытаясь это сделать: как бы вы присвоили Out или InOutArgument? - person Curtor; 06.09.2011
comment
Я даю вам вклад, который я хотел иметь, когда начинал сам. Проверьте ссылку, которую я разместил выше. Это был мой лучший друг, когда я начинал, и остается им по сей день. Удачи. - person Joao; 06.09.2011
comment
OutArguments связаны со свойством Result, если действие с результатом. Вы можете либо context.SetValue(base.Result, anyValuehere);, если внутри пользовательского действия, либо Назначить действие при назначении извне. Опять же, посмотрите на образцы. Любая новая проблема, пожалуйста, откройте новый вопрос. Ваше здоровье - person Joao; 06.09.2011