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: {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 變數是在中的BeginExecute屬性中AsyncCodeActivityContext.UserState傳遞。 這是 BeginExecute 和 EndExecute 之間共享狀態的正確方法。 在衍生類別中使用成員變數(如本例中的FileWriter)在BeginExecute和EndExecute之間共享狀態是不正確的,因為活動物件可能會被多個活動實例引用。 嘗試使用成員變數來共享狀態,可能會導致一個 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 衍生的自定義活動提供方法,讓您在工作流程執行緒之外以非同步方式進行工作,但不提供排程子活動或動作的能力。 然而,可以透過組合安排子活動來實現異步行為。 您可以建立異步活動,然後與 Activity 或 NativeActivity 衍生活動組合,以實現子活動或動作的異步行為和排程。 例如,可以建立衍生自Activity的活動,其實作是利用Sequence,其中包含異步活動以及實作活動邏輯的其他活動。 如需使用 Activity 和 NativeActivity撰寫活動的更多範例,請參閱如何:建立活動和活動撰寫選項。