Uwaga
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Program Windows Workflow Foundation (WF) udostępnia kilka metod hostowania przepływów pracy. WorkflowInvoker zapewnia prosty sposób wywoływania przepływu pracy tak, jakby to było wywołanie metody i może być używany tylko w przypadku przepływów pracy, które nie używają trwałości. WorkflowApplication udostępnia bogatszy model wykonywania przepływów pracy, który obejmuje powiadomienia o zdarzeniach cyklu życia, zarządzanie wykonaniem, wznowienie zakładek oraz przechowywanie. WorkflowServiceHost zapewnia obsługę działań związanych z obsługą komunikatów i jest używana głównie z usługami przepływu pracy. W tym temacie przedstawiono hosting przepływu pracy wraz z WorkflowInvoker i WorkflowApplication. Aby uzyskać więcej informacji na temat hostowania przepływów pracy za pomocą WorkflowServiceHost, zobacz Usługi przepływu pracy i Przegląd hostowania usług przepływu pracy .
Korzystanie z elementu WorkflowInvoker
WorkflowInvoker udostępnia model wykonywania przepływu pracy, jakby było to wywołanie metody. Aby wywołać przepływ pracy przy użyciu WorkflowInvoker, wywołaj metodę Invoke i przekaż definicję przepływu pracy do wywołania. W tym przykładzie wywoływane jest działanie WriteLine przy użyciu WorkflowInvoker.
Activity wf = new WriteLine
{
Text = "Hello World."
};
WorkflowInvoker.Invoke(wf);
Gdy przepływ pracy jest wywoływany przy użyciu WorkflowInvoker, wykonywanie przepływu pracy odbywa się na wątku wywołującym, a metoda Invoke pozostaje zablokowana aż do zakończenia przepływu pracy, włącznie z czasem bezczynności. Aby skonfigurować przedział czasu, w którym przepływ pracy musi zostać ukończony, użyj jednego z przeciążeń Invoke, które przyjmuje parametr TimeSpan. W tym przykładzie przepływ pracy jest wywoływany dwa razy z dwoma różnymi interwałami limitu czasu. Pierwszy przepływ pracy kończy się, ale drugi nie.
Activity wf = new Sequence()
{
Activities =
{
new WriteLine()
{
Text = "Before the 1 minute delay."
},
new Delay()
{
Duration = TimeSpan.FromMinutes(1)
},
new WriteLine()
{
Text = "After the 1 minute delay."
}
}
};
// This workflow completes successfully.
WorkflowInvoker.Invoke(wf, TimeSpan.FromMinutes(2));
// This workflow does not complete and a TimeoutException
// is thrown.
try
{
WorkflowInvoker.Invoke(wf, TimeSpan.FromSeconds(30));
}
catch (TimeoutException ex)
{
Console.WriteLine(ex.Message);
}
Uwaga
TimeoutException jest zgłaszany tylko wtedy, gdy interwał limitu czasu upływa, a przepływ pracy jest bezczynny podczas wykonywania. Przepływ pracy, który trwa dłużej niż określony interwał przekroczenia limitu czasu, zostanie ukończony pomyślnie, jeśli przepływ pracy nie stanie się bezczynny.
WorkflowInvoker również udostępnia asynchroniczne wersje metody invoke. Aby uzyskać więcej informacji, zobacz InvokeAsync oraz BeginInvoke.
Ustawianie argumentów wejściowych przepływu pracy
Dane mogą być przekazywane do przepływu pracy przy użyciu słownika parametrów wejściowych, gdzie kluczami są nazwy argumentów, które mapują się na argumenty wejściowe przepływu pracy. W tym przykładzie wywoływana jest WriteLine, a wartość argumentu Text jest określana przy użyciu słownika parametrów wejściowych.
Activity wf = new WriteLine();
Dictionary<string, object> inputs = new Dictionary<string, object>();
inputs.Add("Text", "Hello World.");
WorkflowInvoker.Invoke(wf, inputs);
Pobieranie argumentów wyjściowych przepływu pracy
Parametry wyjściowe przepływu pracy można uzyskać poprzez słownik 'outputs' zwracany z wywołania do Invoke. Poniższy przykład wywołuje przepływ pracy składający się z jednego działania Divide
, który ma dwa argumenty wejściowe i dwa argumenty wyjściowe. Po wywołaniu przebiegu pracy przekazywany jest słownik arguments
, który zawiera wartości dla każdego argumentu wejściowego, gdzie kluczami są nazwy argumentów. Gdy wywołanie funkcji Invoke
zakończy się, każdy argument wyjściowy jest zwracany w słowniku outputs
, gdzie kluczem jest nazwa argumentu.
public sealed class Divide : CodeActivity
{
[RequiredArgument]
public InArgument<int> Dividend { get; set; }
[RequiredArgument]
public InArgument<int> Divisor { get; set; }
public OutArgument<int> Remainder { get; set; }
public OutArgument<int> Result { get; set; }
protected override void Execute(CodeActivityContext context)
{
int quotient = Dividend.Get(context) / Divisor.Get(context);
int remainder = Dividend.Get(context) % Divisor.Get(context);
Result.Set(context, quotient);
Remainder.Set(context, remainder);
}
}
int dividend = 500;
int divisor = 36;
Dictionary<string, object> arguments = new Dictionary<string, object>();
arguments.Add("Dividend", dividend);
arguments.Add("Divisor", divisor);
IDictionary<string, object> outputs =
WorkflowInvoker.Invoke(new Divide(), arguments);
Console.WriteLine($"{dividend} / {divisor} = {outputs["Result"]} Remainder {outputs["Remainder"]}");
W przypadku, gdy przepływ pracy pochodzi z ActivityWithResult, np. CodeActivity<TResult>
lub Activity<TResult>
, oraz istnieją argumenty wyjściowe oprócz dobrze zdefiniowanego argumentu wyjściowego Result, należy użyć niegenerycznego przeciążenia Invoke
, aby pobrać dodatkowe argumenty. W tym celu definicja przepływu pracy przekazana do Invoke
musi być typu Activity. W tym przykładzie działanie Divide
pochodzi z CodeActivity<int>
, ale jest deklarowane jako Activity, aby użyć przeciążenia specyficznego dla danego przypadku Invoke
, które zwraca słownik argumentów zamiast pojedynczej zwracanej wartości.
public sealed class Divide : CodeActivity<int>
{
public InArgument<int> Dividend { get; set; }
public InArgument<int> Divisor { get; set; }
public OutArgument<int> Remainder { get; set; }
protected override int Execute(CodeActivityContext context)
{
int quotient = Dividend.Get(context) / Divisor.Get(context);
int remainder = Dividend.Get(context) % Divisor.Get(context);
Remainder.Set(context, remainder);
return quotient;
}
}
int dividend = 500;
int divisor = 36;
Dictionary<string, object> arguments = new Dictionary<string, object>();
arguments.Add("Dividend", dividend);
arguments.Add("Divisor", divisor);
Activity wf = new Divide();
IDictionary<string, object> outputs =
WorkflowInvoker.Invoke(wf, arguments);
Console.WriteLine($"{dividend} / {divisor} = {outputs["Result"]} Remainder {outputs["Remainder"]}");
Korzystanie z funkcji WorkflowApplication
WorkflowApplication udostępnia bogaty zestaw funkcji do zarządzania wystąpieniami przepływu pracy. WorkflowApplication działa jako proxy bezpieczne dla wątków do rzeczywistego WorkflowInstance, który hermetyzuje środowisko uruchomieniowe i udostępnia metody tworzenia i ładowania wystąpień przepływu pracy, wstrzymywania i wznawiania, kończenia i powiadamiania o zdarzeniach cyklu życia. Aby uruchomić przepływ pracy przy użyciu WorkflowApplication, najpierw utwórz WorkflowApplication, następnie zasubskrybuj wybrane zdarzenia cyklu życia, uruchom przepływ pracy i na koniec poczekaj na jego zakończenie. W tym przykładzie tworzona jest definicja przepływu pracy składająca się z działania WriteLine, a WorkflowApplication jest tworzona przy użyciu określonej definicji przepływu pracy. Completed jest zarządzana, dzięki czemu host jest powiadamiany o zakończeniu przepływu pracy. Proces rozpoczyna się wywołaniem funkcji Run, po czym host czeka na jego zakończenie. Po zakończeniu przepływu pracy zostanie ustawiony AutoResetEvent, a aplikacja główna może wznowić wykonywanie, jak pokazano w poniższym przykładzie.
AutoResetEvent syncEvent = new AutoResetEvent(false);
Activity wf = new WriteLine
{
Text = "Hello World."
};
// Create the WorkflowApplication using the desired
// workflow definition.
WorkflowApplication wfApp = new WorkflowApplication(wf);
// Handle the desired lifecycle events.
wfApp.Completed = delegate (WorkflowApplicationCompletedEventArgs e)
{
syncEvent.Set();
};
// Start the workflow.
wfApp.Run();
// Wait for Completed to arrive and signal that
// the workflow is complete.
syncEvent.WaitOne();
Zdarzenia cyklu życia aplikacji workflow
Oprócz Completedautorzy hostów mogą być powiadamiani, gdy przepływ pracy zostanie rozładowany (Unloaded), przerwany (Aborted), stanie się nieaktywny (Idle i PersistableIdle) lub wystąpi nieobsługiwany wyjątek (OnUnhandledException). Deweloperzy aplikacji przepływu pracy mogą obsługiwać te powiadomienia i podejmować odpowiednie działania, jak pokazano w poniższym przykładzie.
wfApp.Completed = delegate (WorkflowApplicationCompletedEventArgs e)
{
if (e.CompletionState == ActivityInstanceState.Faulted)
{
Console.WriteLine($"Workflow {e.InstanceId} Terminated.");
Console.WriteLine($"Exception: {e.TerminationException.GetType().FullName}\n{e.TerminationException.Message}");
}
else if (e.CompletionState == ActivityInstanceState.Canceled)
{
Console.WriteLine($"Workflow {e.InstanceId} Canceled.");
}
else
{
Console.WriteLine($"Workflow {e.InstanceId} Completed.");
// Outputs can be retrieved from the Outputs dictionary,
// keyed by argument name.
// Console.WriteLine($"The winner is {e.Outputs["Winner"]}.");
}
};
wfApp.Aborted = delegate (WorkflowApplicationAbortedEventArgs e)
{
// Display the exception that caused the workflow
// to abort.
Console.WriteLine($"Workflow {e.InstanceId} Aborted.");
Console.WriteLine($"Exception: {e.Reason.GetType().FullName}\n{e.Reason.Message}");
};
wfApp.Idle = delegate (WorkflowApplicationIdleEventArgs e)
{
// Perform any processing that should occur
// when a workflow goes idle. If the workflow can persist,
// both Idle and PersistableIdle are called in that order.
Console.WriteLine($"Workflow {e.InstanceId} Idle.");
};
wfApp.PersistableIdle = delegate (WorkflowApplicationIdleEventArgs e)
{
// Instruct the runtime to persist and unload the workflow.
// Choices are None, Persist, and Unload.
return PersistableIdleAction.Unload;
};
wfApp.Unloaded = delegate (WorkflowApplicationEventArgs e)
{
Console.WriteLine($"Workflow {e.InstanceId} Unloaded.");
};
wfApp.OnUnhandledException = delegate (WorkflowApplicationUnhandledExceptionEventArgs e)
{
// Display the unhandled exception.
Console.WriteLine($"OnUnhandledException in Workflow {e.InstanceId}\n{e.UnhandledException.Message}");
Console.WriteLine($"ExceptionSource: {e.ExceptionSource.DisplayName} - {e.ExceptionSourceInstanceId}");
// Instruct the runtime to terminate the workflow.
// Other choices are Abort and Cancel. Terminate
// is the default if no OnUnhandledException handler
// is present.
return UnhandledExceptionAction.Terminate;
};
Ustawianie argumentów wejściowych przepływu pracy
Dane mogą być przekazywane do workflowu w momencie jego uruchamiania przy użyciu słownika parametrów, podobnie jak w przypadku używania WorkflowInvoker. Każdy element w słowniku jest przypisany do argumentu wejściowego określonego procesu roboczego. W tym przykładzie wywoływany jest przepływ pracy składający się z działania WriteLine, a jego Text argument jest określany przy użyciu słownika parametrów wejściowych.
AutoResetEvent syncEvent = new AutoResetEvent(false);
Activity wf = new WriteLine();
// Create the dictionary of input parameters.
Dictionary<string, object> inputs = new Dictionary<string, object>();
inputs.Add("Text", "Hello World!");
// Create the WorkflowApplication using the desired
// workflow definition and dictionary of input parameters.
WorkflowApplication wfApp = new WorkflowApplication(wf, inputs);
// Handle the desired lifecycle events.
wfApp.Completed = delegate (WorkflowApplicationCompletedEventArgs e)
{
syncEvent.Set();
};
// Start the workflow.
wfApp.Run();
// Wait for Completed to arrive and signal that
// the workflow is complete.
syncEvent.WaitOne();
Pobieranie argumentów wyjściowych przepływu pracy
Po zakończeniu procesu wszystkie argumenty wyjściowe można pobrać w obsłudze Completed, uzyskując dostęp do słownika WorkflowApplicationCompletedEventArgs.Outputs. Poniższy przykład hostuje przepływ pracy przy użyciu WorkflowApplication. Wystąpienie WorkflowApplication jest tworzone przy użyciu definicji przepływu pracy składającej się z jednego działania DiceRoll
. Działanie DiceRoll
ma dwa argumenty wyjściowe reprezentujące wyniki operacji rzutu kostką. Po zakończeniu przepływu pracy dane wyjściowe są pobierane w programie obsługi Completed.
public sealed class DiceRoll : CodeActivity
{
public OutArgument<int> D1 { get; set; }
public OutArgument<int> D2 { get; set; }
static Random r = new Random();
protected override void Execute(CodeActivityContext context)
{
D1.Set(context, r.Next(1, 7));
D2.Set(context, r.Next(1, 7));
}
}
// Create a WorkflowApplication instance.
WorkflowApplication wfApp = new WorkflowApplication(new DiceRoll());
// Subscribe to any desired workflow lifecycle events.
wfApp.Completed = delegate (WorkflowApplicationCompletedEventArgs e)
{
if (e.CompletionState == ActivityInstanceState.Faulted)
{
Console.WriteLine($"Workflow {e.InstanceId} Terminated.");
Console.WriteLine($"Exception: {e.TerminationException.GetType().FullName}\n{e.TerminationException.Message}");
}
else if (e.CompletionState == ActivityInstanceState.Canceled)
{
Console.WriteLine($"Workflow {e.InstanceId} Canceled.");
}
else
{
Console.WriteLine($"Workflow {e.InstanceId} Completed.");
// Outputs can be retrieved from the Outputs dictionary,
// keyed by argument name.
Console.WriteLine($"The two dice are {e.Outputs["D1"]} and {e.Outputs["D2"]}.");
}
};
// Run the workflow.
wfApp.Run();
Uwaga
WorkflowApplication i WorkflowInvoker przyjmują słownik argumentów wejściowych i zwracają słownik argumentów out
. Te parametry, właściwości i wartości zwracane słownika są typu IDictionary<string, object>
. Rzeczywiste wystąpienie klasy słownika, która jest przekazywana, może być dowolną klasą implementowaną IDictionary<string, object>
. W tych przykładach używany jest Dictionary<string, object>
. Aby uzyskać więcej informacji na temat słowników, zobacz IDictionary<TKey,TValue> i Dictionary<TKey,TValue>.
Przekazywanie danych do uruchomionego przepływu pracy przy użyciu zakładek
Zakładki to mechanizm, za pomocą którego czynność może pasywnie czekać na wznowienie oraz stanowią mechanizm przekazywania danych do uruchomionego wystąpienia przepływu pracy. Jeśli działanie oczekuje na dane, może utworzyć Bookmark i zarejestrować metodę wywołania zwrotnego, która ma zostać wywołana po wznowieniu Bookmark, jak pokazano w poniższym przykładzie.
public sealed class ReadLine : NativeActivity<string>
{
[RequiredArgument]
public InArgument<string> BookmarkName { get; set; }
protected override void Execute(NativeActivityContext context)
{
// Create a Bookmark and wait for it to be resumed.
context.CreateBookmark(BookmarkName.Get(context),
new BookmarkCallback(OnResumeBookmark));
}
// NativeActivity derived activities that do asynchronous operations by calling
// one of the CreateBookmark overloads defined on System.Activities.NativeActivityContext
// must override the CanInduceIdle property and return true.
protected override bool CanInduceIdle
{
get { return true; }
}
public void OnResumeBookmark(NativeActivityContext context, Bookmark bookmark, object obj)
{
// When the Bookmark is resumed, assign its value to
// the Result argument.
Result.Set(context, (string)obj);
}
Gdy działanie ReadLine
zostanie wykonane, tworzy Bookmark, rejestruje wywołanie zwrotne, a następnie czeka na wznowienie Bookmark. Po wznowieniu działanie ReadLine
przypisuje dane przekazane za pomocą Bookmark do argumentu Result. W tym przykładzie tworzony jest przepływ pracy, który używa działania ReadLine
do zbierania nazwy użytkownika i wyświetlania go w oknie konsoli.
Variable<string> name = new Variable<string>();
Activity wf = new Sequence
{
Variables = { name },
Activities =
{
new WriteLine
{
Text = "What is your name?"
},
new ReadLine
{
BookmarkName = "UserName",
Result = new OutArgument<string>(name)
},
new WriteLine
{
Text = new InArgument<string>((env) =>
("Hello, " + name.Get(env)))
}
}
};
// Create a WorkflowApplication instance.
WorkflowApplication wfApp = new WorkflowApplication(wf);
// Workflow lifecycle events omitted except idle.
AutoResetEvent idleEvent = new AutoResetEvent(false);
wfApp.Idle = delegate (WorkflowApplicationIdleEventArgs e)
{
idleEvent.Set();
};
// Run the workflow.
wfApp.Run();
// Wait for the workflow to go idle before gathering
// the user's input.
idleEvent.WaitOne();
// Gather the user's input and resume the bookmark.
// Bookmark resumption only occurs when the workflow
// is idle. If a call to ResumeBookmark is made and the workflow
// is not idle, ResumeBookmark blocks until the workflow becomes
// idle before resuming the bookmark.
BookmarkResumptionResult result = wfApp.ResumeBookmark("UserName",
Console.ReadLine());
// Possible BookmarkResumptionResult values:
// Success, NotFound, or NotReady
Console.WriteLine($"BookmarkResumptionResult: {result}");
Po wykonaniu działania ReadLine
tworzy Bookmark o nazwie UserName
, a następnie czeka na wznowienie zakładki. Host zbiera żądane dane, a następnie wznawia Bookmark. Przepływ pracy zostanie wznowiony, wyświetli nazwę, a następnie się zakończy.
Aplikacja hosta może sprawdzić przepływ pracy, aby określić, czy istnieją jakieś aktywne zakładki. Może to zrobić, wywołując metodę GetBookmarks instancji WorkflowApplication lub sprawdzając WorkflowApplicationIdleEventArgs w obsłudze Idle.
Poniższy przykład kodu jest podobny do poprzedniego przykładu, z tą różnicą, że aktywne zakładki są wyliczane przed wznowieniu zakładki. Przepływ pracy zostaje uruchomiony, a po utworzeniu Bookmark i gdy przepływ pracy stanie się bezczynny, wywoływane jest GetBookmarks. Po zakończeniu przepływu pracy następujące dane wyjściowe zostaną wyświetlone w konsoli programu .
Jaka jest Twoja nazwa?
BookmarkName: UserName — OwnerDisplayName: ReadLineSteveWitaj, Steve
Variable<string> name = new Variable<string>();
Activity wf = new Sequence
{
Variables = { name },
Activities =
{
new WriteLine
{
Text = "What is your name?"
},
new ReadLine
{
BookmarkName = "UserName",
Result = new OutArgument<string>(name)
},
new WriteLine
{
Text = new InArgument<string>((env) =>
("Hello, " + name.Get(env)))
}
}
};
// Create a WorkflowApplication instance.
WorkflowApplication wfApp = new WorkflowApplication(wf);
// Workflow lifecycle events omitted except idle.
AutoResetEvent idleEvent = new AutoResetEvent(false);
wfApp.Idle = delegate (WorkflowApplicationIdleEventArgs e)
{
// You can also inspect the bookmarks from the Idle handler
// using e.Bookmarks
idleEvent.Set();
};
// Run the workflow.
wfApp.Run();
// Wait for the workflow to go idle and give it a chance
// to create the Bookmark.
idleEvent.WaitOne();
// Inspect the bookmarks
foreach (BookmarkInfo info in wfApp.GetBookmarks())
{
Console.WriteLine($"BookmarkName: {info.BookmarkName} - OwnerDisplayName: {info.OwnerDisplayName}");
}
// Gather the user's input and resume the bookmark.
wfApp.ResumeBookmark("UserName", Console.ReadLine());
Poniższy przykład kodu sprawdza WorkflowApplicationIdleEventArgs przekazany do obsługi Idle instancji WorkflowApplication. W tym przykładzie przepływ pracy w stanie bezczynności ma jeden Bookmark o nazwie EnterGuess
, należący do aktywności o nazwie ReadInt
. Ten przykład kodu jest oparty na Jak uruchomić przepływ pracy, który jest częścią samouczka wprowadzającego . Jeśli program obsługi Idle w tym kroku został zmodyfikowany tak, aby zawierał kod z tego przykładu, zostaną wyświetlone następujące dane wyjściowe.
Nazwa zakładki: EnterGuess — OwnerDisplayName: ReadInt
wfApp.Idle = delegate (WorkflowApplicationIdleEventArgs e)
{
foreach (BookmarkInfo info in e.Bookmarks)
{
Console.WriteLine($"BookmarkName: {info.BookmarkName} - OwnerDisplayName: {info.OwnerDisplayName}");
}
idleEvent.Set();
};
Podsumowanie
WorkflowInvoker zapewnia lekki sposób wywoływania przepływów pracy i chociaż udostępnia metody przekazywania danych na początku przepływu pracy i wyodrębniania danych z ukończonego przepływu pracy, nie zapewnia bardziej złożonych scenariuszy, w których można używać WorkflowApplication.