Freigeben über


Erstellen von asynchronen Aktivitäten in WF

Das AsyncCodeActivity-Objekt stellt eine Basisklasse für Aktivitätsautoren bereit, mit der abgeleitete Aktivitäten asynchrone Ausführungslogik implementieren können. Dies ist nützlich bei benutzerdefinierten Aktivitäten, die asynchrone Aufgaben ausführen müssen, ohne dass der Workflowplanerthread angehalten und Aktivitäten, die parallel ausgeführt werden, bockiert werden. Dieses Thema enthält eine Übersicht zum Erstellen von benutzerdefinierten asynchronen Aktivitäten mit AsyncCodeActivity.

Verwenden von AsyncCodeActivity

System.Activities bietet benutzerdefinierten Aktivitätsautoren verschiedene Basisklassen, mit denen auf die unterschiedlichen Anforderungen der Aktivitätserstellung eingegangen wird. Jede einzelne davon weist eine bestimmte Semantik auf und stellt einem Workflowautor (und der Aktivitätslaufzeit) einen entsprechenden Vertrag bereit. Ein Aktivität auf Grundlage des AsyncCodeActivity-Objekts ist eine Aktivität, die Aufgaben asynchron in Verbindung mit dem Planerthread ausführt und deren Ausführungslogik in verwaltetem Code ausgedrückt wird. Als Ergebnis der Asynchronität kann ein AsyncCodeActivity-Objekt möglicherweise während der Ausführung einen Leerlaufpunkt herbeiführen. Aufgrund der flüchtigen Art asynchroner Arbeit erstellt ein AsyncCodeActivity-Objekt immer einen nicht persistenten Block für die Dauer der Ausführung der Aktivität. Dadurch wird verhindert, dass die Workflowlaufzeit die Workflowinstanz während der asynchronen Arbeit speichert oder die Workflowinstanz während der Ausführung von asynchronem Code entladen wird.

AsyncCodeActivity-Methoden

Aktivitäten, die vom AsyncCodeActivity-Objekt ableiten, können asynchrone Ausführungslogik erstellen, indem sie die BeginExecute-Methode und die EndExecute-Methode mit benutzerdefiniertem Code überschreiben. Wenn diese Methoden von der Laufzeit aufgerufen werden, wird AsyncCodeActivityContext an sie übergeben. Mit AsyncCodeActivityContext kann der Autor der Aktivität den gemeinsam verwendeten Zustand übergreifend über BeginExecute/ EndExecute in der UserState-Eigenschaft des Kontexts bereitstellen. Im folgenden Beispiel generiert eine GenerateRandom-Aktivität eine Zufallszahl auf asynchrone Weise.

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

Die vorangehende Beispielaktivität wird von AsyncCodeActivity<TResult> abgeleitet und verfügt über ein erhöhtes OutArgument<int> mit dem Namen Result. Der von der GetRandom-Methode zurückgegebene Wert wird extrahiert und von der EndExecute-Überschreibung zurückgegeben. Dieser Wert wird als Result-Wert festgelegt. Asynchrone Aktivitäten, die kein Ergebnis zurückgeben, sollten von AsyncCodeActivity abgeleitet werden. Im folgenden Beispiel wird eine DisplayRandom-Aktivität definiert, die von AsyncCodeActivity abgeleitet wird. Diese Aktivität entspricht der GetRandom-Aktivität, aber statt ein Ergebnis zurückzugeben, wird eine Meldung in der Konsole angezeigt.

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: {0}", r.Next(1, 101));
    }
}

Da es keinen Rückgabewert gibt, verwendet DisplayRandom zum Aufrufen des zugehörigen Delegaten ein Action-Element statt eines Func<T,TResult>-Elements, und der Delegat gibt keinen Wert zurück.

