Workflow tulajdonságainak kiolvasása

Gyakori kérdés, hogy egy adott workflow példány belső állapotát, mezőit, tulajdonságait hogyan lehet kiolvasni? A rövid válasz, hogy egyszerűen sehogy:

  • Egy futó workflow esetén az ember a WorkflowInstance osztály környékén próbálkozik, amin keresztül legfeljebb az InstanceId lehet megszerezni. Futó workflow esetén az igazi megoldás az lehet, ha kommunikálunk a folyamattal, azaz a Queuing- vagy az ExternalDataExchangeServicen-en keresztül üzenetet küldünk neki.
  • Egy már lefutott workflow példány esetén már nincs más lehetőségünk, mint a tracking service használata.

Az utóbbi az érdekesebb és kevésbé dokumentált eset. A cél tehát egy SimpleWorkflow típusú folyamat Result tulajdonságának utólagos kiolvasása.

Ehhez szükségünk lesz a tracking szolgáltatásra, használjuk a beépített SqlTrackingService-t, hozzuk létre az adatbázisát például WorkflowTrackingStore néven és adjuk hozzá a runtime-hoz:

    const string connectionString = 
@"Initial Catalog=WorkflowTrackingStore;Data Source=.SqlExpress;Integrated Security=SSPI;"; SqlTrackingService trackingService = new SqlTrackingService( connectionString ); runtime.AddService( trackingService );

A workflow futtatásakor nem kell semmi extrát tennünk, a tracking naplóz. Csakhogy nem azt naplózza, ami nekünk kell, hanem amiről azt hiszi, hogy nekünk kell! A tracking profile szolgál arra, hogy meghatározzuk, mire van szükségünk. Ezt megtehetjük objektum modell segítségével (használva a System.Workflow.Runtime.Tracking.TrackingProfile) osztályt, vagy deklaratívan egy XML segítségével. XML-t senki sem szeret kézzel írni (minden ellenkező híreszteléssel szemben az XML nem arra való, hogy homo sapiensek olvassák), a profil összeállításához használjuk a WF SDK példák között elérhető Tracking Profile Designert (közvetlenül letölthető a WF közösségi oldalról is).

Néhány tipp a Tracking Profile Designer használatához:

  • Ne felejtsük el módosítani a tracking adatbázisunkra mutató connection stringet a .config állományban.
  • Másoljuk az exe mellé a workflow-nkat tartalmazó lefordított szerelvényt.
  • A profil erősen típusosan hivatkozik a naplózandó workflow típusára, ami egyben a workflow-t tartalmazó szerelvényre is erősen típusos hivatkozást jelent. Ha nem akarjuk minden fordítás után módosítani a profilt, akkor a workflow AssemblyInfo.cs fájljában adjunk fix és ne csillagos verziószámot az AssemblyVersion attribútumban.
  • Ne csak adatbázisba mentsük el a létrehozott profilt, hanem archiváljuk magunknak XML-be is.

A profilban workflow, activity és felhasználói eseményeket tudunk meghatározni. Ahhoz, hogy egy adott tulajdonság értékét naplózza a rendszer, ún. data tracking extractot kell definiálnunk. Erre szolgál a TrackingExtract ősosztály, amiből a WorkflowDataTrackingExtract és az ActivityDataTrackingExtract származik. Biztos én bénáztam, de akárhogy kattintgattam a profile designerben, nekem nem sikerült workflow extractot létrehozni, csak activity extractot. Annyi baj legyen, a szekvenciális workflow végülis egy szép nagy sequence activity.

Nekem ez lett a profil:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<TrackingProfile xmlns="http://schemas.microsoft.com/winfx/2006/workflow/trackingprofile" version="1.0.0">
    <TrackPoints>
        <ActivityTrackPoint>
            <MatchingLocations>
                <ActivityTrackingLocation>
                    <Activity>
                        <Type>MyApp.SimpleWorkflow, MyApp, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null</Type>
                        <MatchDerivedTypes>false</MatchDerivedTypes>
                    </Activity>
                    <ExecutionStatusEvents>
                        <ExecutionStatus>Closed</ExecutionStatus>
                    </ExecutionStatusEvents>
                </ActivityTrackingLocation>
            </MatchingLocations>
            <Extracts>
                <ActivityDataTrackingExtract>
                    <Member>Result</Member>
                </ActivityDataTrackingExtract>
            </Extracts>
        </ActivityTrackPoint>
    </TrackPoints>
