Windows Workflow Foundation 4, část 3

Tomáš Holan       27.07.2011       Workflow Foundation       10335 zobrazení

Ještě než přistoupíme k návrhu samotného workflow, připravíme si custom XAML aktivitu, která v sobě zaobalí volání pomocných aktivit OdeslaniZadosti, ObdrzeniRozhodnutiZadosti a StornoZadosti do jednoho logického celku.

Aktivita obdrží kolekci žádostí. Provede odeslání všech těchto žádostí a čekání na rozhodnutí kterékoliv z těchto žádostí. Při obdržení rozhodnutí pro první z nich čekání na ostatní ukončí, všechny žádosti zneplatní a rozhodnutou žádost i její rozhodnutí vrátí jako výstupní argumenty.

XAML aktivita nemá žádný code behind, ale je celá definovaná jen pomoci jazyka XAML resp. pomoci designeru. Ve složce odpovídající namespace PlanAbsenci.Schvaleni.Activities proto zvolíme volbu Add/New Item… a vybereme typ Workflow/Activity. Novou aktivitu pojmenujeme RozhodnutiZadosti.xaml. Otevře se nám prázdná plocha workflow designeru. Na tuto plochu lze z toolboxu umísťovat standardní (například Sequence, If, Switch, While, Assign) i naše vlastní již připravené custom aktivity.

V části Arguments založíme vstupní argument Zadosti typu IEnumerable<ZadostRequest> a nastavíme ho jako povinný (vlastnosti IsRequired), dále výstupní argument Result typu ZadostResponse a výstupní argument RozhodnutaZadost typu ZadostRequest (viz obr.).

Veškerá činnost aktivity probíhá uvnitř aktivity ParallelForEach<T>, kde T (nastavuje se pomoci vlastnosti TypeArgument) je v našem případě typ ZadostRequest. Dále nastavíme výraz pro CompletionCondition na “true”, což způsobí, že se činnost aktivity ukončí po dokončení první větve a ostatní paralelní větve budou stornovány. Výraz pro Values bude “Zadosti” (vstupní argument aktivity) a jméno proměnné paralelního cyklu pojmenujeme “zadost”.

Pozn.: Všechny výrazy pro výchozí hodnoty argumentů/proměnných, výrazy pro argumenty aktivit apod., které se ve workflow designeru zadávají, jsou zadávány v jazyce VB.NET.

Body aktivity ParallelForEach<T> je tvořeno aktivitou Sequence. Tělo nejprve volá aktivitu OdeslaniZadosti (výraz pro vstupní argument Zadost bude “zadost”), a následně aktivitu CancellationScope. Body této aktivity je tvořeno aktivitou Pick s jednou větví PickBranch. Tyto aktivity umožňují čekat na externí událost (část trigger). Na úrovni (scope) aktivity PickBranch založíme proměnnou (v části Variables) zadostResponse typu ZadostResponse (viz obr.).

Jako Trigger aktivity PickBranch bude v našem případě sloužit aktivita ObdrzeniRozhodnutiZadosti (výraz pro vstupní argument ZadostGuid bude “zadost.Guid”, výraz pro Result bude zadostResponse – zavedená proměnná).

Část Action aktivity PickBranch je opět aktivita Sequence s aktivitami StornoZadosti (výraz pro Zadost je “zadost”), Assign (výraz pro To je “RozhodnutaZadost” – výstupní argument celé aktivity, výraz pro Value je “zadost”) a druhou Assign (výraz pro To je “Result” – opět výstupní argument celé aktivity, výraz pro Value je “zadostResponse” – proměnná zavedená na scope aktivity PickBranch). Tím dojde po rozhodnutí žádosti nastavení výstupních argumentů a zneplatnění rozhodnuté žádosti.

Zbývá provedení zneplatnění i ostatních žádostí, toho je docíleno voláním aktivity StornoZadosti (výraz pro Zadost je “zadost”) v části CancellationHandler aktivity CancellationScope.

Tím máme tuto aktivitu navrženou, její výsledný XAML kód vypadá následovně:

