Condividi tramite


Serializzare flussi di lavoro e attività da e verso XAML

Oltre alla compilazione in tipi contenuti in assembly, le definizioni di flusso di lavoro possono essere serializzate anche in XAML. Queste definizioni serializzate possono essere ricaricate per la modifica o l'ispezione, passate a un sistema di compilazione per la compilazione o caricate e richiamate. In questo argomento viene fornita una panoramica della serializzazione di definizioni del flusso di lavoro e dell'utilizzo di definizioni del flusso di lavoro XAML.

Usare le definizioni del flusso di lavoro XAML

Per creare una definizione di flusso di lavoro per la serializzazione, viene usata la classe ActivityBuilder. La creazione di un oggetto ActivityBuilder è molto simile alla creazione di DynamicActivity. Vengono specificati gli argomenti desiderati e vengono configurate le attività che costituiscono il comportamento. Nell'esempio seguente viene creata un'attività Add che accetta due argomenti di input, li somma e restituisce il risultato. Poiché questa attività restituisce un risultato, viene usata la classe ActivityBuilder<TResult> generica.

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")
        }
    }
};

Ognuna delle istanze di DynamicActivityProperty rappresenta uno degli argomenti di input per il flusso di lavoro e Implementation contiene le attività che costituiscono la logica del flusso di lavoro. Si noti che le espressioni r-value in questo esempio sono espressioni di Visual Basic. Le espressioni lambda non sono serializzabili nel formato XAML a meno che non si utilizzi Convert. Se i flussi di lavoro serializzati sono progettati per essere aperti o modificati nella finestra di progettazione del flusso di lavoro, occorre usare le espressioni di Visual Basic. Per altre informazioni, vedere Creazione di flussi di lavoro, attività ed espressioni tramite codice imperativo.

Per serializzare la definizione del flusso di lavoro rappresentata dall'istanza di ActivityBuilder in XAML, usare ActivityXamlServices per creare un oggetto XamlWriter, quindi usare XamlServices per serializzare la definizione del flusso di lavoro tramite XamlWriter. ActivityXamlServices offre metodi per eseguire il mapping delle istanze di ActivityBuilder in e da XAML e per caricare flussi di lavoro XAML e restituire un oggetto DynamicActivity che può essere richiamato. Nell'esempio seguente l'istanza di ActivityBuilder dall'esempio precedente viene serializzata in una stringa e salvata in un file.

// 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();

L'esempio seguente rappresenta il flusso di lavoro serializzato.

<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>

Per caricare un flusso di lavoro serializzato, usare il metodo ActivityXamlServicesLoad. che accetta la definizione di flusso di lavoro serializzata e restituisce un oggetto DynamicActivity che rappresenta la definizione di flusso di lavoro. Notare che XAML non viene deserializzato fino a che CacheMetadata non viene chiamato nel corpo di DynamicActivity durante il processo di convalida. Se la convalida non viene chiamata in modo esplicito, viene eseguita quando viene richiamato il flusso di lavoro. Se la definizione di flusso di lavoro XAML non è valida, verrà generata un'eccezione ArgumentException. Qualsiasi eccezione generata da CacheMetadata usa caratteri di escape dalla chiamata a Validate e deve essere gestite dal chiamante. Nell'esempio seguente il flusso di lavoro serializzato dall'esempio precedente viene caricato e richiamato tramite 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);

Quando questo flusso di lavoro viene richiamato, nella console viene visualizzato l'output seguente.

25 + 15
40

Nota

Per altre informazioni su come richiamare flussi di lavoro con argomenti di input e output, vedere Uso di WorkflowInvoker e WorkflowApplication e Invoke.

Se il flusso di lavoro serializzato contiene espressioni di C#, un'istanza di ActivityXamlServicesSettings con la relativa proprietà CompileExpressions impostata su true deve essere passata come parametro a ActivityXamlServices.Load; in caso contrario, verrà generata un'eccezione NotSupportedException con un messaggio simile al seguente: Per l'esecuzione, il tipo di attività dell'espressione 'CSharpValue`1' richiede la compilazione. Assicurarsi che il flusso di lavoro sia stato compilato.

ActivityXamlServicesSettings settings = new ActivityXamlServicesSettings
{
    CompileExpressions = true
};

DynamicActivity<int> wf = ActivityXamlServices.Load(new StringReader(serializedAB), settings) as DynamicActivity<int>;

Per altre informazioni, vedere Espressioni C#.

Una definizione di flusso di lavoro serializzato può essere caricata anche in un'istanza di ActivityBuilder tramite il metodo ActivityXamlServicesCreateBuilderReader. Una volta che un flusso di lavoro serializzato è stato caricato in un'istanza di ActivityBuilder, può essere controllato e modificato. Ciò si rivela utile per gli autori di finestre di progettazione flussi di lavoro personalizzati crea e fornisce un meccanismo per salvare e ricaricare definizioni di flusso di lavoro durante il processo di progettazione. Nell'esempio seguente la definizione di flusso di lavoro serializzato dall'esempio precedente viene caricata e le relative proprietà vengono controllate.

// 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);
}