Использование делегатов действий
Делегаты действий позволяют создателям действий предоставлять обратные вызовы с определенными сигнатурами, для которых пользователи действия могут предоставить обработчики, основанные на действиях. Доступны два типа делегатов действия: 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> определяется с использованием 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);