<Activity mc:Ignorable="sap" x:Class="PlanAbsenci.Schvaleni.Activities.RozhodnutiZadosti" xmlns="http://schemas.microsoft.com/netfx/2009/xaml/activities" xmlns:local="clr-namespace:PlanAbsenci.Schvaleni" xmlns:local1="clr-namespace:PlanAbsenci.Schvaleni.Activities" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mv="clr-namespace:Microsoft.VisualBasic;assembly=System" xmlns:mva="clr-namespace:Microsoft.VisualBasic.Activities;assembly=System.Activities" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:s1="clr-namespace:System;assembly=System" xmlns:s2="clr-namespace:System;assembly=System.Xml" xmlns:s3="clr-namespace:System;assembly=System.Core" xmlns:s4="clr-namespace:System;assembly=System.ServiceModel" xmlns:sa="clr-namespace:System.Activities;assembly=System.Activities" xmlns:sad="clr-namespace:System.Activities.Debugger;assembly=System.Activities" xmlns:sap="http://schemas.microsoft.com/netfx/2009/xaml/activities/presentation" xmlns:scg="clr-namespace:System.Collections.Generic;assembly=mscorlib" xmlns:scg1="clr-namespace:System.Collections.Generic;assembly=System.Reactive" xmlns:scg2="clr-namespace:System.Collections.Generic;assembly=System.CoreEx" xmlns:scg3="clr-namespace:System.Collections.Generic;assembly=System" xmlns:scg4="clr-namespace:System.Collections.Generic;assembly=System.ServiceModel" xmlns:scg5="clr-namespace:System.Collections.Generic;assembly=System.Core" xmlns:sd="clr-namespace:System.Data;assembly=System.Data" xmlns:sl="clr-namespace:System.Linq;assembly=System.Core" xmlns:st="clr-namespace:System.Text;assembly=mscorlib" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <x:Members>
    <x:Property Name="Result" Type="OutArgument(local:ZadostResponse)" />
    <x:Property Name="Zadosti" Type="InArgument(scg:IEnumerable(local:ZadostRequest))">
      <x:Property.Attributes>
        <RequiredArgumentAttribute />
      </x:Property.Attributes>
    </x:Property>
    <x:Property Name="RozhodnutaZadost" Type="OutArgument(local:ZadostRequest)" />
  </x:Members>
  <sap:VirtualizedContainerService.HintSize>536,1115</sap:VirtualizedContainerService.HintSize>
  <mva:VisualBasic.Settings>Assembly references and imported namespaces for internal implementation</mva:VisualBasic.Settings>
  <ParallelForEach x:TypeArguments="local:ZadostRequest" CompletionCondition="True" DisplayName="ParallelForEach&lt;ZadostRequest&gt;" sap:VirtualizedContainerService.HintSize="496,1075" Values="[Zadosti]">
    <ActivityAction x:TypeArguments="local:ZadostRequest">
      <ActivityAction.Argument>
        <DelegateInArgument x:TypeArguments="local:ZadostRequest" Name="zadost" />
      </ActivityAction.Argument>
      <Sequence sap:VirtualizedContainerService.HintSize="466,969">
        <sap:WorkflowViewStateService.ViewState>
          <scg:Dictionary x:TypeArguments="x:String, x:Object">
            <x:Boolean x:Key="IsExpanded">True</x:Boolean>
            <x:Boolean x:Key="IsPinned">False</x:Boolean>
          </scg:Dictionary>
        </sap:WorkflowViewStateService.ViewState>
        <local1:OdeslaniZadosti DisplayName="OdeslaniZadosti" sap:VirtualizedContainerService.HintSize="444,22" Zadost="[zadost]" />
        <CancellationScope sap:VirtualizedContainerService.HintSize="444,783">
          <sap:WorkflowViewStateService.ViewState>
            <scg:Dictionary x:TypeArguments="x:String, x:Object">
              <x:Boolean x:Key="IsExpanded">True</x:Boolean>
              <x:Boolean x:Key="IsPinned">False</x:Boolean>
            </scg:Dictionary>
          </sap:WorkflowViewStateService.ViewState>
          <Pick DisplayName="PickCekaniNaRozhodnuti" sap:VirtualizedContainerService.HintSize="408,606">
            <PickBranch DisplayName="BranchObdrzeniRozhodnuti" sap:VirtualizedContainerService.HintSize="294,560">
              <PickBranch.Variables>
                <Variable x:TypeArguments="local:ZadostResponse" Name="zadostResponse" />
              </PickBranch.Variables>
              <sap:WorkflowViewStateService.ViewState>
                <scg:Dictionary x:TypeArguments="x:String, x:Object">
                  <x:Boolean x:Key="IsExpanded">True</x:Boolean>
                  <x:Boolean x:Key="IsPinned">False</x:Boolean>
                </scg:Dictionary>
              </sap:WorkflowViewStateService.ViewState>
              <PickBranch.Trigger>
                <local1:ObdrzeniRozhodnutiZadosti DisplayName="ObdrzeniRozhodnutiZadosti" sap:VirtualizedContainerService.HintSize="264,100" Result="[zadostResponse]" ZadostGuid="[zadost.Guid]" />
              </PickBranch.Trigger>
              <Sequence sap:VirtualizedContainerService.HintSize="264,342">
                <sap:WorkflowViewStateService.ViewState>
                  <scg:Dictionary x:TypeArguments="x:String, x:Object">
                    <x:Boolean x:Key="IsExpanded">True</x:Boolean>
                    <x:Boolean x:Key="IsPinned">False</x:Boolean>
                  </scg:Dictionary>
                </sap:WorkflowViewStateService.ViewState>
                <local1:StornoZadosti sap:VirtualizedContainerService.HintSize="242,22" Zadost="[zadost]" />
                <Assign sap:VirtualizedContainerService.HintSize="242,58">
                  <Assign.To>
                    <OutArgument x:TypeArguments="local:ZadostRequest">[RozhodnutaZadost]</OutArgument>
                  </Assign.To>
                  <Assign.Value>
                    <InArgument x:TypeArguments="local:ZadostRequest">[zadost]</InArgument>
                  </Assign.Value>
                </Assign>
                <Assign sap:VirtualizedContainerService.HintSize="242,58">
                  <Assign.To>
                    <OutArgument x:TypeArguments="local:ZadostResponse">[Result]</OutArgument>
                  </Assign.To>
                  <Assign.Value>
                    <InArgument x:TypeArguments="local:ZadostResponse">[zadostResponse]</InArgument>
                  </Assign.Value>
                </Assign>
              </Sequence>
            </PickBranch>
          </Pick>
          <CancellationScope.CancellationHandler>
            <local1:StornoZadosti sap:VirtualizedContainerService.HintSize="408,50" Zadost="[zadost]" />
          </CancellationScope.CancellationHandler>
        </CancellationScope>
      </Sequence>
    </ActivityAction>
  </ParallelForEach>
