使用活动委托
通过活动委托,活动作者可以公开具有特定签名的回调,活动的用户可为其提供基于活动的处理程序。 可以使用两种类型的活动委托: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);