使用活动委托

通过活动委托,活动作者可以公开具有特定签名的回调,活动的用户可为其提供基于活动的处理程序。 可以使用两种类型的活动委托:ActivityAction<T> 用于定义没有返回值的活动委托,ActivityFunc<TResult> 用于定义有返回值的活动委托。

在必须对子活动进行限制,使其包含特定签名的情况下,活动委托非常有用。 例如,While 活动可包含任何类型的无约束子活动,但 ForEach<T> 活动的主体是一个 ActivityAction<T>,并且 ForEach<T> 最终执行的子活动必须具有 InArgument<T>ForEach<T> 所枚举的集合成员相同的类型。

使用 ActivityAction

一些 .NET Framework 4.6.1 活动使用活动操作,例如 Catch 活动和 ForEach<T> 活动。 在每种情况下,活动操作都表示一个位置,当使用这些活动之一构造工作流时,工作流作者在此处指定一个活动来提供所需的行为。 在下面的示例中,使用 ForEach<T> 活动来向控制台窗口显示文本。 使用一个与 ForEach<T> 的类型(此处为 string)相匹配的 ActivityAction<T> 来指定 ForEach<T> 的主体。 在 WriteLine 中指定的 Handler 活动将它的 Text 自变量绑定到 ForEach<T> 活动迭代的集合中的字符串值。

DelegateInArgument<string> actionArgument = new DelegateInArgument<string>();

Activity wf = new ForEach<string>
{
    Body = new ActivityAction<string>
    {
        Argument = actionArgument,
        Handler = new WriteLine
        {
            Text = new InArgument<string>(actionArgument)
        }
    }
};

List<string> items = new List<string>();
items.Add("Hello");
items.Add("World.");

Dictionary<string, object> inputs = new Dictionary<string, object>();
inputs.Add("Values", items);

WorkflowInvoker.Invoke(wf, inputs);

actionArgument 用于将集合中的各个项流到 WriteLine。 在调用工作流时,将下面的输出显示到控制台。

HelloWorld.

本主题中的示例使用对象初始化语法。 对于用代码创建工作流定义而言,对象初始化语法非常有用,因为它为工作流中的活动提供了分层视图,可以显示活动之间的关系。 通过编程方式创建工作流时,不要求必须使用对象初始化语法。 下面的示例与前面的示例在功能上是等效的。

DelegateInArgument<string> actionArgument = new DelegateInArgument<string>();

WriteLine output = new WriteLine();
output.Text = new InArgument<string>(actionArgument);

ActivityAction<string> body = new ActivityAction<string>();
body.Argument = actionArgument;
body.Handler = output;

ForEach<string> wf = new ForEach<string>();
wf.Body = body;

List<string> items = new List<string>();
items.Add("Hello");
items.Add("World.");

Dictionary<string, object> inputs = new Dictionary<string, object>();
inputs.Add("Values", items);

WorkflowInvoker.Invoke(wf, inputs);