</Activity>

Nyní máme již připravené opravdu všechny aktivity a můžeme přistoupit k návrhu samotného Workflow.

Workflow umístíme do složky a namespace PlanAbsenci.Schvaleni.Workflow. Workflow bude opět typu Workflow/Activity (vzpomínáte, že workflow je ve WF vlastně “obyčejná” XAML aktivita) a pojmenujeme ho SchvaleniAbsenceVedoucim.xaml. Workflow samotné bude v našem příkladu překvapivě asi to jednodušší:

Workflow bude mít jeden povinný vstupní argument Absence typu AbsenceRequest a nebude mít žádný výstupní argument. Jeho tělo bude tvořeno aktivitou Sequence. Na úrovni této aktivity založíme proměnné zadosti typu IEnumerable<ZadostRequest>, zadost typu ZadostRequest a zadostResponse typu ZadostResponse (viz obr.).

Sekvence bude volat nejprve aktivitu GetZadostiVedouci (výraz pro Absence je “Absence” - vstupní argument workflow, výraz pro Result je “zadosti”), dále aktivitu RozhodnutiZadosti (výraz pro Zadosti je “zadosti”, výraz pro Result je “zadostResponse” a pro RozhodnutaZadost je “zadost”) a aktivitu If.

Podmínka (Condition) aktivity If bude výraz “zadostResponse.Schvaleno” tj. zda byla žádost schválena. V případě schválení žádosti se v části Then volá aktivita AbsenceSchvalení (výraz pro Zadost je “zadost” a výraz pro ZadostResponse je “zadostResponse”) a v případě zamítnutí v části Else aktivita AbsenceZamitnuti (výraz pro Zadost je opět “zadost” a výraz pro ZadostResponse je “zadostResponse”).

A opět uvedu i výsledný XAML kód:

