<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:copyright="http://blogs.law.harvard.edu/tech/rss" xmlns:image="http://purl.org/rss/1.0/modules/image/">
    <channel>
        <title>WWF</title>
        <link>http://blogs.ugidotnet.org/dsantarelli/category/WWF.aspx</link>
        <description>WWF</description>
        <language>it-IT</language>
        <copyright>Dario Santarelli</copyright>
        <generator>Subtext Version 2.6.0.0</generator>
        <item>
            <title>[WF] Cancellation Handling</title>
            <link>http://blogs.ugidotnet.org/dsantarelli/archive/2008/06/15/wf-cancellation-handling.aspx</link>
            <description>La cancellazione dell'esecuzione di un workflow è simile per certi versi al Fault Handling: osando un parallelismo con il mondo della programmazione OO, potremmo affermare che se i Fault Handler possono essere paragonati ad un catch, i Cancellation Handlers costituiscono il finally. Da un punto di vista pratico, l'utilità dei Cancellation Handlers si trova nella capacità di permettere l'esecuzione di operazioni (tipicamente notifiche, logging, cleanup delle risorse utilizzate...) prima che il workflow venga effettivamente terminato, senza alcuna possibilità di interruzione. Il processo di cancellazione infatti, una volta avviato, è inarrestabile.Un Cancellation Handler può essere definito sia a livello di Workflow (globale) che localmente a livello di Composite Activity (es. ParallelActivity, SequenceActivity). In quest'ultimo caso, il processo di cancellazione è attivato se almeno una delle attività figlie di una Composite Activity scatena un errore non gestito o è in esecuzione quando il workflow è cancellato (ad es. dall'applicazione host).Veniamo ora ad un aspetto interessante: un Cancellation Handler può non comportarsi esattamente come ci aspettiamo. Ebbene si.Se ad esempio impostiamo un Cancellation Handler globale (a livello di workflow), non è detto che venga chiamato ogniqualvolta il Workflow subisce una cancellazione. Infatti, poiché la cancellazione può essere gestita anche localmente, in caso di errore saranno attivati solamente i Cancellation Handler delle Composite Activities che possiedono delle attività figlie in esecuzione. Per quanto riguarda il Cancellation Handler globale, invece, sarà attivato solo se la cancellazione viene invocata "esternamente" per l'intero workflow (tipicamente via UI/Applicazione Host).Vediamo un esempio: ecco un workflow che definisce una ParallelActivity con 3 branch, ciascuno dei quali munito di una propria sequenza interna.   Supponendo che il branch centrale causi un' eccezione non gestita ( facendo passare inevitabilmente il workflow allo stato Terminated ), gli altri branch contenuti nella Parallel Activity principale verranno informati del fatto che il workflow sta per essere terminato, con conseguente attivazione dei relativi Cancellation Handlers. Stessa cosa vale anche per le Composite Activities ulteriormente nidificate. Tuttavia, i Cancellation Handlers della Composite Activity che ha generato l'eccezione, della Parallel Activity esterna e del Workflow, NON verranno svegliati, differentemente da quanto ci si potrebbe aspettare.Tirando le somme, è importante ricordare che il comportamento dei Cancellation Handler è strettamente dipendente dall'ordine di esecuzione delle attività al momento della cancellazione. Se da una parte possiamo pianificare la gestione dei Cancellation Handlers, dall'altra costituisce buona prassi (per ovvi motivi) NON implementare dipendenze logiche/funzionali tra i vari Cancellation Handlers e soprattutto sperimentare il loro comportamento nell'ambiente di esecuzione, in quanto può avere una natura variabile (addirittura eseguendo in modalità debug o meno).Technorati tags: Workflow WF&lt;img src="http://blogs.ugidotnet.org/dsantarelli/aggbug/93036.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Dario Santarelli</dc:creator>
            <guid>http://blogs.ugidotnet.org/dsantarelli/archive/2008/06/15/wf-cancellation-handling.aspx</guid>
            <pubDate>Sun, 15 Jun 2008 15:37:55 GMT</pubDate>
            <comments>http://blogs.ugidotnet.org/dsantarelli/archive/2008/06/15/wf-cancellation-handling.aspx#feedback</comments>
            <wfw:commentRss>http://blogs.ugidotnet.org/dsantarelli/comments/commentRss/93036.aspx</wfw:commentRss>
            <trackback:ping>http://blogs.ugidotnet.org/dsantarelli/services/trackbacks/93036.aspx</trackback:ping>
        </item>
        <item>
            <title>[WF] Creare un Custom Activity Layout</title>
            <link>http://blogs.ugidotnet.org/dsantarelli/archive/2008/06/08/wf-creare-un-custom-activity-layout.aspx</link>
            <description>Il designer di WF integrato in Visual Studio 2008 (o 2005 munito delle relative 'extensions') utilizza le classi del namespace System.Workflow.ComponentModel.Design per il controllo del rendering delle attività. In realtà il layout di una attività semplice (Activity) o composta (CompositeActivity) può essere personalizzato in maniera molto facile. Anzitutto, il designer sfrutta due classi base: ActivityDesigner e ActivityDesignerTheme. La prima si occupa di gestire "come" un' attività verrà visualizzata nel designer (es. dimensioni, forma, editing etc.), mentre la seconda viene prettamente utilizzata per impostare il tema da associare al designer (es. colori, bordi etc.). Nello stesso namespace esistono inoltre diverse specializzazioni della classe base ActivityDesigner come CompositeActivityDesigner, SequentialActivityDesigner, ParallelActivityDesigner etc.Quindi, estendendo opportunamente metodi e proprietà della classe base che scegliamo, possiamo apportare interessanti personalizzazioni.Vediamo un semplice esempio. Creiamo una classe MyCustomActivityDesignerTheme ereditata da ActivityDesignerTheme per definire il nostro tema.using System.Workflow.ComponentModel.Design;namespace MyCustomActivityLibrary{    internal sealed class MyCustomActivityDesignerTheme : ActivityDesignerTheme    {      public MyCustomActivityDesignerTheme(WorkflowTheme theme) : base(theme)      {         base.Initialize();         this.BackColorStart = System.Drawing.Color.Orange;         this.BackColorEnd = System.Drawing.Color.OrangeRed;         this.BorderColor = System.Drawing.Color.Red;         this.BorderStyle = System.Drawing.Drawing2D.DashStyle.Solid;                  }    }}&lt;?xml:namespace prefix = o /?&gt; Quindi, ci concentriamo sul comportamento del rendering del designer implementando una classe MyCustomActivityDesigner derivata da ActivityDesigner, specificando che il tema da utilizzare sia quello sopra definito (MyCustomActivityDesignerTheme)using System.Workflow.ComponentModel.Design;using System.Workflow.ComponentModel;using System.Drawing;namespace MyCustomActivityLibrary{  [ActivityDesignerTheme(typeof(MyCustomActivityDesignerTheme))]  internal sealed class MyCustomActivityDesigner : ActivityDesigner  {    protected override void Initialize(Activity activity)    {      base.Initialize(activity);            this.Text = "[MyCustomActivity] " + activity.Name;                    }    protected override Rectangle TextRectangle    {      get { ... }    }    protected override Rectangle ImageRectangle    {      get { ... }     }       protected override void OnPaint(ActivityDesignerPaintEventArgs e)    { ... }  }}Osserviamo come l'overriding di metodi e proprietà della classe ActivityDesigner (qui la lista completa) ci permette di modificare particamente ogni aspetto del rendering. Nel nostro stupidissimo esempio è stata semplicemente estesta la logica del metodo Initialize(...) per anteporre il prefisso "[MyCustomActivity]" al nome dell' attività di interesse. L'ultimo step è ovviamente l'associazione del designer realizzato nei confronti di una nostra attività custom (ShippingActivity) tramite l'impostazione dell' attributo Designer.using System.Workflow.ComponentModel;using System.Workflow.ComponentModel.Design;using System.Workflow.Activities;using System.Workflow.Activities.Rules;namespace MyCustomActivityLibrary{  [Designer(typeof(MyCustomActivityDesigner))]  public class ShippingActivity : Activity  { ... }}Il risultiamo che otteniamo è il seguente: Technorati tags: Workflow Foundation WF&lt;img src="http://blogs.ugidotnet.org/dsantarelli/aggbug/92968.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Dario Santarelli</dc:creator>
            <guid>http://blogs.ugidotnet.org/dsantarelli/archive/2008/06/08/wf-creare-un-custom-activity-layout.aspx</guid>
            <pubDate>Sun, 08 Jun 2008 16:18:47 GMT</pubDate>
            <comments>http://blogs.ugidotnet.org/dsantarelli/archive/2008/06/08/wf-creare-un-custom-activity-layout.aspx#feedback</comments>
            <wfw:commentRss>http://blogs.ugidotnet.org/dsantarelli/comments/commentRss/92968.aspx</wfw:commentRss>
            <trackback:ping>http://blogs.ugidotnet.org/dsantarelli/services/trackbacks/92968.aspx</trackback:ping>
        </item>
        <item>
            <title>[Windows Workflow Foundation] Utilizzo della WhileActivity.DynamicActivity</title>
            <link>http://blogs.ugidotnet.org/dsantarelli/archive/2008/05/22/windows-workflow-foundation-utilizzo-della-whileactivity.dynamicactivity.aspx</link>
            <description>La WhileActivity è una attività di uso abbastanza comune in WWF, ma il suo utilizzo implica spesso di tenere in considerazione un paio questioni non così banali:  Può contenere una sola attività figlio. Quindi, per eseguire attività multiple al suo interno occorre utilizzare un' attività "wrapper" (come la CompositeActivity o la SequenceActivity) che contenga l'insieme le nostre attività da eseguire.  Ad ogni iterazione il runtime crea una nuova istanza dell'attività figlia della WhileActivity e di conseguenza ciascuna attività possiederà un contesto di esecuzione indipendente. Partendo da queste due assunzioni, nel caso in cui volessimo accedere programmaticamente ad un' attività figlia (base o custom) all'interno della nostra WhileActivity, abbiamo bisogno di ricorrere alla proprietà WhileActivity.DynamicActivity per reperirne la reference d'istanza.Ad esempio, supponendo di voler accedere ad una nostra attività custom ShippingActivity inserita all'interno di una SequenceActivity iterata da una WhileActivity, possiamo utilizzare del codice come il seguente:using CustomActivityLibrary; // Libreria di attività custom...// La DynamicActivity in questo caso è una SequenceActivity 'wrapper' che contiene più attivitàShippingActivity shippingActivity = (ShippingActivity)myWhileActivity.DynamicActivity.GetActivityByName("shippingActivity", true);// Do something...In particolare, il metodo GetActivityByName con il secondo parametro impostato a true ritorna l'istanza della ShippingActivity assumendo che essa sia figlia della SequenceActivity (in questo caso la DynamicActivity) oggetto dell'iterazione tramite la WhileActivity. Technorati tags: Workflow WWF&lt;img src="http://blogs.ugidotnet.org/dsantarelli/aggbug/92772.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Dario Santarelli</dc:creator>
            <guid>http://blogs.ugidotnet.org/dsantarelli/archive/2008/05/22/windows-workflow-foundation-utilizzo-della-whileactivity.dynamicactivity.aspx</guid>
            <pubDate>Thu, 22 May 2008 19:56:52 GMT</pubDate>
            <comments>http://blogs.ugidotnet.org/dsantarelli/archive/2008/05/22/windows-workflow-foundation-utilizzo-della-whileactivity.dynamicactivity.aspx#feedback</comments>
            <wfw:commentRss>http://blogs.ugidotnet.org/dsantarelli/comments/commentRss/92772.aspx</wfw:commentRss>
            <trackback:ping>http://blogs.ugidotnet.org/dsantarelli/services/trackbacks/92772.aspx</trackback:ping>
        </item>
        <item>
            <title>[Windows Workflow Foundation] Modificare un Workflow a run-time</title>
            <link>http://blogs.ugidotnet.org/dsantarelli/archive/2008/05/15/windows-workflow-foundation-modificare-un-workflow-a-run-time.aspx</link>
            <description>Un' interessante possibilità che ci viene offerta da Workflow Foundation riguarda il cambiamento dinamico a run-time dell'albero delle attività di un' istanza di un workflow. In realtà, escludendo scenari particolari che richiedono una certa flessibilità nella composizione di un workflow, non ritengo che questa costituisca una prassi così consigliabile, considerando ad esempio i ritardi di esecuzione oppure i possibili ulteriori problemi che potrebbero affliggere il runtime a seguito di modifiche dinamiche in fase di esecuzione. Ad ogni modo, WWF ci permette di aggiornare dinamicamente un'istanza di workflow in termini di aggiunta/rimozione attività (anche custom), cambiamento del flow control, aggiornamento della logica condizionale (If-Else) , ma soprattutto in termini di gestione di nuovi eventi.All'atto pratico ci sono due modi per apportare modifiche ad un' istanza di un workflow in esecuzione: all'interno della logica del workflow stesso, oppure all'interno della logica dell'applicazione host. E' evidente che dal punto di vista del runtime, le modifiche interesseranno solo ed esclusivamente una precisa istanza di un workflow, dal momento che altre istanze potrebbero essere in esecuzione e non interessate al cambiamento previsto. Ciò significa che quando la nostra istanza di workflow raggiungerà lo stato Completed, i nostri cambiamenti 'moriranno' con essa poiché non coinvolgono la definizione originale del workflow.Cambiamenti all'interno dell'istanza di workflowPrima domanda: "Dove inseriamo il nostro codice che modifica il comportamento del workflow?" Risposta: tipicamente all'interno di un event handler di una attività (anche custom) del workflow stesso, ma preferibilmente ovunque il workflow si trovi in uno stato Suspended o Idled (ricordiamoci che stiamo lavorando su un' istanza in esecuzione). Vediamo un semplice esempio: abbiamo un SampleWorkflow (che deriva dal solito SequentialWorkflowActivity - ovvero una rappresentazione di un workflow che esegue delle attività sequenzialmente) in cui vogliamo aggiungere un' attività custom SumActivity che dietro le quinte non fa altro che sommare due interi A e B esposti come proprietà. Possiamo inserire questo 'cambiamento' all'interno della logica di una CodeActivity già definita a livello di workflow:public class SampleWorkflow : SequentialWorkflowActivity{   private void codeActivity_ExecuteCode(object sender, EventArgs e) {   ...   CustomActivityLibrary.SumActivity sumActivity = new CustomActivityLibrary.SumActivity();   WorkflowChanges changes = new WorkflowChanges(this);          sumActivity.A = 2;   sumActivity.B = 4;             changes.TransientWorkflow.Activities.Add(sumActivity);   changes.Validate();   this.ApplyWorkflowChanges(changes);   ...  }}Osserviamo immediatamente l'utilizzo della classe WorkflowChanges, che espone la proprietà TransientWorkflow, ovvero una clonazione dell'albero delle attività dell'istanza del workflow corrente a cui applicare i cambiamenti, che in seguito verranno propagati alla corretta istanza del workflow (lasciando le altre istanze immutate). Il metodo Validate si occupa di controllare preventivamente la semantica dei cambiamenti e quindi che non si verifichino potenziali eccezioni. Infine, tramite il metodo ApplyWorkflowChanges applichiamo i cambiamenti implementati direttamente sull'istanza del workflow in esecuzione.Cambiamenti all'interno dell'applicazione HostDiversamente dal caso precedente, i cambiamenti su un' istanza di un workflow vengono effettuati a partire dall'applicazione host, sfruttando le API messe a disposizione da WWF. Osservando il seguente esempio, si nota subito come l'unica differenza si trovi nel modo con cui andiamo a reperire l'istanza sui cui andare ad apportare le nostre personalizzazioni: in questo caso infatti un'istanza di workflow viene creata tramite il metodo CreateWorkflow del runtime e tramite il metodo GetWorkflowDefinition() non facciamo altro che puntare alla relativa attività root. Il codice dunque non fa altro che 'appendere' semplicemente la nostra SumActivity all'albero delle attività così come definito in SampleWorkflow. using (WorkflowRuntime workflowRuntime = new WorkflowRuntime()){  ...  WorkflowInstance workflow_instance = workflowRuntime.CreateWorkflow(typeof(SampleWorkflow));                  WorkflowChanges changes = new WorkflowChanges(workflow_instance.GetWorkflowDefinition());  CustomActivityLibrary.SumActivity sumActivity = new CustomActivityLibrary.SumActivity();  sumActivity.A = 2;  sumActivity.B = 4;  changes.TransientWorkflow.Activities.Add(sumActivity);  changes.Validate();  workflow_instance.ApplyWorkflowChanges(changes);  ...} Technorati tags: Workflow WWF&lt;img src="http://blogs.ugidotnet.org/dsantarelli/aggbug/92672.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Dario Santarelli</dc:creator>
            <guid>http://blogs.ugidotnet.org/dsantarelli/archive/2008/05/15/windows-workflow-foundation-modificare-un-workflow-a-run-time.aspx</guid>
            <pubDate>Thu, 15 May 2008 00:30:26 GMT</pubDate>
            <comments>http://blogs.ugidotnet.org/dsantarelli/archive/2008/05/15/windows-workflow-foundation-modificare-un-workflow-a-run-time.aspx#feedback</comments>
            <wfw:commentRss>http://blogs.ugidotnet.org/dsantarelli/comments/commentRss/92672.aspx</wfw:commentRss>
            <trackback:ping>http://blogs.ugidotnet.org/dsantarelli/services/trackbacks/92672.aspx</trackback:ping>
        </item>
    </channel>
</rss>