</TrackingProfile>

Emberi nyelvre fordítva: a SimpleWorkflow típusú activity (!) Closed eseményekor kérjük a Result tulajdonság naplózását. És semmi több! Ha tehát valaki később csak a lefutott workflow példányokat szeretné felolvasni vagy további tracking információkra van szüksége, az kénytelen lesz kiegészíteni a profilt még azokkal is.

Az összekattintgatott profilt mentsük el az adatbázisba. Akit érdekel, hogy mi történik a háttérben, az nézze meg a TrackingProfile táblát vagy az UpdateProfile tárolt eljárást.

Megvan tehát a workflow és a profil, ha most futtatjuk a folyamatot, akkor az adatbázisba be kell kerülnie a naplózandó adatoknak. Érdemes bekukkantani a TrackingDataItem táblába, ha mindent jól csináltunk, ott lesz.

Nem maradt más hátra, le kell kérdeznünk a tracking adatbázist, mégpedig az objektum modell SqlTrackingQuery osztályának segítségével:

    const string connectionString = 
@"Initial Catalog=WorkflowTrackingStore;Data Source=.SqlExpress;Integrated Security=SSPI;"; SqlTrackingQuery query = new SqlTrackingQuery( connectionString );

Csak azokat a workflow példányokat kérjük, amik SimpleWorkflow típusúak:

    SqlTrackingQueryOptions options = new SqlTrackingQueryOptions();
    options.WorkflowType = typeof( MyApp.SimpleWorkflow ); // Egyben szerelvény és verzió hivatkozás is!

Na itt kell észnél lenni, mert ha itt megszokásból megadjuk, hogy csak a Completed állapotú példányokra vagyunk kiváncsiak, de ennek a ténynek a naplózását nem kértük a profilban, akkor nem lesz eredményünk. Kérjük vissza a feltételnek megfelelő példányokat és ha nem akarunk sokat debuggolni, írassuk ki, hogy lett-e eredmény:

    IList<SqlTrackingWorkflowInstance> instances = query.GetWorkflows( options );
    Console.WriteLine( "Talált workflow: {0} db", instances.Count );

Innentől kezdve nincs más hátra, végig kell menni a példányokon. Minden SqlTrackingWorkflowInstance tartalmaz egy ActivityEvents gyűjteményt az activity szintű eseményeknek. Ha a profilban így kértük, most is ezt kell használni. Az ActivityTrackingRecordok Body tulajdonságában találhatjuk meg a kért napló sorokat:

    foreach( SqlTrackingWorkflowInstance instance in instances )
    {
        Console.WriteLine( "nnID: {0}", instance.WorkflowInstanceId );

        foreach( ActivityTrackingRecord activityRecord in instance.ActivityEvents )
        {
            Console.WriteLine( "t{0,-10:T}{1,-20}{2}", activityRecord.EventDateTime, 
activityRecord.QualifiedName, activityRecord.ExecutionStatus ); foreach( TrackingDataItem dataItem in activityRecord.Body ) { Console.WriteLine( "ttData: {0}t{1}", dataItem.FieldName, dataItem.Data ); } } }

Természetesen még véletlenül sincs benne egyik változó nevében sem, hogy extract. Ami a profilban extract, azt itt TrackingDataItemnek hívják. Ennyi, tessék kísérletezni!

A fenti példát tartalmazó teljes forráskód letölthető a devPORTALról.

 

Technorati tags: , ,

Vélemény, hozzászólás?

Adatok megadása vagy bejelentkezés valamelyik ikonnal:

WordPress.com Logo

Hozzászólhat a WordPress.com felhasználói fiók használatával. Kilépés / Módosítás )

Twitter kép

Hozzászólhat a Twitter felhasználói fiók használatával. Kilépés / Módosítás )

Facebook kép

Hozzászólhat a Facebook felhasználói fiók használatával. Kilépés / Módosítás )

Google+ kép

Hozzászólhat a Google+ felhasználói fiók használatával. Kilépés / Módosítás )

Kapcsolódás: %s