<Activity mc:Ignorable="sap" x:Class="PlanAbsenci.Schvaleni.Workflow.SchvaleniAbsenceVedoucim" xmlns="http://schemas.microsoft.com/netfx/2009/xaml/activities" xmlns:local="clr-namespace:PlanAbsenci.Schvaleni" xmlns:local1="clr-namespace:PlanAbsenci.Schvaleni.Activities" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mv="clr-namespace:Microsoft.VisualBasic;assembly=System" xmlns:mva="clr-namespace:Microsoft.VisualBasic.Activities;assembly=System.Activities" xmlns:psw="clr-namespace:PlanAbsenci.Schvaleni.Workflow" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:s1="clr-namespace:System;assembly=System" xmlns:s2="clr-namespace:System;assembly=System.Xml" xmlns:s3="clr-namespace:System;assembly=System.Core" xmlns:s4="clr-namespace:System;assembly=System.ServiceModel" xmlns:sa="clr-namespace:System.Activities;assembly=System.Activities" xmlns:sad="clr-namespace:System.Activities.Debugger;assembly=System.Activities" xmlns:sap="http://schemas.microsoft.com/netfx/2009/xaml/activities/presentation" xmlns:scg="clr-namespace:System.Collections.Generic;assembly=System.Reactive" xmlns:scg1="clr-namespace:System.Collections.Generic;assembly=System.CoreEx" xmlns:scg2="clr-namespace:System.Collections.Generic;assembly=System" xmlns:scg3="clr-namespace:System.Collections.Generic;assembly=System.ServiceModel" xmlns:scg4="clr-namespace:System.Collections.Generic;assembly=System.Core" xmlns:scg5="clr-namespace:System.Collections.Generic;assembly=mscorlib" xmlns:sd="clr-namespace:System.Data;assembly=System.Data" xmlns:sl="clr-namespace:System.Linq;assembly=System.Core" xmlns:st="clr-namespace:System.Text;assembly=mscorlib" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <x:Members>
    <x:Property Name="Absence" Type="InArgument(local:AbsenceRequest)">
      <x:Property.Attributes>
        <RequiredArgumentAttribute />
      </x:Property.Attributes>
    </x:Property>
  </x:Members>
  <sap:VirtualizedContainerService.HintSize>526,494</sap:VirtualizedContainerService.HintSize>
  <mva:VisualBasic.Settings>Assembly references and imported namespaces for internal implementation</mva:VisualBasic.Settings>
  <Sequence sap:VirtualizedContainerService.HintSize="486,454">
    <Sequence.Variables>
      <Variable x:TypeArguments="scg5:IEnumerable(local:ZadostRequest)" Name="zadosti" />
      <Variable x:TypeArguments="local:ZadostResponse" Name="zadostResponse" />
      <Variable x:TypeArguments="local:ZadostRequest" Name="zadost" />
    </Sequence.Variables>
    <sap:WorkflowViewStateService.ViewState>
      <scg5:Dictionary x:TypeArguments="x:String, x:Object">
        <x:Boolean x:Key="IsExpanded">True</x:Boolean>
      </scg5:Dictionary>
    </sap:WorkflowViewStateService.ViewState>
    <local1:GetZadostiVedouci Absence="[Absence]" sap:VirtualizedContainerService.HintSize="464,22" Result="[zadosti]" />
    <local1:RozhodnutiZadosti sap:VirtualizedContainerService.HintSize="464,22" Result="[zadostResponse]" RozhodnutaZadost="[zadost]" Zadosti="[zadosti]" />
    <If Condition="[zadostResponse.Schvaleno]" DisplayName="IfSchvaleno" sap:VirtualizedContainerService.HintSize="464,206">
      <If.Then>
        <local1:AbsenceSchvaleni sap:VirtualizedContainerService.HintSize="219,100" Zadost="[zadost]" ZadostResponse="[zadostResponse]" />
      </If.Then>
      <If.Else>
        <local1:AbsenceZamitnuti sap:VirtualizedContainerService.HintSize="220,100" Zadost="[zadost]" ZadostResponse="[zadostResponse]" />
      </If.Else>
    </If>
  </Sequence>
</Activity>

Tím je dokončen návrh jednoho konkrétního workflow využívající dříve připravené aktivity. Je asi také vidět, že skutečná síla použití WF bude až v případech různých modifikací, variabilitě a možnosti využití několika různých takovýchto workflow a tomu odpovídajícím procesům.

 

hodnocení článku

0 bodů / 1 hlasů       Hodnotit mohou jen registrované uživatelé.

 

Nový příspěvek

 

                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.

Nyní zakládáte pod článkem nové diskusní vlákno.
Pokud chcete reagovat na jiný příspěvek, klikněte na tlačítko "Odpovědět" u některého diskusního příspěvku.

Nyní odpovídáte na příspěvek pod článkem. Nebo chcete raději založit nové vlákno?

 

  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:
Heslo:

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback