Condividi tramite


Utilizzo di delegati di attività

I delegati di attività consentono agli autori di attività di esporre callback con firme specifiche, per cui gli utenti dell'attività possono fornire gestori in base all'attività. Sono disponibili due tipi di delegati di attività: ActivityAction<T>, usato per definire i delegati di attività senza un valore restituito, e ActivityFunc<TResult>, usato per definire i delegati di attività con un valore restituito.

I delegati di attività sono utili in scenari in cui un'attività figlio deve disporre di una determinata firma. Ad esempio, un'attività While può contenere qualsiasi tipo di attività figlio senza vincoli, ma il corpo di un'attività ForEach<T> è un oggetto ActivityAction<T> e l'attività figlio che viene eseguita alla fine dall'oggetto ForEach<T> deve disporre di un oggetto InArgument<T> dello stesso tipo dei membri della raccolta enumerata dall'oggetto ForEach<T>.

Uso di ActivityAction

Diverse attività di .NET Framework 4.6.1 usano le azioni di attività, ad esempio le attività Catch e ForEach<T>. In ogni caso, l'azione di attività rappresenta un percorso in cui l'autore del flusso di lavoro specifica un'attività per fornire il comportamento desiderato quando si crea un flusso di lavoro usando una di queste attività. Nell'esempio seguente, si usa un'attività ForEach<T> per visualizzare il testo nella finestra della console. Il corpo dell'oggetto ForEach<T> viene specificato tramite un oggetto ActivityAction<T> che corrisponde al tipo dell'oggetto ForEach<T> che è una stringa. L'argomento WriteLine dell'attività Handler specificata nell'oggetto Text è associato ai valori stringa della raccolta scorsa dall'attività 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);

L'oggetto ActionArgument è usato per propagare i singoli elementi della raccolta in WriteLine. Quando il flusso di lavoro viene richiamato, l'output seguente viene visualizzato nella console.

HelloWorld.

Negli esempi di questo argomento viene usata la sintassi di inizializzazione oggetti che può essere utile per creare definizioni del flusso di lavoro in codice in quanto fornisce una visualizzazione gerarchica delle attività nel flusso di lavoro e consente di illustrare la relazione tra le attività. Non è previsto alcun requisito per usare la sintassi di inizializzazione oggetti quando si creano flussi di lavoro a livello di codice. L'esempio seguente rappresenta l'equivalente funzionale dell'esempio precedente.

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

Per altre informazioni sugli inizializzatori di oggetti, vedere Procedura: inizializzare oggetti senza chiamare un costruttore (Guida per programmatori C#) e Procedura: dichiarare un oggetto usando un inizializzatore di oggetto (Visual Basic).

Nell'esempio seguente, un'attività TryCatch è usata in un flusso di lavoro. Un'eccezione ApplicationException viene generata dal flusso di lavoro e gestita da un'attività Catch<TException>. Il gestore dell'azione dell'attività Catch<TException> è un'attività WriteLine e i dettagli dell'eccezione vengono propagati al gestore usando l'oggetto exDelegateInArgument<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."
    }
};

Quando si crea un'attività personalizzata che definisce un oggetto ActivityAction<T>, usare un oggetto InvokeAction<T> per modellare la chiamata di tale oggetto ActivityAction<T>. In questo esempio viene definita un'attività WriteLineWithNotification personalizzata. Questa attività è costituita da un oggetto Sequence che contiene un'attività WriteLine seguita da un oggetto InvokeAction<T> che richiama un oggetto ActivityAction<T> che accetta un argomento di tipo stringa.

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

Quando un flusso di lavoro viene creato usando l'attività WriteLineWithNotification, l'autore del flusso di lavoro specifica la logica personalizzata desiderata nella proprietà Handler dell'azione dell'attività. In questo esempio viene creato un flusso di lavoro che usa l'attività WriteLineWithNotification e un'attività WriteLine viene usata come proprietà 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.

Sono disponibili più versioni generiche degli oggetti InvokeAction<T> e ActivityAction<T> forniti per passare uno o più argomenti.

Uso di ActivityFunc

L'oggetto ActivityAction<T> è utile quando l'attività non restituisce alcun valore di risultato, mentre l'oggetto ActivityFunc<TResult> è usato quando viene restituito un valore di risultato. Quando si crea un'attività personalizzata che definisce un oggetto ActivityFunc<TResult>, usare un oggetto InvokeFunc<TResult> per modellare la chiamata di tale oggetto ActivityFunc<TResult>. Nell'esempio seguente viene definita un'attività WriteFillerText . Per fornire il testo di riempimento, viene specificato un oggetto InvokeFunc<TResult> che accetta un argomento Integer e dispone di un risultato di stringa. Una volta recuperato il testo di riempimento, viene visualizzato nella console tramite un'attività 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)
                }
            }
        };
    }
}

Per fornire il testo, è necessario usare un'attività che accetta un argomento int e dispone di un risultato di stringa. In questo esempio viene mostrata un'attività TextGenerator che soddisfa questi requisiti.

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

Per usare l'attività TextGenerator con l'attività WriteFillerText, specificarla come proprietà 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);