在 WF 中建立非同步活動
AsyncCodeActivity 會提供活動作者可用的基底類別,此類別可讓衍生活動實作非同步執行邏輯。 若自訂活動必須執行非同步工作而不保存工作流程排程器執行緒,並且封鎖任何能夠平行執行的活動,則此功能非常實用。 本主題提供如何使用 AsyncCodeActivity 建立自訂非同步活動的概觀。
使用 AsyncCodeActivity
System.Activities 會提供自訂活動作者不同的基底類別,用於不同的活動撰寫需求。 每個基底類別具有一種特定的語意,並且提供工作流程作者 (及活動執行階段) 對應的合約。 以 AsyncCodeActivity 為主的活動會採用非同步方式來對排程器執行緒執行相對工作,且以 Managed 程式碼表示其執行邏輯的活動。 因為非同步的緣故,AsyncCodeActivity 可能會在執行期間造成閒置點。 由於非同步工作的變動特性,AsyncCodeActivity 一定會在活動執行期間建立不保存的區塊。 這樣可以避免工作流程執行階段在非同步工作當中保存工作流程執行個體,同時也避免工作流程執行個體在非同步程式碼執行期間卸載。
AsyncCodeActivity 方法
衍生自 AsyncCodeActivity 的活動可以透過以自訂程式碼覆寫 BeginExecute 和 EndExecute 方法來建立非同步執行邏輯。 執行階段呼叫這些方法時,會將這些方法傳遞到 AsyncCodeActivityContext。 AsyncCodeActivityContext 允許活動作者在內容的 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: {0}", r.Next(1, 101));
}
}
請注意,因為沒有傳回值,DisplayRandom
使用 Action 而非 Func<T,TResult> 叫用其委派,而委派未傳回值。
AsyncCodeActivity 也提供 Cancel 覆寫。 當 BeginExecute 和 EndExecute 是必要的覆寫時,Cancel 是選擇性的,而且可加以覆寫,讓活動能夠在取消或中止時清除未完成的非同步狀態。 如果可以清除,且 AsyncCodeActivity.ExecutingActivityInstance.IsCancellationRequested
是 true
,活動應呼叫 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
變數會在 AsyncCodeActivityContext.UserState 的 BeginExecute 屬性中傳遞。 這是在 BeginExecute 和 EndExecute 之間共用狀態的正確方法。 在衍生類別 (此範例中為 FileWriter
) 中使用成員變數來共用 BeginExecute 和 EndExecute 之間的狀態並不正確,因為可能會有多個活動執行個體參考該活動物件。 嘗試使用成員變數來共用狀態,可能會導致其中一個 ActivityInstance 的值覆寫或耗用另一個 ActivityInstance 的值。
存取引數值
AsyncCodeActivity 的環境包含在活動定義的引數。 您可以使用 AsyncCodeActivityContext 參數,從 BeginExecute/EndExecute 覆寫存取這些引數。 您不可以在委派中存取引數,但可以使用其參數將引數值或任何其他所需的資料傳遞至委派。 在下列範例中,會定義產生隨機數字的活動,此活動會從其 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 衍生的自訂活動提供根據工作流程執行緒非同步執行工作的方法,但不提供排定子活動或動作的功能。 但是,可以透過撰寫方式將非同步行為納入子活動排程。 您可以建立非同步活動,然後以 Activity 或 NativeActivity 衍生活動來撰寫該活動,以提供非同步行為,並排定子活動或動作。 例如,您可以建立衍生自 Activity 的活動,並且實作包含該非同步活動及其他實作活動邏輯之活動的 Sequence。 如需使用 Activity 和 NativeActivity 撰寫活動的更多範例,請參閱如何:建立活動和活動撰寫選項。