Tworzenie działań asynchronicznych w programie WF
AsyncCodeActivity Udostępnia autorom działań klasę bazową, która umożliwia wykonywanie działań pochodnych w celu zaimplementowania logiki wykonywania asynchronicznego. Jest to przydatne w przypadku działań niestandardowych, które muszą wykonywać asynchroniczną pracę bez przechowywania wątku harmonogramu przepływu pracy i blokowania wszelkich działań, które mogą być uruchamiane równolegle. Ten temat zawiera omówienie sposobu tworzenia niestandardowych działań asynchronicznych przy użyciu programu AsyncCodeActivity.
Korzystanie z funkcji AsyncCodeActivity
System.Activities Udostępnia autorom działań niestandardowych różne klasy bazowe dla różnych wymagań dotyczących tworzenia działań. Każdy z nich ma określony semantyczny i udostępnia autor przepływu pracy (i środowisko uruchomieniowe działania) odpowiadający mu kontrakt. Działanie AsyncCodeActivity oparte to działanie, które wykonuje pracę asynchronicznie względem wątku harmonogramu i którego logika wykonywania jest wyrażona w kodzie zarządzanym. W wyniku asynchronicznego AsyncCodeActivity może wywołać bezczynny punkt podczas wykonywania. Ze względu na nietrwały charakter pracy asynchronicznej, AsyncCodeActivity zawsze tworzy blok nietrwały na czas wykonywania działania. Zapobiega to utrwalaniu wystąpienia przepływu pracy w trakcie pracy asynchronicznej przez środowisko uruchomieniowe przepływu pracy, a także uniemożliwia zwalnianie wystąpienia przepływu pracy podczas wykonywania kodu asynchronicznego.
Metody AsyncCodeActivity
Działania pochodzące z AsyncCodeActivity programu mogą tworzyć logikę wykonywania asynchronicznego przez zastąpienie BeginExecute metod i EndExecute kodem niestandardowym. Po wywołaniu przez środowisko uruchomieniowe te metody są przekazywane jako AsyncCodeActivityContext. AsyncCodeActivityContextumożliwia autorowi działania udostępnianie stanu BeginExecute/ EndExecute we właściwości kontekstu.UserState W poniższym przykładzie GenerateRandom
działanie generuje asynchronicznie losową liczbę.
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);
}
}
Poprzednie przykładowe działanie pochodzi z AsyncCodeActivity<TResult>klasy i ma podwyższony poziom o OutArgument<int>
nazwie Result
. Wartość zwracana przez metodę GetRandom
jest wyodrębniona i zwracana przez EndExecute przesłonięcia, a ta wartość jest ustawiana jako Result
wartość. Działania asynchroniczne, które nie zwracają wyniku, powinny pochodzić z klasy AsyncCodeActivity. W poniższym przykładzie zdefiniowano DisplayRandom
działanie, które pochodzi z klasy AsyncCodeActivity. To działanie jest podobne do GetRandom
działania, ale zamiast zwracać wynik, wyświetla komunikat do konsoli.
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));
}
}
Należy pamiętać, że ponieważ nie ma wartości zwracanej, DisplayRandom
używa Action elementu zamiast , Func<T,TResult> aby wywołać jego delegata, a delegat nie zwraca żadnej wartości.
AsyncCodeActivity Zapewnia Cancel również przesłonięcia. Chociaż BeginExecute i EndExecute są wymagane przesłonięcia, Cancel jest opcjonalne i można je zastąpić, dzięki czemu działanie może wyczyścić jego zaległy stan asynchroniczny po anulowaniu lub przerwaniu. Jeśli czyszczenie jest możliwe i AsyncCodeActivity.ExecutingActivityInstance.IsCancellationRequested
ma true
wartość , działanie powinno wywołać metodę MarkCanceled. Wszelkie wyjątki zgłoszone przez tę metodę są krytyczne dla wystąpienia przepływu pracy.
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();
}
}
Wywoływanie metod asynchronicznych w klasie
Wiele klas w programie .NET Framework zapewnia funkcje asynchroniczne, a ta funkcja może być asynchronicznie wywoływana przy użyciu działania opartego AsyncCodeActivity . W poniższym przykładzie zostanie utworzone działanie, które asynchronicznie tworzy plik przy użyciu FileStream klasy .
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();
}
}
}
Udostępnianie stanu między metodami BeginExecute i EndExecute
W poprzednim przykładzie FileStream obiekt, BeginExecute w ramach którego utworzono obiekt, został wyświetlony w obiekcie EndExecute. Jest to możliwe, ponieważ zmienna file
została przekazana we AsyncCodeActivityContext.UserState właściwości w BeginExecutepliku . Jest to prawidłowa metoda udostępniania stanu między BeginExecute i EndExecute. Niepoprawne jest użycie zmiennej składowej w klasie pochodnej (FileWriter
w tym przypadku) do współużytkowania stanu między elementami BeginExecute i EndExecute dlatego, że obiekt działania może być przywołyny przez wiele wystąpień działań. Próba użycia zmiennej składowej do współużytkowania stanu może spowodować, że wartości z jednego ActivityInstance zastępowania lub zużywania wartości z innego ActivityInstanceelementu .
Uzyskiwanie dostępu do wartości argumentów
Środowisko obiektu AsyncCodeActivity składa się z argumentów zdefiniowanych w działaniu. Dostęp do tych argumentów można uzyskać z BeginExecute/EndExecute przesłonięć przy użyciu parametru .AsyncCodeActivityContext Nie można uzyskać dostępu do argumentów w delegatzie, ale wartości argumentów lub inne żądane dane można przekazać do delegata przy użyciu jego parametrów. W poniższym przykładzie zdefiniowano losowe działanie generowania liczb, które uzyskuje górną granicę inkluzywną z argumentu Max
. Wartość argumentu jest przekazywana do kodu asynchronicznego po wywołaniu delegata.
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);
}
}
Planowanie akcji lub działań podrzędnych przy użyciu funkcji AsyncCodeActivity
AsyncCodeActivity Pochodne działania niestandardowe zapewniają metodę wykonywania pracy asynchronicznie w odniesieniu do wątku przepływu pracy, ale nie zapewniają możliwości planowania działań podrzędnych ani akcji. Jednak zachowanie asynchroniczne może być włączone do planowania działań podrzędnych poprzez kompozycję. Można utworzyć działanie asynchroniczne, a następnie składać się z działania lub NativeActivity pochodnego Activity w celu zapewnienia asynchronicznego zachowania i planowania działań podrzędnych lub akcji. Na przykład można utworzyć działanie, które pochodzi z Activityelementu , i ma jako implementację Sequence zawierające działanie asynchroniczne, a także inne działania, które implementują logikę działania. Aby uzyskać więcej przykładów tworzenia działań przy użyciu poleceń Activity i , zobacz How to: Create an Activity and Activity Authoring Options (Instrukcje: tworzenie działań i opcje tworzenia działań).NativeActivity