Vytváření asynchronních aktivit ve WF
AsyncCodeActivity poskytuje autorům aktivit základní třídu, která umožňuje odvozené aktivity implementovat asynchronní logiku spouštění. To je užitečné pro vlastní aktivity, které musí provádět asynchronní práci, aniž by podrží vlákno plánovače pracovního postupu a blokovaly všechny aktivity, které by mohly být schopny běžet paralelně. Toto téma obsahuje přehled o tom, jak vytvořit vlastní asynchronní aktivity pomocí AsyncCodeActivity.
Použití AsyncCodeActivity
System.Activities poskytuje autorům vlastních aktivit různé základní třídy pro různé požadavky na vytváření aktivit. Každý z nich má konkrétní sémantickou sémantiku a poskytuje autor pracovního postupu (a modul runtime aktivity) odpovídající kontrakt. Založená AsyncCodeActivity aktivita je aktivita, která provádí práci asynchronně vzhledem k vláknu plánovače a jejíž prováděcí logika je vyjádřena ve spravovaném kódu. V důsledku asynchronního přechodu AsyncCodeActivity může během provádění vyvolat nečinný bod. Vzhledem k nestálé povaze asynchronní práce AsyncCodeActivity vždy vytvoří žádný trvalý blok po dobu trvání provádění aktivity. Tím zabráníte modulu runtime pracovního postupu zachovat instanci pracovního postupu uprostřed asynchronní práce a zároveň zabráníte uvolnění instance pracovního postupu při provádění asynchronního kódu.
Metody AsyncCodeActivity
Aktivity odvozené z AsyncCodeActivity můžou vytvořit logiku asynchronního spouštění přepsáním BeginExecute a EndExecute metodami pomocí vlastního kódu. Při zavolání modulem runtime jsou tyto metody předány .AsyncCodeActivityContext AsyncCodeActivityContextumožňuje autorovi aktivity poskytnout sdílený stav BeginExecute/ EndExecute ve vlastnosti kontextu.UserState V následujícím příkladu GenerateRandom
aktivita generuje náhodné číslo asynchronně.
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);
}
}
Předchozí ukázková aktivita je odvozena od AsyncCodeActivity<TResult>a má zvýšený OutArgument<int>
název Result
. Hodnota vrácená metodou GetRandom
je extrahována a vrácena přepsáním EndExecute a tato hodnota je nastavena Result
jako hodnota. Asynchronní aktivity, které nevrací výsledek, by měly být odvozeny z AsyncCodeActivity. V následujícím příkladu je definována DisplayRandom
aktivita, která je odvozena z AsyncCodeActivity. Tato aktivita se podobá aktivitě GetRandom
, ale místo vrácení výsledku se v konzole zobrazí zpráva.
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));
}
}
Všimněte si, že protože neexistuje žádná návratová hodnota, DisplayRandom
používá Action místo vyvolání Func<T,TResult> svého delegáta a delegát nevrátí žádnou hodnotu.
AsyncCodeActivity poskytuje také přepsání Cancel . I když BeginExecute a EndExecute jsou povinné přepsání, Cancel je volitelné a lze je přepsat, aby aktivita při zrušení nebo přerušení vyčistit svůj nevyrovnaný asynchronní stav. Pokud je vyčištění možné a AsyncCodeActivity.ExecutingActivityInstance.IsCancellationRequested
je true
, měla by aktivita volat MarkCanceled. Všechny výjimky vyvolané touto metodou jsou pro instanci pracovního postupu závažné.
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();
}
}
Vyvolání asynchronních metod ve třídě
Mnoho tříd v rozhraní .NET Framework poskytuje asynchronní funkce a tuto funkci lze asynchronně vyvolat pomocí AsyncCodeActivity založené aktivity. V následujícím příkladu se vytvoří aktivita, která asynchronně vytvoří soubor pomocí FileStream třídy.
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();
}
}
}
Sdílení stavu mezi metodami BeginExecute a EndExecute
V předchozím příkladu FileStream byl objekt, který byl vytvořen v BeginExecute souboru EndExecute. To je možné, protože file
proměnná byla předána ve AsyncCodeActivityContext.UserState vlastnosti v BeginExecute. Toto je správná metoda pro sdílení stavu mezi BeginExecute a EndExecute. Není správné použít členovou proměnnou v odvozené třídě (FileWriter
v tomto případě) ke sdílení stavu mezi BeginExecute a EndExecute protože objekt aktivity může být odkazován více instancemi aktivity. Pokus o použití členské proměnné ke sdílení stavu může vést k hodnotám z jednoho ActivityInstance přepsání nebo využívání hodnot z jiného ActivityInstance.
Přístup k hodnotám argumentů
Prostředí AsyncCodeActivity se skládá z argumentů definovaných v aktivitě. K těmto argumentům lze přistupovat z BeginExecute/EndExecute přepsání pomocí parametru.AsyncCodeActivityContext Argumenty nelze v delegátu získat přístup, ale hodnoty argumentů nebo jakákoli jiná požadovaná data lze delegátu předat pomocí jeho parametrů. V následujícím příkladu je definována náhodná aktivita generování čísel, která z argumentu Max
získá inkluzivní horní mez. Hodnota argumentu se předá asynchronnímu kódu při vyvolání delegáta.
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);
}
}
Plánování akcí nebo podřízených aktivit pomocí AsyncCodeActivity
AsyncCodeActivity odvozené vlastní aktivity poskytují metodu pro asynchronní provádění práce s ohledem na vlákno pracovního postupu, ale neposkytuje možnost plánovat podřízené aktivity nebo akce. Asynchronní chování je však možné začlenit do plánování podřízených aktivit prostřednictvím složení. Asynchronní aktivitu lze vytvořit a pak se skládat s nebo NativeActivity odvozenou Activity aktivitou, která poskytuje asynchronní chování a plánování podřízených aktivit nebo akcí. Například lze vytvořit aktivitu, která je odvozena z Activity, a má jako její implementaci Sequence obsahující asynchronní aktivitu a další aktivity, které implementují logiku aktivity. Další příklady psaní aktivit pomocí Activity a , viz Postupy: Vytvoření aktivity a možnosti vytváření aktivit.NativeActivity