Die AsyncCodeActivity stellt außerdem eine Cancel-Überschreibung bereit. Während BeginExecute und EndExecute erforderliche Überschreibungen darstellen, ist Cancel optional und kann überschrieben werden, damit die Aktivität ihren ausstehenden asynchronen Zustand bereinigen kann, wenn sie abgebrochen wird. Wenn eine Bereinigung möglich ist und AsyncCodeActivity.ExecutingActivityInstance.IsCancellationRequested auf true festgelegt ist, sollte die Aktivität MarkCanceled aufrufen. Alle von dieser Methode ausgelösten Ausnahmen wirken sich fatal auf die Workflowinstanz aus.

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

Aufrufen von asynchronen Methoden für eine Klasse

Viele der Klassen in .NET Framework stellen asynchrone Funktionalität bereit, und diese Funktionalität kann asynchron mit einer AsyncCodeActivity-basierten Aktivität aufgerufen werden. Im folgenden Beispiel wird eine Aktivität erstellt, die mit der FileStream-Klasse asynchron eine Datei erstellt.

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

Gemeinsame Zustandsverwendung durch die BeginExecute-Methode und die EndExecute-Methode

Im vorherigen Beispiel wurde auf das FileStream-Objekt, das in BeginExecute erstellt wurde, in EndExecute zugegriffen. Dies ist möglich, weil die file-Variable in der AsyncCodeActivityContext.UserState-Eigenschaft in BeginExecute übergeben wurde. Dies ist die richtige Methode zum gemeinsamen Verwenden des Zustands zwischen BeginExecute und EndExecute. Es ist unzulässig, eine Membervariable in der abgeleiteten Klasse (in diesem Fall FileWriter) zu verwenden, um einen gemeinsamen Zustand für BeginExecute und EndExecute zu verwenden, da auf das Aktivitätsobjekt von mehreren Aktivitätsinstanzen verwiesen werden kann. Der Versuch, eine Membervariable zu verwenden, um einen Zustand gemeinsam zu verwenden, kann dazu führen, dass Werte einer ActivityInstance Werte einer anderen ActivityInstance überschreiben oder nutzen.

Zugreifen auf Argumentwerte

Die Umgebung eines AsyncCodeActivity-Objekts besteht aus den Argumenten, die für die Aktivität definiert sind. Der Zugriff auf diese Argumente erfolgt über die BeginExecute/EndExecute-Überschreibungen mit dem AsyncCodeActivityContext-Parameter. Im Delegaten kann nicht auf die Argumente zugegriffen werden. Die Argumentwerte oder andere gewünschte Daten können jedoch mithilfe seiner Parameter an den Delegaten übergeben werden. Im folgenden Beispiel wird eine Aktivität definiert, die Zufallszahlen generiert. Die Aktivität ruft ihre inklusive obere Begrenzung aus ihrem Max-Argument ab. Der Wert des Arguments wird an den asynchronen Code übergeben, sobald der Delegat aufgerufen wird.

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

Planen von Aktionen oder untergeordneten Aktivitäten mithilfe von AsyncCodeActivity

Von AsyncCodeActivity abgeleitete benutzerdefinierte Aktivitäten ermöglichen das asynchrone Arbeiten am Workflowthread, bieten aber keine Möglichkeit, untergeordnete Aktivitäten oder Aktionen zu planen. Das asynchrone Verhalten kann jedoch integriert werden, indem untergeordnete Aktivitäten bei der Zusammensetzung geplant werden. Eine asynchrone Aktivität kann erstellt und dann mit einer abgeleiteten Activity-Aktivität oder NativeActivity-Aktivität zusammengestellt werden, um Unterstützung für asynchrones Verhalten und die Planung untergeordneter Aktivitäten oder Aktionen bereitzustellen. Beispielsweise könnte eine Aktivität erstellt werden, die von Activity abgeleitet ist und als Implementierung über Sequence verfügt, die asynchrone Aktivität sowie die anderen Aktivitäten enthält, die die Logik der Aktivität implementieren. Weitere Beispiele für das Verfassen von Aktivitäten mit Activity und NativeActivity finden Sie unter Vorgehensweise: Erstellen einer Aktivität und Aktivitätserstellungsoptionen.

Weitere Informationen