Сериализация рабочих процессов и действий в XAML и из нее
Кроме компиляции в содержащиеся в сборках типы определения рабочих процессов также могут быть сериализованы в XAML. Такие сериализованные определения могут быть загружены повторно для редактирования или просмотра, переданы в систему сборки для компиляции или загружены и вызваны. В этом разделе представлены общие сведения о сериализации определений рабочих процессов и работе с определениями рабочих процессов языка XAML.
Работа с определениями рабочих процессов XAML
Для создания определения рабочего процесса для сериализации используется класс ActivityBuilder. Создание класса ActivityBuilder очень похоже на создание DynamicActivity. Можно указать любые необходимые аргументы и настроить действия, составляющие поведение. В следующем примере создается действие Add
, которое принимает два входных аргумента, складывает их и возвращает результат. Поскольку это действие возвращает результат, используется универсальный класс ActivityBuilder<TResult>.
ActivityBuilder<int> ab = new ActivityBuilder<int>();
ab.Name = "Add";
ab.Properties.Add(new DynamicActivityProperty { Name = "Operand1", Type = typeof(InArgument<int>) });
ab.Properties.Add(new DynamicActivityProperty { Name = "Operand2", Type = typeof(InArgument<int>) });
ab.Implementation = new Sequence
{
Activities =
{
new WriteLine
{
Text = new VisualBasicValue<string>("Operand1.ToString() + \" + \" + Operand2.ToString()")
},
new Assign<int>
{
To = new ArgumentReference<int> { ArgumentName = "Result" },
Value = new VisualBasicValue<int>("Operand1 + Operand2")
}
}
};
Каждый экземпляр DynamicActivityProperty представляет собой один входной аргумент для рабочего процесса, а класс Implementation содержит действия, которые образуют логику рабочего процесса. Обратите внимание, что выражения правостороннего значения в этом примере - это выражения Visual Basic. Лямбда-выражения не могут быть сериализованы в языке XAML, если не используется Convert. Если сериализованные рабочие процессы предназначены для открытия или изменения в конструкторе рабочих процессов, следует использовать выражения Visual Basic. Дополнительные сведения см. в статье "Создание рабочих процессов,действий и выражений с помощью императивного кода".
Для сериализации определения рабочего процесса, представленного экземпляром ActivityBuilder в языке XAML, используйте класс ActivityXamlServices, чтобы создать XamlWriter, и затем класс XamlServices, чтобы сериализовать определение рабочего процесса с помощью XamlWriter. ActivityXamlServices имеет методы сопоставления ActivityBuilder экземпляров с XAML и из него, а также для загрузки рабочих процессов XAML и возврата вызываемого DynamicActivity экземпляра. В следующем примере экземпляр из предыдущего примера ActivityBuilder сериализуется в строку и сохраняется в файле.
// Serialize the workflow to XAML and store it in a string.
StringBuilder sb = new StringBuilder();
StringWriter tw = new StringWriter(sb);
XamlWriter xw = ActivityXamlServices.CreateBuilderWriter(new XamlXmlWriter(tw, new XamlSchemaContext()));
XamlServices.Save(xw, ab);
string serializedAB = sb.ToString();
// Display the XAML to the console.
Console.WriteLine(serializedAB);
// Serialize the workflow to XAML and save it to a file.
StreamWriter sw = File.CreateText(@"C:\Workflows\add.xaml");
XamlWriter xw2 = ActivityXamlServices.CreateBuilderWriter(new XamlXmlWriter(sw, new XamlSchemaContext()));
XamlServices.Save(xw2, ab);
sw.Close();
В следующем примере представлен сериализованный рабочий процесс.
<Activity
x:TypeArguments="x:Int32"
x:Class="Add"
xmlns="http://schemas.microsoft.com/netfx/2009/xaml/activities"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<x:Members>
<x:Property Name="Operand1" Type="InArgument(x:Int32)" />
<x:Property Name="Operand2" Type="InArgument(x:Int32)" />
</x:Members>
<Sequence>
<WriteLine Text="[Operand1.ToString() + " + " + Operand2.ToString()]" />
<Assign x:TypeArguments="x:Int32" Value="[Operand1 + Operand2]">
<Assign.To>
<OutArgument x:TypeArguments="x:Int32">
<ArgumentReference x:TypeArguments="x:Int32" ArgumentName="Result" />
</OutArgument>
</Assign.To>
</Assign>
</Sequence>
</Activity>
Чтобы загрузить сериализованный рабочий процесс, используйте ActivityXamlServices Load этот метод. При этом сериализуется определение рабочего процесса и возвращается действие DynamicActivity, представляющее определение рабочего процесса. Обратите внимание, что XAML не десериализуется до тех пор, пока действие CacheMetadata не будет вызвано для тела DynamicActivity во время процесса проверки. Если проверка не вызывается явным образом, она выполняется при вызове рабочего процесса. Если определение рабочего процесса XAML является недействительным, возникает исключение ArgumentException. Исключения, возникающие из-за CacheMetadata, не реагируют на вызов Validate и должны быть обработаны вызывающим классом. В следующем примере сериализованный рабочий процесс из предыдущего примера загружается и вызывается с помощью класса WorkflowInvoker.
// Load the workflow definition from XAML and invoke it.
DynamicActivity<int> wf = ActivityXamlServices.Load(new StringReader(serializedAB)) as DynamicActivity<int>;
Dictionary<string, object> wfParams = new Dictionary<string, object>
{
{ "Operand1", 25 },
{ "Operand2", 15 }
};
int result = WorkflowInvoker.Invoke(wf, wfParams);
Console.WriteLine(result);
При вызове этого рабочего процесса на консоль выводятся следующие данные.
25 + 15
40
Примечание.
Дополнительные сведения о вызове рабочих процессов с входными и выходными аргументами см. в разделе Using WorkflowInvoker и WorkflowApplication и Invoke.
Если сериализованный рабочий процесс содержит выражения C#, ActivityXamlServicesSettings экземпляр со своим CompileExpressions набором true
свойств должен быть передан в качестве параметра ActivityXamlServices.Load, в противном случае NotSupportedException создается сообщение, аналогичное следующему: тип действия выражения "CSharpValue'1" требует компиляции для выполнения. Убедитесь, что рабочий процесс скомпилирован.
ActivityXamlServicesSettings settings = new ActivityXamlServicesSettings
{
CompileExpressions = true
};
DynamicActivity<int> wf = ActivityXamlServices.Load(new StringReader(serializedAB), settings) as DynamicActivity<int>;
Дополнительные сведения см. в разделе "Выражения C#".
Сериализованное определение рабочего процесса также можно загрузить в ActivityBuilder экземпляр с помощью ActivityXamlServices CreateBuilderReader метода. После загрузки сериализованного ActivityBuilder рабочего процесса в экземпляр его можно проверить и изменить. Это может быть полезно разработчикам пользовательских конструкторов рабочих процессов, а также реализует механизм сохранения и повторной загрузки определений рабочих процессов во время конструирования. В следующем примере загружается определение сериализованного рабочего процесса из предыдущего примера, после чего выполняется просмотр его свойств.
// Create a new ActivityBuilder and initialize it using the serialized
// workflow definition.
ActivityBuilder<int> ab2 = XamlServices.Load(
ActivityXamlServices.CreateBuilderReader(
new XamlXmlReader(new StringReader(serializedAB)))) as ActivityBuilder<int>;
// Now you can continue working with the ActivityBuilder, inspect
// properties, etc...
Console.WriteLine("There are {0} arguments in the activity builder.", ab2.Properties.Count);
foreach (var prop in ab2.Properties)
{
Console.WriteLine("Name: {0}, Type: {1}", prop.Name, prop.Type);
}