Планирование дочерних действий, реализующих интерфейс с входными параметрами

public sealed class Parent : NativeActivity
{
    public Parent()
    {
        Childrens = new Collection<Activity>();
        Variables = new Collection<Variable>();

        _currentActivityIndex = new Variable<int>();
        CurrentCustomTypeInstance= new Variable<MyCustomType>();
    }

    [Browsable(false)]
    public Collection<Activity> Childrens { get; set; }

    protected override void Execute(NativeActivityContext context)
    {
        _currentActivityIndex.Set(context, 0);
        context.ScheduleActivity(FirstActivity, Callback);
    }

    private void Callback(NativeActivityContext context, ActivityInstance completedInstance, MyCustomType customTypeInstance)
    {
        CurrentCustomTypeInstance.Set(context, customTypeInstance);
        ScheduleNextChildren(context, completedInstance);
    }

    private void ScheduleNextChildren(NativeActivityContext context, ActivityInstance completedInstance)
    {
        int nextActivityIndex = _currentActivityIndex.Get(context) + 1;
        if (nextActivityIndex >= Childrens.Count)
            return;

        Activity nextActivity = Childrens[nextActivityIndex];

        IFoo nextActivityAsIFoo = nextActivity as IFoo;
        if (nextActivityAsIFoo != null)
        {
            var currentCustomTypeInstance = CurrentCustomTypeInstance.Get(context);
            // HERE IS MY EXCEPTION
            nextActivityAsIFoo.FooField.Set(context, currentCustomTypeInstance);
        }

        context.ScheduleActivity(nextActivity);
        _currentActivityIndex.Set(context, nextActivityIndex);

    }
}

И в метаданных реестра:

metadata.SetChildrenCollection(Childrens);

Я уже читал http://msmvps.com/blogs/theproblemsolver/archive/2011/04/05/scheduling-child-activities-with-input-parameters.aspx, но в моем случае родитель не знает дочернюю активность

Изменить

Подобно: Действие не может установить переменную, определенную в его области действия? < / а>

Activity '1.1: Parent' cannot access this variable because it is declared at the scope
of activity '1.1: Parent'. An activity can only access its own implementation variables.

Но в моем случае мне не нужно получать возвращаемое значение, так что надеюсь, что будет проще. Просто нужно передать FooField неявно, а не передавать его автору потока. Мне нужно сделать это безоговорочно! Если это вообще не сработает, я выберу NativeActivityContext Properties.


person Davi Fiamenghi    schedule 13.09.2011    source источник


Ответы (1)


Понятно! Глядя на блог Мориса, я вдохновился на это

Рабочий процесс (очень-очень простой!)

static void Main(string[] args)
{
    Parent parent = new Parent();
    parent.Childrens.Add(new FooWriter());
    parent.Childrens.Add(new FooFormater());
    parent.Childrens.Add(new FooWriter());

    WorkflowInvoker.Invoke(parent);
    Console.Read();
}

Вывод

What's the Foo name?
Implicit FTW!
Im a custom Foo Handler, my Foo name is: Implicit FTW!
Im a custom Foo Handler, my Foo name is: IMPLICIT FTW!

Реализация

using System;
using System.Activities;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;

namespace WorkflowConsoleApplication2
{
// Parent class that creates a Foo and passes it to their childrens
public sealed class Parent : NativeActivity
{
    private Variable<int> _currentActivityIndex;
    private Variable<Foo> _currentFoo;

    public Parent()
    {
        Childrens = new Collection<Activity>();
        _executionChildrens = new Collection<Tuple<Activity, ActivityAction<Foo>>>();

        _currentActivityIndex = new Variable<int>();
        _currentFoo = new Variable<Foo>();
    }

    [Browsable(false)]
    public Collection<Activity> Childrens { get; set; }
    private Collection<Tuple<Activity, ActivityAction<Foo>>> _executionChildrens;

    protected override void Execute(NativeActivityContext context)
    {
        Console.WriteLine("What's the Foo name?");
        _currentFoo.Set(context, new Foo { Name = Console.ReadLine() });

        _currentActivityIndex.Set(context, 0);
        ScheduleNextChildren(context, null);
    }

    private void ScheduleNextChildren(NativeActivityContext context, ActivityInstance completedInstance)
    {
        int currentActivityIndex = _currentActivityIndex.Get(context);
        if (currentActivityIndex >= Childrens.Count)
            return;

        Tuple<Activity, ActivityAction<Foo>> nextActivity = _executionChildrens[currentActivityIndex];

        if (IsFooHandler(nextActivity))
        {
            context.ScheduleAction(nextActivity.Item2, _currentFoo.Get(context),
                ScheduleNextChildren);
        }
        else
        {
            context.ScheduleActivity(nextActivity.Item1,
                ScheduleNextChildren);
        }

        _currentActivityIndex.Set(context, currentActivityIndex + 1);

    }

