Condividi tramite


Creazione di attività asincrone in WF

AsyncCodeActivity fornisce agli autori di attività una classe base da usare che consente alle attività derivate di implementare la logica di esecuzione asincrona. Ciò è utile per le attività personalizzate che devono eseguire operazioni asincrone senza contenere il thread dell'utilità di pianificazione del flusso di lavoro e bloccare eventuali attività che possono essere eseguite in parallelo. In questo argomento viene fornita una panoramica di come creare attività asincrone personalizzate usando AsyncCodeActivity.

Uso di AsyncCodeActivity

System.Activities fornisce agli autori di attività personalizzate diverse classi di base per requisiti di creazione di attività diversi. Ognuno porta una particolare semantica e fornisce all'autore del flusso di lavoro (e al runtime di attività) il contratto corrispondente. Un'attività basata su AsyncCodeActivity è un'attività che esegue il lavoro in modo asincrono rispetto al thread del pianificatore e la cui logica di esecuzione è espressa in codice gestito. In seguito al passaggio all'asincrono, un AsyncCodeActivity può indurre un momento di inattività durante l'esecuzione. A causa della natura volatile del lavoro asincrono, un AsyncCodeActivity crea sempre un blocco non persistente per la durata dell'esecuzione dell'attività. Ciò impedisce al runtime del flusso di lavoro di persistere nel mezzo del lavoro asincrono e impedisce lo scaricamento dell'istanza del flusso di lavoro durante l'esecuzione del codice asincrono.

Metodi AsyncCodeActivity

Le attività che derivano da AsyncCodeActivity possono creare logica di esecuzione asincrona eseguendo l'override dei BeginExecute metodi e EndExecute con codice personalizzato. Quando vengono chiamati dal runtime, a questi metodi viene passato un AsyncCodeActivityContext. AsyncCodeActivityContext consente all'autore dell'attività di fornire uno stato condiviso per BeginExecute/ EndExecute nella proprietà del contesto UserState. Nell'esempio seguente un'attività GenerateRandom genera un numero casuale in modo asincrono.

public sealed class GenerateRandom : AsyncCodeActivity<int>
{
    static Random r = new Random();

    protected override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state)
    {
        // Create a delegate that references the method that implements
        // the asynchronous work. Assign the delegate to the UserState,
        // invoke the delegate, and return the resulting IAsyncResult.
        Func<int> GetRandomDelegate = new Func<int>(GetRandom);
        context.UserState = GetRandomDelegate;
        return GetRandomDelegate.BeginInvoke(callback, state);
    }

    protected override int EndExecute(AsyncCodeActivityContext context, IAsyncResult result)
    {
        // Get the delegate from the UserState and call EndInvoke
        Func<int> GetRandomDelegate = (Func<int>)context.UserState;
        return (int)GetRandomDelegate.EndInvoke(result);
    }

    int GetRandom()
    {
        // This activity simulates taking a few moments
        // to generate the random number. This code runs
        // asynchronously with respect to the workflow thread.
        Thread.Sleep(5000);

        return r.Next(1, 101);
    }
}

L'attività di esempio precedente deriva da AsyncCodeActivity<TResult>, e ha un OutArgument<int> elevato denominato Result. Il valore restituito dal GetRandom metodo viene estratto e restituito dall'override EndExecute e questo valore viene impostato come Result valore. Le attività asincrone che non restituiscono un risultato devono derivare da AsyncCodeActivity. Nell'esempio seguente viene definita un'attività DisplayRandom che deriva da AsyncCodeActivity. Questa attività è simile all'attività GetRandom , ma invece di restituire un risultato visualizza un messaggio alla console.

public sealed class DisplayRandom : AsyncCodeActivity
{
    static Random r = new Random();

    protected override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state)
    {
        // Create a delegate that references the method that implements
        // the asynchronous work. Assign the delegate to the UserState,
        // invoke the delegate, and return the resulting IAsyncResult.
        Action GetRandomDelegate = new Action(GetRandom);
        context.UserState = GetRandomDelegate;
        return GetRandomDelegate.BeginInvoke(callback, state);
    }

    protected override void EndExecute(AsyncCodeActivityContext context, IAsyncResult result)
    {
        // Get the delegate from the UserState and call EndInvoke
        Action GetRandomDelegate = (Action)context.UserState;
        GetRandomDelegate.EndInvoke(result);
    }

    void GetRandom()
    {
        // This activity simulates taking a few moments
        // to generate the random number. This code runs
        // asynchronously with respect to the workflow thread.
        Thread.Sleep(5000);

        Console.WriteLine($"Random Number: {r.Next(1, 101)}");
    }
}

Si noti che poiché non esiste alcun valore restituito, DisplayRandom utilizza un Action anziché un Func<T,TResult> per invocare il suo delegato, e il delegato non restituisce alcun valore.

AsyncCodeActivity fornisce anche un Cancel override. Mentre BeginExecute e EndExecute sono override obbligatori, Cancel è facoltativo e può essere sottoposto a override in modo che l'attività possa pulire lo stato asincrono in sospeso quando questa viene annullata o interrotta. Se la pulizia è possibile e AsyncCodeActivity.ExecutingActivityInstance.IsCancellationRequested è true, l'attività dovrebbe chiamare MarkCanceled. Tutte le eccezioni generate da questo metodo sono fatali per l'istanza del flusso di lavoro.

