共用方式為


在 WF 裡製作非同步活動

AsyncCodeActivity 提供活動作者基類,讓衍生活動實作異步執行邏輯。 這適用於必須執行異步工作的自定義活動,而不需要保存工作流程排程器線程,並封鎖任何可能可以平行執行的活動。 本主題提供如何使用 建立自定義異步活動 AsyncCodeActivity的概觀。

使用 AsyncCodeActivity

System.Activities 針對不同的活動撰寫需求,提供具有不同基類的自定義活動作者。 每一個都有特定的語意,並提供工作流程作者(和活動運行時間)對應的合約。 基 AsyncCodeActivity 底活動是一個活動,會以異步方式執行相對於排程器線程的工作,且其執行邏輯是以Managed程式碼表示。 由於採用異步方式,可能會在 AsyncCodeActivity 的執行過程中產生閒置點。 由於異步工作的變動本質,AsyncCodeActivity 總是在活動執行期間建立一個不持久保存的區塊。 這可防止工作流程執行階段在異步工作中途持續化工作流程實例,也防止工作流程實例在執行異步程式碼時卸載。

AsyncCodeActivity 方法

衍生自 AsyncCodeActivity 的活動可以藉由使用自定義程式碼覆寫 BeginExecuteEndExecute 方法來建立異步執行邏輯。 由執行階段呼叫時,這些方法會傳遞 AsyncCodeActivityContextAsyncCodeActivityContext可讓活動作者在內容的UserState屬性中提供BeginExecute/ EndExecute的共享狀態。 在下列範例中, GenerateRandom 活動會以異步方式產生隨機數。

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

前一個範例活動衍生自 AsyncCodeActivity<TResult>,且具有提升的 OutArgument<int> 命名為 Result。 方法傳 GetRandom 回的值會擷 EndExecute 取並由覆寫傳回,此值會設定為 Result 值。 不傳回結果的異步活動應該衍生自 AsyncCodeActivity。 在下列範例中, DisplayRandom 定義衍生自 AsyncCodeActivity的活動。 此活動就像 GetRandom 活動,但它不是傳回結果,而是在主控台顯示訊息。

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

請注意,因為沒有傳回值, DisplayRandom 因此會使用 Action 而不是 Func<T,TResult> 叫用其委派,而委派不會傳回任何值。

AsyncCodeActivity 也提供 Cancel 覆寫。 雖然 BeginExecuteEndExecute 是必要的覆寫, Cancel 但為選擇性,而且可以覆寫,讓活動可以在取消或中止時清除其未處理的異步狀態。 如果可以清除 ,而且 AsyncCodeActivity.ExecutingActivityInstance.IsCancellationRequestedtrue,活動應該呼叫 MarkCanceled。 從這個方法拋出的任何例外對工作流程個體都是致命的。

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

在類別上叫用異步方法

.NET Framework 中的許多類別都提供異步功能,並可以使用基於 AsyncCodeActivity 的活動來異步調用這些功能。 在下列範例中,會建立活動,以異步方式使用 FileStream 類別建立檔案。

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

BeginExecute 和 EndExecute 方法之間的共享狀態

在上一個範例中,FileStream物件在BeginExecute中建立,並在EndExecute中存取。 這是可能的,因為 file 變數是在中的BeginExecute屬性中AsyncCodeActivityContext.UserState傳遞。 這是 BeginExecuteEndExecute 之間共享狀態的正確方法。 在衍生類別中使用成員變數(如本例中的FileWriter)在BeginExecuteEndExecute之間共享狀態是不正確的,因為活動物件可能會被多個活動實例引用。 嘗試使用成員變數來共享狀態,可能會導致一個 ActivityInstance 的值覆寫或取用另一個 ActivityInstance 的值。

存取自變數值

的環境 AsyncCodeActivity 是由活動上定義的自變數所組成。 您可以使用 參數,從 BeginExecute/EndExecute 覆寫 AsyncCodeActivityContext 存取這些自變數。 無法在委派中存取自變數,但自變數值或任何其他所需的數據都可以使用其參數傳入委派。 在下列範例中,會定義隨機產生數位的活動,以從其 Max 自變數取得內含上限。 叫用委派時,自變數的值會傳入異步程序代碼。

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

使用 AsyncCodeActivity 排程動作或子活動

AsyncCodeActivity 衍生的自定義活動提供方法,讓您在工作流程執行緒之外以非同步方式進行工作,但不提供排程子活動或動作的能力。 然而,可以透過組合安排子活動來實現異步行為。 您可以建立異步活動,然後與 ActivityNativeActivity 衍生活動組合,以實現子活動或動作的異步行為和排程。 例如,可以建立衍生自Activity的活動,其實作是利用Sequence,其中包含異步活動以及實作活動邏輯的其他活動。 如需使用 ActivityNativeActivity撰寫活動的更多範例,請參閱如何:建立活動和活動撰寫選項

另請參閱