    protected override void CacheMetadata(NativeActivityMetadata metadata)
    {
        metadata.SetArgumentsCollection(metadata.GetArgumentsWithReflection());

        metadata.AddImplementationVariable(_currentActivityIndex);
        metadata.AddImplementationVariable(_currentFoo);

        RegisterChildrens(metadata, Childrens);
        // remove "base.Cachemetadata" to "Childrens collection" doesn't become a child   again        }

    public void RegisterChildrens(NativeActivityMetadata metadata, IEnumerable<Activity> childrens)
    {
        foreach (Activity child in childrens)
        {
            IFooHandler childAsIFooHandler = child as IFooHandler;
            if (childAsIFooHandler != null)
            {
                ActivityAction<Foo> childsWrapperAction = new ActivityAction<Foo>();

                var activityToActionBinderArgument = new DelegateInArgument<Foo>();
                childsWrapperAction.Argument =  activityToActionBinderArgument;
                childAsIFooHandler.Foo =        activityToActionBinderArgument;

                childsWrapperAction.Handler = child;

                metadata.AddDelegate(childsWrapperAction);
                _executionChildrens.Add(new Tuple<Activity, ActivityAction<Foo>>(child, childsWrapperAction));
            }
            else
            {
                metadata.AddChild(child);
                _executionChildrens.Add(new Tuple<Activity, ActivityAction<Foo>>(child, null));
            }
        }
    }

    public static bool IsFooHandler(Tuple<Activity, ActivityAction<Foo>> activity)
    {
        return activity.Item2 != null;
    }
}

// samples of Foo handlers
public class FooWriter : CodeActivity, IFooHandler
{
    /// When FooWriter is direct child of "Parent" this argument is passed implicitly
    public InArgument<Foo> Foo { get; set; }

    protected override void Execute(CodeActivityContext context)
    {
        Console.WriteLine("Im a custom Foo Handler, my Foo name is: {0}", Foo.Get(context).Name);
    }
}

public class FooFormater : CodeActivity, IFooHandler
{
    public InArgument<Foo> Foo { get; set; }

    protected override void Execute(CodeActivityContext context)
    {
        Foo foo = Foo.Get(context);
        foo.Name = foo.Name.ToUpper();
    }
}

// sample classes
public class Foo
{
    public string Name { get; set; }
}

public interface IFooHandler
{
    InArgument<Foo> Foo { get; set; }
}
}

Если кто-нибудь знает, как это сделать лучше, пожалуйста, сообщите мне. Мне также нужно будет передать значения вложенным действиям

person Davi Fiamenghi    schedule 14.09.2011
comment
Аааа, я вижу, что ты наделал. Вместо того, чтобы делать это в конструкторе с использованием какого-либо преобразователя, вы используете CacheMetadata (который вызывается при каждом изменении в области дизайна) для создания и регистрации ActivityActions. Очень умно. Это кажется слегка хакерским (две коллекции, несколько ссылок на один и тот же дочерний элемент), но это определенно должно работать и быть намного проще. Мне нравится. - person ; 14.09.2011
comment
Спасибо за комментарий. Думаю, это можно реализовать и с одной коллекцией. Что вы имеете в виду под «каким-то конвертером»? Я все еще ищу альтернативные способы сделать это. - person Davi Fiamenghi; 15.09.2011
comment
Используя преобразователь значений, который преобразует В процессе привязки активность конструктора перешла в ActivityAction. Проблема в том, как в конвертере определить, что именно делать. Есть много возможных делегатов действий на выбор, и вопрос о том, как назвать каждый аргумент в делегате, и как сброшенное действие связывается с ними ... множество вопросов, на которые я еще не ответил. Вот почему это хитроумно. - person ; 15.09.2011
comment
Благослови вас, сэр. У меня седые волосы, пытаясь заставить этот сценарий работать. - person Tudor; 15.10.2018
comment
Не знаю, живете ли вы еще, но видите ли вы какие-нибудь проблемы, если не включить вызов base.CacheMetadata? Я знаю, что это не сработает, если вы это сделаете, но есть ли с этим проблемы? - person Tudor; 16.10.2018
comment
Я не понимаю ваш вопрос ... base.CacheMetadata уже был удален, поэтому Collection<Activity> Childrens не превратился в дублирующую коллекцию выполнения ... он был заменен правильно кешированным _executionChildrens - person Davi Fiamenghi; 16.10.2018
comment
Спасибо, проверив код NativeActivity.CacheMetadata и сравнив его с вашим кодом, кажется, что все поля заполняются правильно, поэтому вызов base.CacheMetadata не требуется. - person Tudor; 16.10.2018