有关对象初始值设定项的更多信息,请参阅如何:在不调用构造函数的情况下初始化对象(C# 编程指南)如何:使用对象初始值设定项声明对象 (Visual Basic)

在下面的示例中,在工作流中使用一个 TryCatch 活动。 该工作流引发一个 ApplicationException,并由 Catch<TException> 活动对其进行处理。 Catch<TException> 活动的活动操作的处理程序是一个 WriteLine 活动,并且异常详细信息是使用 ex DelegateInArgument<T> 流动到该活动的。

DelegateInArgument<ApplicationException> ex = new DelegateInArgument<ApplicationException>()
{
    Name = "ex"
};

Activity wf = new TryCatch
{
    Try = new Throw()
    {
        Exception = new InArgument<Exception>((env) => new ApplicationException("An ApplicationException was thrown."))
    },
    Catches =
    {
        new Catch<ApplicationException>
        {
            Action = new ActivityAction<ApplicationException>
            {
                Argument = ex,
                Handler = new WriteLine()
                {
                    Text = new InArgument<string>((env) => ex.Get(env).Message)
                }
            }
        }
    },
    Finally = new WriteLine()
    {
        Text = "Executing in Finally."
    }
};

创建定义 ActivityAction<T> 的自定义活动时,使用 InvokeAction<T> 来建立调用该 ActivityAction<T> 的模型。 在本示例中,定义了自定义 WriteLineWithNotification 活动。 此活动由一个 Sequence 组成,它包含一个 WriteLine 活动,该活动后面是一个调用接收一个字符串参数的 InvokeAction<T>ActivityAction<T>

public class WriteLineWithNotification : Activity
{
    public InArgument<string> Text { get; set; }
    public ActivityAction<string> TextProcessedAction { get; set; }

    public WriteLineWithNotification()
    {
        this.Implementation = () => new Sequence
        {
            Activities =
            {
                new WriteLine
                {
                    Text = new InArgument<string>((env) => Text.Get(env))
                },
                new InvokeAction<string>
                {
                    Action = TextProcessedAction,
                    Argument = new InArgument<string>((env) => Text.Get(env))
                }
            }
        };
    }
}

使用 WriteLineWithNotification 活动创建工作流时,工作流作者在活动操作的 Handler 中指定所需的自定义逻辑。 在本示例中,创建一个使用 WriteLineWithNotification 活动的工作流,并将 WriteLine 活动用作 Handler

// Create and invoke the workflow without specifying any activity action
// for TextProcessed.
Activity wf = new WriteLineWithNotification
{
    Text = "Hello World."
};

WorkflowInvoker.Invoke(wf);

// Output:
// Hello World.

// Create and Invoke the workflow with specifying an activity action
// for TextProcessed.
DelegateInArgument<string> msg = new DelegateInArgument<string>();
Activity wf2 = new WriteLineWithNotification
{
    Text = "Hello World with activity action.",
    TextProcessedAction = new ActivityAction<string>
    {
        Argument = msg,
        Handler = new WriteLine
        {
            Text = new InArgument<string>((env) => "Handler of: " + msg.Get(env))
        }
    }
};

// Invoke the workflow with an activity action specified
WorkflowInvoker.Invoke(wf2);

// Output:
// Hello World with activity action.
// Handler of: Hello World with activity action.

InvokeAction<T> 有多个泛型版本,提供 ActivityAction<T> 用于传递一个或多个自变量。

使用 ActivityFunc

当活动中没有结果值时,ActivityAction<T> 将很有用,当返回结果值时,将使用 ActivityFunc<TResult>。 创建定义 ActivityFunc<TResult> 的自定义活动时,使用 InvokeFunc<TResult> 来建立调用该 ActivityFunc<TResult> 的模型。 下面的示例定义了一个 WriteFillerText 活动。 为了提供填充符文本,将指定一个 InvokeFunc<TResult>,它接收一个整数参数并具有一个字符串结果。 在检索填充符文本之后,将会使用 WriteLine 活动将其显示到控制台中。

public class WriteFillerText : Activity
{
    public ActivityFunc<int, string> GetText { get; set; }
    public InArgument<int> Quantity { get; set; }

    Variable<string> text = new Variable<string>
    {
        Name = "Text"
    };

    public WriteFillerText()
    {
        this.Implementation = () => new Sequence
        {
            Variables =
            {
                text
            },
            Activities =
            {
                new InvokeFunc<int, string>
                {
                    Func = GetText,
                    Argument = new InArgument<int>((env) => Quantity.Get(env)),
                    Result = new OutArgument<string>(text)
                },
                new WriteLine
                {
                    Text = new InArgument<string>(text)
                }
            }
        };
    }
}

若要提供文本,必须使用接收一个 int 自变量并具有一个字符串结果的活动。 本示例演示满足这些需求的 TextGenerator 活动。

public class TextGenerator : CodeActivity<string>
{
    public InArgument<int> Quantity { get; set; }
    public InArgument<string> Text { get; set; }

    protected override string Execute(CodeActivityContext context)
    {
        // Provide a quantity of Random Text
        int q = Quantity.Get(context);
        if (q < 1)
        {
            q = 1;
        }

        string text = Text.Get(context) + " ";
        StringBuilder sb = new StringBuilder(text.Length * q);
        for (int i = 0; i < q; i++)
        {
            sb.Append(text);
        }

        return sb.ToString();
    }
}

若要将 TextGenerator 活动和 WriteFillerText 活动一起使用,应将前者指定为 Handler

DelegateInArgument<int> actionArgument = new DelegateInArgument<int>();

Activity wf = new WriteFillerText
{
    Quantity = 5,
    GetText = new ActivityFunc<int, string>
    {
        Argument = actionArgument,
        Handler = new TextGenerator
        {
            Quantity = new InArgument<int>(actionArgument),
            Text = "Hello World."
        }
    }
};

WorkflowInvoker.Invoke(wf);