protected override void Cancel(AsyncCodeActivityContext context)
{
    // Implement any cleanup as a result of the asynchronous work
    // being canceled, and then call MarkCanceled.
    if (context.IsCancellationRequested)
    {
        context.MarkCanceled();
    }
}

Richiamo di metodi asincroni in una classe

Molte delle classi di .NET Framework forniscono funzionalità asincrone e questa funzionalità può essere richiamata in modo asincrono usando un'attività AsyncCodeActivity basata su . Nell'esempio seguente viene creata un'attività che crea in modo asincrono un file usando la FileStream classe .

public sealed class FileWriter : AsyncCodeActivity
{
    public FileWriter()
        : base()
    {
    }

    protected override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state)
    {
        string tempFileName = Path.GetTempFileName();
        Console.WriteLine("Writing to file: " + tempFileName);

        FileStream file = File.Open(tempFileName, FileMode.Create);

        context.UserState = file;

        byte[] bytes = UnicodeEncoding.Unicode.GetBytes("123456789");
        return file.BeginWrite(bytes, 0, bytes.Length, callback, state);
    }

    protected override void EndExecute(AsyncCodeActivityContext context, IAsyncResult result)
    {
        FileStream file = (FileStream)context.UserState;

        try
        {
            file.EndWrite(result);
            file.Flush();
        }
        finally
        {
            file.Close();
        }
    }
}

Condivisione dello stato tra i metodi BeginExecute e EndExecute

Nell'esempio precedente è stato eseguito l'accesso all'oggetto FileStream creato in BeginExecute in EndExecute. Ciò è possibile perché la file variabile è stata passata nella AsyncCodeActivityContext.UserState proprietà in BeginExecute. Si tratta del metodo corretto per la condivisione dello stato tra BeginExecute e EndExecute. Non è corretto usare una variabile membro nella classe derivata (FileWriter in questo caso) per condividere lo stato tra BeginExecute e EndExecute perché l'oggetto attività può fare riferimento a più istanze di attività. Il tentativo di usare una variabile membro per condividere lo stato può comportare la sovrascrittura o l'utilizzo di valori di un ActivityInstance da parte di un altro ActivityInstance oggetto.

Accesso ai valori degli argomenti

L'ambiente di un AsyncCodeActivity è composto dagli argomenti definiti nell'attività. È possibile accedere a questi argomenti dagli BeginExecute/EndExecute override usando il parametro AsyncCodeActivityContext. Non è possibile accedere agli argomenti nel delegato, ma i valori dell'argomento o qualsiasi altro dato desiderato possono essere passati al delegato usando i relativi parametri. Nell'esempio seguente viene definita un'attività di generazione di numeri casuali che ottiene il limite superiore inclusivo dal relativo Max argomento. Il valore dell'argomento viene passato al codice asincrono quando il delegato viene invocato.

public sealed class GenerateRandomMax : AsyncCodeActivity<int>
{
    public InArgument<int> Max { get; set; }

    static Random r = new Random();

    protected override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state)
    {
        // Create a delegate that references the method that implements
        // the asynchronous work. Assign the delegate to the UserState,
        // invoke the delegate, and return the resulting IAsyncResult.
        Func<int, int> GetRandomDelegate = new Func<int, int>(GetRandom);
        context.UserState = GetRandomDelegate;
        return GetRandomDelegate.BeginInvoke(Max.Get(context), callback, state);
    }

    protected override int EndExecute(AsyncCodeActivityContext context, IAsyncResult result)
    {
        // Get the delegate from the UserState and call EndInvoke
        Func<int, int> GetRandomDelegate = (Func<int, int>)context.UserState;
        return (int)GetRandomDelegate.EndInvoke(result);
    }

    int GetRandom(int max)
    {
        // This activity simulates taking a few moments
        // to generate the random number. This code runs
        // asynchronously with respect to the workflow thread.
        Thread.Sleep(5000);

        return r.Next(1, max + 1);
    }
}

Pianificazione di azioni o attività secondarie tramite AsyncCodeActivity

AsyncCodeActivity Le attività personalizzate derivate forniscono un metodo per eseguire il lavoro in modo asincrono per quanto riguarda il thread del flusso di lavoro, ma non consentono di pianificare attività o azioni figlio. Tuttavia, il comportamento asincrono può essere incorporato nella pianificazione delle attività figlie tramite la composizione. È possibile creare un'attività asincrona e quindi comporla con un'attività derivata Activity o NativeActivity per fornire un comportamento asincrono e la pianificazione delle attività figlio o delle azioni figlie. Ad esempio, è possibile creare un'attività che deriva da Activitye ha come implementazione un Sequence oggetto contenente l'attività asincrona e le altre attività che implementano la logica dell'attività. Per altri esempi di composizione di attività con Activity e NativeActivity, vedere Procedura: Creare un'attività e opzioni di creazione di attività.

Vedere anche