Articoli DevLeap

Articoli DevLeap

November 2008 - Posts

Visual Studio 2010: primo contatto (parte 3)

Autore: Roberto Brunetti - DevLeap  

In questa puntata ci concentriamo su Testing & Dubugging.

In VSTS2010 è stato aggiunto il supporto per la validazione della user interface. Si parte da un progetto di test con Add New Guided UI Test.

clip_image002

Si sceglie “Launch recorder to generate code”

L’idea è registrare cosa stiamo facendo e generare il codice per testare user interface WinForm e ASP.NET (e forse WPF già nella release 2010). Per Silverlight probabilmente occorrera aspettare ancora un pò.

Ad esempio eseguendo qualche click su questo form si ottiene questa registrazione

clip_image004

Poi si genera il codice tramite il pulsante evidenziato “Generate Code” (stare molto attenti: si siamo in debug posizionare prima il cursore nel metodo di test prima di generare il codice); meglio lanciare prima l’applicazione e poi registrare, al posto di fare F5 diretto da Visual Studio: almeno in questa prima CTP di ottobre 2008.

Per fare la validazione della UI si prende il UI Control Validator (tasto destro sul metodo o sul recorder a registrazione ferma premere “Add Validation”)

clip_image006

Per attivare il controllo si prende il “cazzillo” bianco in alto a destra del panel UI Control Finder e si fa un drag sul controllo da testare: con le freccie ci si può spostare sui controlli da testare.

Per ogni controllo si fa Add e al termine (done) si vedono le proprietà di ogni controllo e si impostano i valori di test.

clip_image008

In questo caso dichiaro che il checkbox deve essere disabilitato. Come si nota si possono controllare più o meno tutte le proprietà (colori, visibilità, focus, etc)

Al lanio del test creato viene fatta girare di nuovo l’applicazione (compleso l’F5 da VS se è stato registrato o l’apertura del browser, oppure l’applicazione deve essere aperta):

clip_image010

Sono andato anche a fare un giro sul sistema operativo per capire come si muove il registratore, e direi che ci segue molto bene.

clip_image012

Si vede la registrazione del Task Manager.

Per far partire un test si può fare tasto destro sul test e Run oppure, come sempre dalla finestra Test View.

Gli Unit Test aiutano, ma in VSTS 2010 si va oltre con Impact Analysis e Historical Debugger, affrontati in modo introduttivo nella prossima sezione.

Vediamo prima qualche dettaglio sul codice generato dallo strumento “Coded UI Test”.

Oltre al file xxx.cs, visto in precedenza, per il coded test viene creato

1) un file UIMap.uitest

2) un file RecordedMethods.cs

3) UserControl.cs

UIMap.uitest è in xml e contiene per ogni elemento (ad esempio la lstElementi)

clip_image014

Il file .cs allegato contiene le classi che consentono a questo framework di recuperare i controlli, definire i criteri di ricerca dei controlli andando per derivazione di alcune classi base: ad esempio LstElementWindow del file xml precedente è un classe definità così:

clip_image016

Si deriva da WinWindow restituendo nel Get di LstElementList l’elemento di tipo WinList (ricordiamoci che si tratta di una finestra Windows Form).

Il codice dello unit test usa metodi del file RecordedMethods.cs che rappresentano i metodi da lanciare per eseguire il test: questi metodo sono stati creati durante la registrazione:

clip_image018

Questi metodi rappresentano appunto lo unit test da eseguire. Ecco il codice:

clip_image020

Come si nota viene creata la struttura per cercare, nel nostro esempio, il pulsante denominato Controlla (dove “Controlla” è il Text del pulsante) e su questo viene eseguito un click nel punto 39,13.

Il file UserControls.cs (che immagino verrà poi inserito nelle librerie Microsoft e non all’interno di ogni progetto come adesso) contiene la definizione dei vari controlli utilizzati (WinButton, WinCheckBox) e ne definisce le proprietà visibili (e quindi controllabili dalla mascherina di validazione del Coded UI Test).

Software Diagnostics and QoS

Bug Prevention: trovarli il prima possibile è la nostra missione.

Oggi in VSTS 2008: Code Analysis per trovare qualche defect strutturale o codice scritto male, ancora prima di lanciare il codice. Il Profiler inoltre ci da una mano fin dalle prime fasi. Poi ci sono gli Unit Test che possono intercettare i bug, poi in produzione si può fare tracing. VSTS 2010 prosegue su questa linea aggiungendo due strumenti interni e uno strumento esterno.

VSTS2010 prima feature: Impact Analysis

clip_image022

Quando cambio del codice vedo quali test dovrebbero essere lanciati perchè la modifica impatta il codice testato da questo test. Nella finestra selezionare il team project e la relativa build.

Serve una Build perchè il mapping fra test e metodi sta dentro TFS insieme ai risultati dei test. Il file di mapping server-side viene caricato in VS per controllare questo mapping.

Selezionata la Build e modificando qualche riga di codice, al momento del salvataggio viene controllato l’impatto:

clip_image024

In questo caso la modifica che ho fatto (che ha toccato il metodo Somma) non impatta su nessun test. Questo strumento consente di verificare anche il famoso problema “One Code Line Change”: non preoccupiamoci, sto modificando solo una riga di codice, non posso fare casino J. In questo modo sappiamo a colpo d’occhio quali test dover rilanciare. Esiste poi (almeno l’ho trovata nel prodotto) una check in policy che consente di bloccare un check-in se il codice modificato ha impattato su alcuni test che non sono stati lanciati:

clip_image026

Inoltre la finestra “Test Impact Analysis” indica anche tutti i metodi modificati dall’ultimo check-out: è quindi comodissimo anche per capire cosa ha fatto; sembra una cosa stupida, ma durante lo sviuppo di una parte di codice spesso (almeno a me accade) saltiamo da un metodo all’altro, e spesso siamo interrotti da una mail (la prima regola dello sviluppatore è: tenere posta, messenger, skype, telefonino...SPENTI, ma spesso questa non è la situazione classica in molti uffici). Ritornando sul codice occorre capire dove eravamo, cosa avevamo fatto: la finestrina aiuta in questo !

Secondo strumento: Hystorical Debugger

Serve per tornare al punto esatto per capire cosa abbiamo fatto durante il debugging.

Si può vedere cosa è successo prima del nostro breakpoint o dove abbiamo riscontrato un errore: in pratica si può risalire la call stack per capire cosa è successo precedentemente (cosa è accaduto nel metodo che ha chiamato questo metodo).

Facendo un esempio stupido, ma spero calzante: al click di un pulsante eseguo questo codice:

clip_image028

Sperando che si legga nel post (ingrandire l’immagine cliccandoci sopra), si nota che chiamo la funzione Somma, dal click del mio pulsante, una prima volta con due numeri random (potrebbero essere presi ovviamente da un record nel DB o da un file di testo). Al risultato delle mia funzione devo aggiungere un altro valore (sempre random nel mio semplice caso, ma ancora una volta può arrivare da dove vogliamo).

Al primo breakpoint si vede che a e b sono 1402002823. Ecco lo screenshot, notare la finestra “Locals”

clip_image030

Facendo F5 chiaramente mi rifermo al breakpoint di Somma e i due valori saranno diversi, come si nota sempre dalla finestra “Locals”:

clip_image032

Se mi accorgo di un problema sul valore che mi aspetto, ad oggi in VSTS 2008 è difficile capire quali valori erano stati usati nella prima somma (magari i dati nel DB sono cambiati, o il file di testo ha numeri diversi perchè nel processo è stato aggiornato: controllare quindi i dati di origine diventa complicato se non imposssibile) è ed un casino risalire alla natura del problema e capire perchè sono finito in questa situazione.

L’hystorical debugger consente di risalire lo stack per verificare cosa è successo: nella finestra a destra si vede infatti che il Thread 5936 ha eseguito il metodo Main che ha chiamato il form su cui si è eseguito il metodo cmd_HistoricalDebugger (è il nome del mio pulsante) il quale adesso sta eseguendo il metodo Somma. Fin quì niente di particolare: è una call stack.

Se però faccio doppio click sul metodo cmd_HystoricalDebugger vedo che ho eseguito due volte il metodo somma:

clip_image034

E facendo doppio click sul primo ritorno indietro nella storia e posso analizzare nuovamente il contenuto delle variabili sia nella classiche mascherine Auto/Local/Immediate/Watch o nella finestra in basso a destra:

clip_image036

Come si nota si vedono i valori 1402002823 per a e b del metodo somma che erano i valori passati alla prima chiamata a Somma.

Pensato al caso in cui stiamo debuggando il Business Layer su dati arrivati dal DataAccess Layer: ci accorgiamo nell’esecuzione del codice che qualche dato non è come ci aspettavamo; possiamo tornare su cosa ha fatto il DataAccess Layer per capire come mai ci sono arrivati dati strani, possiamo capire se i metodi che abbiamo richiamato hanno fatto il loro lavoro.

Per abilitare o configurare il tutto dal Tools/Options, nella sezione debug.

clip_image038

Terzo Strumento: Test Activity Center “Camano”

Non occorre VS, è una interfaccia stand-alone con accesso diretto a TFS, che presenta i vari test case per ogni progetto.

L’idea è risolvere il problema “It works on my machine” e il Test RePro ovvero il problema di riprodurre un bug che durante la fase di test si è verificato, ma che non “esce fuori” sulla macchina di sviluppo. Migliora anche la collaborazione fra Tester e Developer: sono presenti vari collector (configurabili) per prendere informazioni sul sistema che viene testato (Action Log, Event Log, System Information, Debug Information, Video Recorder anche !). Quindi quando si lancia un test case vengono registrate tutte queste informazioni.

Il test case è composto da varie attività da eseguire manualmente per fare le verifiche. Ogni attività ha poi il flag per indicare se è andato a buon fine o no.

Questa una idea rispetto al form che abbiamo già visto all’inizio di questo articolo. Lavoriamo senza collegare Requirements o Build per semplicità e per rimanere sul punto senza divagare:

clip_image040

Una volta creato il Test Case, dal Test Suite Manager si organizzano i test case in Test Suite. Ad esempio, ho creato una Test Suite denominata Prove UI Manuali che comprende il test case “Visualizzazione Listbox” creato in precedenza:

clip_image042

Per organizzare meglio il lavoro si organizzano i piani di itest in Test Plan: ecco la creazione del Test Plan “Prove su User Interface Manuali” che comprende la test suite appena creata. Per ogni Plan si indicano i classici Test Run, a cui però in VSTS2010 si associa un nome, una suite e una configurazione.

clip_image044

E’ tutto, possiamo tornare sulla home page del menù testing per lanciare il nostro test e raccogliere i dati. Notare nello screenshot seguente la gerarchia appena creata: un Test Run contiene varie Test Suite che a loro volta contengono i test case:

clip_image046

Quindi da “Prove su User Interface Manuali” che rappresenta il nostro piano di esecuzione dei test, si sceglie la suite e al suo interno il test case.

Prima di fare Run assicuriamoci che la nostra Test Settings contenga le informazioni che desideriamo registrare. Ho abilitato quasi tutto J

clip_image048

A questo punto si lancia il test case e con Automation, si registrano tutte le fasi del manual test. Alla fine del processo si può fare il replay delle cose fatte a mano durante la registrazione. Ci sono molti hook nel CLR dove questi strumenti si agganciano quindi non occorre prevedere degli hook nel nostro codice per fare le prime analisi.

Sto ripercorrendo i primi due step che risultano ok e quindi ho marcato come “Pass”:

clip_image050

Adesso clicco sul pulsante Controlla e marco lo step come Pass, ma visto che ripremendo il pulsante non accade quello che ho segnato come controllo sullo step da effettuare marco l’ultimo step come fallito:

clip_image052

Da questo punto posso poi rivedere la registrazione, ripercorrere gli step e controllare eventuali commenti inseriti (ci sono molte opzioni, come ad esempio l’inserimento di checkpoint, ma in questo articolo introduttivo direi di saltarli per non complicarci la vita).

I risultato vengono pubblicati e sono disponibili i report su quanto abbiamo fatto. Nel nostro semplice caso abbiamo un solo test case, in una sola suite, con una sola configurazione, con un solo Test Plan, quindi il risultato è alquanto banale:

clip_image054

Facendo doppio click sul test fallito si apre la maschera di dettaglio:

clip_image056

E ovviamente cliccando sul video (in basso TC280.wmw) si ripercorre cosa abbiamo fatto.

clip_image058

Nel log (TC280.txt) invece sono presenti queste voci:

clip_image060

Se troviamo un bug, alla creazione del bug su TFS vengono aggiunte tutte le informazioni raccolte.

Ovviamente molte di queste operazioni si possono fare da Visual Studio, o meglio dal Team Explorer, da cui poi si possono rivedere i dati. Entrambi gli strumenti lavorano sullo stesso store:

clip_image062

Direi che anche per questo terzo articolo della serie Visual Studio 2010 ci fermiamo.

Alla prossima.

Links a precedenti articoli:

Intro Parte 1 : http://blogs.devleap.com/articolidevleap/archive/2008/10/17/visual-studio-team-system-2010-primo-contatto.aspx

Intro Parte 2: http://blogs.devleap.com/articolidevleap/archive/2008/10/26/visual-studio-team-system-2010-primo-contatto-parte-2.aspx

Link a .NET 4.0

Workflow Foundation 4.0: http://blogs.devleap.com/articolidevleap/archive/2008/11/17/workflow-foundation-4-0-introduzione-alle-ctp-ottobre-2008.aspx

Dublin: http://blogs.devleap.com/articolidevleap/archive/2008/11/14/dublin-windows-application-server.aspx

Workflow Foundation 4.0: Introduzione alle CTP Ottobre 2008

Autore: Roberto Brunetti - DevLeap  

La prima novità che salta all’occhio è il nuovo designer, fatto in WPF e sicuramente per adesso non definitivo nella forma e colori.

clip_image002

La toolbox presenta molte activity aggiuntive, alcune delle quali tratteremo in questa breve introduzione.

Partiamo con la prima novità che riguarda le custom activity: in questo semplice progetto, come si nota, ho creato un progetto separato “CustomActivities” con due semplicissime activity: ReadLine e WriteLine; la prima ha il compito di leggere gli input dell’utente nel prompt dei comandi e la seconda di eseguire una Console.WriteLine.

Partiamo dalla seconda: per eseguire una WriteLine su Console l’activity ha bisogno del testo da visualizzare; in WF 4.0 è possibile definire i parametri di input e di output delle activity senza la necessità dover creare proprietà pubbliche o dependency property come avviene per la versione.

Questo il codice dell’activity:

clip_image004

E’ stato definito in parametro di input (InArgument), che, sfruttando i generics dichiara che il tipo di parametro è string. L’altra novità rispetto alla versione 3.0/3.5 è la derivazione della nostra activity da WorkflowElement e non da Activity.

L’override del metodo Execute sin dalla versione 3.0 consente di indicare il codice da eseguire durante l’esecuzione dell’attività e riceve come sempre l’AEC (Activity Execution Context); per recuperare il valore del parametro di input si esegure this.Text.Get passando sempre il Context. Visto che il tipo di parametro è string non occorrono altre operazioni per estrarre eventuali proprietà di una classe più complessa che potremmo ricevere.

Stesso ragionamento per il parametro di output dell’activity (anzi del WorkflowElement) ReadLine; ecco il codice:

clip_image006

In questo caso si usa il Set su outArguments passando sempre l’AEC ricevuto.

E’ possibile definire le activity interamente in XAML: ecco un esempio di activity che definisce tre parametri

clip_image008

Costruite le activity (in xaml o in code) siamo quindi pronti per il nostro primo worfklow 4.0 che può utilizzare in sequenza WriteLine e Readline per chiaccherare con l’utente tramite la console.

Ecco il flusso completo

clip_image002[1]

La prima WriteLine deve scrivere a video un messaggio che l’autore del workflow può impostare liberamente. Nelle proprietà dell’activity (come nella versione 3.x) è possibile indicare il valore del parametro direttamente nel designer:

clip_image010

La successiva ReadLine, come abbiamo visto nel codice, espone un parametro di output denominato outArgument che dovrà essere passato come parametro di input alla successiva WriteLine che semplicemente lo visualizzerà nella console.

In WF 3.x questo passaggio di informazioni fra una activity e l’altra era prossibile tramite il binding diretto fra la proprietà di una activity e la proprietà dell’altra activity: il meccanismo è sicuramente semplice ma crea un problema di “scope” ovvero: quando muoiono questa variabili esposte dalle activity ? Visto che teoricamente è possibile fare il binding fra activity presenti nell’intero workflow sequenzale, il runtime del workflow è costretto a tenere in vità questa variabili fino a che non termina l’instanza del workflow, cosa che causa anche un tempo di serializzazione/deserializzazione durante la persistenza del workflow.

In WF 4.0 è possibile creare variabile all’interno di uno scope: ad esempio è possibile definire una variabile a livello di sequence che quindi “nascerà” all’inizio della sequenza e “morirà” alla fine della stessa. Queste variabili sono poi bindable verso i parametri di input e output delle varie activity presenti nello stesso scope, ovvero nella sequence per proseguire con il nostro esempio.

clip_image012

La finestrina si apre dall’apposito pulsante nella toolbar in basso e consente di definire appunto le variabili visibli all’interno dello scope. La finestra mostra anche le variabili disponibili che arrivano da variabili definite nel contenitore dell’activity corrente: ad esempio inserendo una sequnce all’interno del nostro flusso principale (che a sua volta è una sequence) avremmo la visibilità della variabili della sequence padre anche sul figlio. Ecco la figurina esemplificativa:

clip_image014

Come si nota le variabili Destinazione (e Prezzo) sono disponibili anche nella sequence figlia e vengono separate come visualizzazione nella parte alta Variable Available.

Le variabili a livello di scope possono essere bindate ai parametri di input e output della varie activity: nel nostro caso quindi ospitiamo la destinazione del nostro volo nella variabile Destinazione durante la ReadLine e la WriteLine successiva. La Readline avrà il parametro di ouput legato alla Destinazione e la WriteLine avrà il parametro di input legato sempre a Destinazione.

Ecco la Readline:

clip_image016

Per adesso l’editor, in questa primissima CTP di Ottobre 2008, non fornisce nessun aiuto (tipo intellisense e enumeration) delle variabili disponibili.

La WriteLine nel nostro flusso farà esattamente la stessa cosa...o quasi. E’ possibile legare ad un parametro di input (o anche ad una variabile) una espressione: il binding quindi consente non solo di legare un parametro ad un altro, ma anche di eseguire una espressione durante il binding. Ad esempio la nostra WriteLine può contenere questo:

clip_image018

In questo semplice esempio abbiamo lavorato con delle stringhe, ma ovviamente il discorso è valido per altri tipi di dato: questo consente all’autore di workflow di eseguire espressioni (che vengono valutate dal runtime) per assegnare (o anche leggere) valori ai parametri o alle variabili.

Per avviare un workflow (o meglio una istanza di workflow) nel WF 3.0/3.5 occorre avviare il workflow runtime, configurarlo adeguatamente e poi chiedere al workflow runtime di creare e avviare una istanza. Questo il codice di una applicazione console per rimanere in tema:

#region Using directives

using System;

using System.Collections.Generic;

using System.Text;

using System.Threading;

using System.Workflow.Runtime;

using System.Workflow.Runtime.Hosting;

#endregion

namespace _5_CodeCondition

{

class Program

{

static void Main(string[] args)

{

using(WorkflowRuntime workflowRuntime = new WorkflowRuntime())

{

AutoResetEvent waitHandle = new AutoResetEvent(false);

workflowRuntime.WorkflowCompleted += delegate(object sender, WorkflowCompletedEventArgs e)
{waitHandle.Set();};

workflowRuntime.WorkflowTerminated += delegate(object sender, WorkflowTerminatedEventArgs
e)

{

Console.WriteLine(e.Exception.Message);

waitHandle.Set();

};

WorkflowInstance instance =
workflowRuntime.CreateWorkflow(typeof(_5_CodeCondition.Workflow1));

instance.Start();

waitHandle.WaitOne();

}

}

}

}

Ecco il codice 4.0, direi molto più semplice per avviare la sequnce del nostro esempio.

namespace TravelSearchApplication

{

using System;

using System.Linq;

using System.Threading;

using System.WorkflowModel;

using System.WorkflowModel.Activities;

class Program

{

static void Main(string[] args)

{

Sequence s;

AutoResetEvent syncEvent = new AutoResetEvent(false);

WorkflowInstance myInstance = WorkflowInstance.Create(new Sequence1());

myInstance.Completed += delegate(object sender, WorkflowCompletedEventArgs e) { syncEvent.Set(); };

myInstance.Resume();

syncEvent.WaitOne();

}

}

}

Complicando leggermente il nostro esempio supponiamo di voler chiamare un servizio WCF che espone le informazioni sui voli in base alla destinazione scelta. A prescindere da come viene creato il servizio server-side (in WCF 4.0 è possibile definire un servizio interamente in xaml), tramite la ClientOperation possiamo indicare il binding, il contratto e la operation da eseguire:

clip_image020

Nel nostro caso usiamo BasicHttpBinding sull’endpoint localhost:8080/service1.xamlx (come accennavo il servizio è scritto interamente in xaml) utilizzando il contratto Contract1 sulla operazione GetData.

Anche in questo caso è possibile fare binding fra i parametri di ingresso e uscita dell’operation rispetto alla variabili del nostro scope:

clip_image022

Il servizio accetta in input il parametro String denominato Destination a cui è verrà legata la nostra ormai famosa variabile Destinazione e risponderà con un valore Double denominato Prezzo che viene legato alla nostra variabile Prezzo (che verrà poi usata per visualizzare il risultato).

L’ultima activity del nostro workflow non farà altro che passare la variabile Prezzo alla WriteLine con il metodo che ormai conosciamo:

clip_image024

Personalmente non mi è chiara una cosa: abbiamo dovuto usare VB.NET per eseguire l’espressione di conversione del Prezzo (Double) in String. Sulla documentazione attuale e alla Professional Developers Conference 2008 di Losa Angeles è stato indicato che le espressioni si scrivono appunto solo in VB.NET. Non ho capito se l’idea sarà questa anche per la versione finale del prodotto o semplicemente per adesso. My Fault.

Anche se l’articolo è su WF 4.0, vale la pena di dare un occhio a come ho realizzato il servizio server-side: no code ! La descrizione del contratto, delle operation e relativi parametri, nonchè le operazioni da eseguire a fronte di una chiamata sono definite in xaml. Ecco la parte server:

clip_image026

L’activity utilizzata descrivere il contratto è ServiceOperation ed è ospitata nel file service1.xamlx (la x finale indica al motore di esecuzione che si tratta di un servizio attivabile tramite WAS di IIS7).

Per descrivere il contratto (non è la maschera definitiva) si usa questa mascherina:

clip_image028

E scendendo in Edit sull’operazione GetData si vedono i parametri Destination e Prezzo che sonon stati usati nella client operation che abbiamo descritto prima:

clip_image030

E’ possibile indicare il flusso Two-way o One-way e il supporto alle transazioni, oltre ai criteri ovvi di security.

Torniamo a WF 4.0: per calcolare il prezzo, il nostro servizio deve eseguire alcune regole in base alla destinazione. Per questo è stato usato (sempre dentro la definizione xamlx del servizio che rappresenta i passi del workflow da eseguire all’arrivo di una richiesta di GetData) un nuovo componente di WF 4.0 denominato Flowchart.

Espandendo il Flowchart si ottiene questo:

clip_image032

Sul rombo con la X si indica la variabile su cui effettuare il test (nel nostro caso Destination) e ogni braccio del flowchart indica il valore che attiva il braccio stesso: nel nostro caso vediamo che sul primo braccio è indicato Brescia nella finestra delle proprietà:

clip_image034

Altra novità della versione 4.0 è l’activity di Assign che valorizza la variabile Price con un valore (o una espressione). Nel nostro caso quindi il volo su Brescia costra 1.000, mentre il volo su Firenze costa 500; negli altri casi si scatena una eccezione che poi verrà convertita in un SOAP Fault.

Una volta calcolato il prezzo base (1000 o 500 nel nostro caso) il flowchart riunisce il flusso per entrare in un RuleSet: chi conosce i RuleSet di Worklfow 3.x si troverà davanti un editor completamente diverso (e molto più intuitivo) che consente di validare una serie di regole (con sequence, chaining, full chaining e reevalutation).

Il nuovo editor si presenta così, e appunto anche in questa prima CTP è molto più intuitivo dell’attuale:

clip_image036

Le regole possono avere nomi descrittivi, il che aiuta molto nell’individuazione delle regole.

Una volta esplosa (nel senso buono J) una regola ottengo la visualizzazione delle espressioni di valutazione:

clip_image038

Se DiscountLevel = 1 Then DiscountPoint = 0.95. L’assegnazione si effettua sempre tramite l’activity di assign.

Per mettere in produzione il nostro servizio che comprende il workflow ci si può affidare alla feature di Dublin di cui trovate un articolo introduttivo sempre nella sezione articolidevleap del sito blogs.devleap.com.

Ecco l’url completo: http://blogs.devleap.com/articolidevleap/archive/2008/11/14/dublin-windows-application-server.aspx

Per un primo contatto su Visual Studio 2010 trovate i seguenti due articoli:

http://blogs.devleap.com/articolidevleap/archive/2008/10/17/visual-studio-team-system-2010-primo-contatto.aspx

http://blogs.devleap.com/articolidevleap/archive/2008/10/26/visual-studio-team-system-2010-primo-contatto-parte-2.aspx.

Roberto Brunetti

Dublin: Windows Application Server

di Roberto Brunetti 

Questo articolo vuole essere una introduzione sulla prima CTP di Dublin ovvero Windows Application Server, almeno per adesso: il nome in codice Dublin fa riferimento anche ad altri meandri della tecnologia. In questa introduzione vediamo all'opera la parte del Windows Application Server.

E' costruito sopra il .NET Framework 4.0  e integra IIS e SQL Server per fornire una piattaforma per la gestione, la configurazione, lo sviluppo e il deploymnet di soluzioni WF/WCF (4.0 appunto).

Si compone di estensioni per integrare Visual Studio (2010) nel processo di deployment di una soluzione WF/WCF verso l'application server e una serie di strumenti per gestire, localmente o remotamente, la configurazione dell'applicazione ospitata sull'application server.

Attualmente è necessario creare il proprio ambiente di host, sia esso un servizio windows, un Windows Activated Service (WAS) o una applicazione ASP.NET, a mano, ricorrendo a file di configurazione .config, installazione di componenti, configurazione dei binding rispetto alle porte IIS e così via. Il Windows Application Server consente di gestire tutte queste componenti tramite una interfaccia di amministrazione che consente di esportare e importare applicazioni, vedere le istanze in esecuzione, tracciare i dati di tracking di un workflow, analizzare e terminare le istanze fallite.

Iniziamo a capire meglio il tutto partendo da uno screenshot (cliccare sulle immagine per visualizzarle a dimensione originale):

01_IISExtensionWindowsApplicationServer

Siamo sul Default Web Site di IIS Manager (su un Windows Server 2008). Come si nota nella parte in basso delle Features View, l'installazione del Windows Application Server ha aggiunto dei moduli per

1) Esportazione e importazione di applicazioni: come vedremo più avanti è possibile esportare una applicazione, ad esempio dall'ambiente di test e importarla, completa di configurazione, nell'ambiente di produzione

2) Database Configuration: consente di gestire le connessioni verso i database di persistenza, tracking e monitoraggio.

3) Diagnostics: consente di configurare il livello di tracing degli errori e message logging rispetto al tipo di errore nell'applicazione e ai messaggi in ingresso

4) Persisted Instances: visualizza le istanza di workflow persistite nei vari database configurati

5) Tracking Configuration e Profile: consentono di gestire profili di tracking per i worflow in esecuzione (come i Tracking Profile di Workflow 3.0) e associarli al database che dovra accogliere queste informazioni.

Procediamo per gradi, questa la maschera di configurazione dei DB:

01b_DBConfiguration

Come si nota, la mia configurazione prevede un database ProcessServerDB come store di default per le istanze persistite di workflow e un database ProductionStore (che in realtà si appoggia sempre allo stesso database fisico) dove appoggiare le istanze dei workflow persistiti. Sempre nello stesso database fisico vengono appoggiati i dati di monitoring (per il tracking delle istanze di workflow) denominato DefaultMonitoringStore. Nel database fisico troveremo quindi le tabelle adibite al monitoring, tabelle con il prefisso defaultStore per tenere le istanze persistite.

Come è facile intuire i servizi in esecuzione si baseranno sul nome indicato nella colonna Persistence e i dati ad essi associati verranno memorizzati nei (nel caso indicato nell'unico) database fisici configurati.

Accedendo al modulo Services, si accede alla seguente maschera che consente di visualizzare i servizi installati, di cui vengono riepilogati il nome, il path rispetto alla Virtual directory IIS, il nome del site che ospita la virtual directory, e l'application pool in cui gira il servizio.

02_Services

Nel mio caso, ho aperto la maschera dei services, sulla virtual directory ProductionPizzaOrderService e quindi ottengo la lista dei servizi presenti in questa virtual directory.

Aprendo il maschera dal default web site ottengo ovviamente la visualizzazione di tutti i servizi ospitati sul site di default:

02b_Services

Su ogni servizio è possibile visualizzare gli endpoint disponibili. Ecco un esempio della maschera che riepiloga i binding di tutti i servizi.

02c_EndPoint

E' possibile visualizzare le istanze dei workflow in esecuzione, dei workflow andati in errore, dei workflow bloccati, dei workflow sospesi, dei workflow Ready-to-run: nella versione attuale di workflow foundation è possibile accedere ai valori numerici solo dal performance monitor, che non fornisce però i dettagli rispetto ai counter. Con il Windows Application Server, oltre ai counter riepilogativi che si vedono nella seguente immagine

03_PersistedInstanceSummary

è possibile cliccare sui vari contatori per capire a cosa si riferiscono i numeri. Ad esempio cliccando su Instances Running (2 sono le istanze riportate anche come suspended) si accede al dettaglio di ogni istanza:

05_PersitedServiceInstance

Come si nota appare il dettaglio che indica che ci sono due istanze del PizzaOrderService, ospitate dal Default Web Site, sulla virtual directory Production... che stanno girando.

Su ogni istanza è possibile, tramite il tasto destro, sospendere l'instanza, terminarla, abortirla o visualizzare i dati di tracking, ovvero l'elenco dei passi (in base al profilo di tracking scelto) che sono stati eseguiti.

06_PersitedServiceInstanceTerminate

Ad esempio, se sul servizio PizzaOrderService (nella maschera che abbiamo visto prima riguardante i Services) si sceglie Configure Tracking, è possibile abilitare la configurazione di tracking più appropriata: ogni configurazione comprende il riferimento allo store di monitoring (indicando il nome logico come abbiamo visto nello screenshot relativo) e il profilo di tracking (esiste anche nella versione 3.0 il concetto di Tracking Profile) da utilizzare. Il profilo indica quali attività e quali eventi tracciare su ognuna di esse e può essere completamente personalizzato come vedremo più avanti.

07_TrackingConfiguration

Una volta impostata la configurazione di tracking su un certo servizio è possibile, dalla mascherina precedente visualizzare i dati di tracking per ogni istanza: tramite il filtro in alto è possibile scegliere lo stato fra Running, Suspended, Blocked, Ready To Run.

Ecco il risultato:

08_PersitedServiceInstanceTrackingData

Come si può notare per l'instanza selezionata si vedono gli eventi che indicano lo stato (AEC) di ogni activity e il "momento" in cui si sono scatenati.

Attualmente esiste nell'SDK di Workflow 3.x un editor di Tracking Profile, prodotto efficace ma abbastanza rudimentale. Nella prossima versione il Tracking Profile Editor si presenta così (almeno in questa primissima CTP di ottobre 2008):

09_TrackingProfileEditorSample

Il TPE consente di vedere il workflow e indicare quali sono gli eventi da tracciare (la mia freccia rossa indica da dove eseguire questa impostazione) e volendo le variabili (è una novità di workflow 4.0 poter definire variabili a livello di composite activity) da inserire nei dati di tracking.

Come dicevano all'inizio è possibile esportare e importare una applicazione da un server all'altro portandosi dietro la configurazione.

Questa la maschera di esportazione:

10_ApplicationExport

In pratica si sceglie il servizio da esportare e si indica il nome di un file .zip da creare. Il contenuto dello zip è il seguente:

11_ZipContent

Il file applicationManifest.xml contiene le informazioni sull'applicazione, sull'application pool (e relativa configurazione) che la ospita, la virtual directory e il path fisico, oltre ad una serie di informazioni sulle modifiche, il tipo di accesso, i protocolli abilitati e la descrizione di tutti i file (dll, proj, etc) che compongono l'applicazione. Ecco il contenuto facilmente intuibile anche se lunghetto :-). Ho evidenziato i punti salienti.

- <Application xmlns:i="http://www.w3.org/2001/XMLSchema-instance">

<Id>85c8dc43-1161-4188-8324-de5a831f1431</Id>

<LpsVersion>3.0.1341.0</LpsVersion>

- <Modules>

- <Module>

- <ApplicationPool>

<CpuAffinityEnabled>false</CpuAffinityEnabled>

<CpuAffinityMask>4294967295</CpuAffinityMask>

<CpuLimit>0</CpuLimit>

<CpuLimitAction>NoAction</CpuLimitAction>

<CpuLimitInterval>PT5M</CpuLimitInterval>

<FrameworkVersion>v4.0</FrameworkVersion>

<IdentityType>SpecificUser</IdentityType>

<IdleTimeout>PT20M</IdleTimeout>

<ManagedPipelineMode>Integrated</ManagedPipelineMode>

<MaxWorkerProcesses>1</MaxWorkerProcesses>

<Name>User Services Application Pool</Name>

<OverlappedRecycleDisabled>false</OverlappedRecycleDisabled>

<PingEnabled>true</PingEnabled>

<PingMaxResponseTime>PT1M30S</PingMaxResponseTime>

<PingPeriod>PT30S</PingPeriod>

<ProcessOrphaningEnabled>false</ProcessOrphaningEnabled>

<ProcessOrphaningExecutable />

<ProcessOrphaningExecutableParameters />

<QueueLength>1000</QueueLength>

<RapidFailProtectionEnabled>true</RapidFailProtectionEnabled>

<RapidFailProtectionFailureInterval>PT5M</RapidFailProtectionFailureInterval>

<RapidFailProtectionMaxFailures>5</RapidFailProtectionMaxFailures>

<RapidFailProtectionResponseType>HttpLevel</RapidFailProtectionResponseType>

<RapidFailProtectionShutdownExecutable />

<RapidFailProtectionShutdownExecutableParameters />

<RecyclingEventLogs>Time, Memory, PrivateMemory</RecyclingEventLogs>

<RecyclingForConfigurationChangesDisabled>false</RecyclingForConfigurationChangesDisabled>

<RecyclingPrivateMemoryLimit>0</RecyclingPrivateMemoryLimit>

<RecyclingRegularTimeInterval>P1DT5H</RecyclingRegularTimeInterval>

<RecyclingRequestLimit>0</RecyclingRequestLimit>

<RecyclingSpecificTimes xmlns:a="http://schemas.microsoft.com/2003/10/Serialization/Arrays" />

<RecyclingVirtualMemoryLimit>0</RecyclingVirtualMemoryLimit>

<ShutdownTimeLimit>PT1M30S</ShutdownTimeLimit>

<StartAutomatically>true</StartAutomatically>

<StartupTimeLimit>PT1M30S</StartupTimeLimit>

<UserName>Administrator</UserName>

</ApplicationPool>

<Id>6ca9913a-a35a-49a8-8420-2d6d069541be</Id>

<IisVersion>7.0</IisVersion>

<MachineName>ROBDUBLIN</MachineName>

<Name>/PizzaOrderService</Name>

<Technology>WAS</Technology>

<TimeStamp>2008-11-04T13:26:38.8691696-08:00</TimeStamp>

- <VirtualApplication>

<AccessPolicy>513</AccessPolicy>

<AnonymousAccess>true</AnonymousAccess>

<ApplicationPoolName>User Services Application Pool</ApplicationPoolName>

<DirectoryBrowsing>true</DirectoryBrowsing>

<EnabledProtocols>http</EnabledProtocols>

<LogVisits>true</LogVisits>

- <VirtualDirectories>

- <VirtualDirectory>

<Attributes>Normal</Attributes>

- <Directories>

- <Directory>

<Attributes>Normal</Attributes>

<Directories />

<Files />

<FullPath>C:\HandsOnLabs\Lab6\PizzaOrderingApplication\PizzaOrderService\App_Data</FullPath>

<Name>App_Data</Name>

</Directory>

- <Directory>

<Attributes>Normal</Attributes>

<Directories />

- <Files>

- <File>

<Attributes>Archive</Attributes>

<CreationTime>2008-09-23T12:56:08-07:00</CreationTime>

<LastAccessTime>2008-09-23T12:56:08-07:00</LastAccessTime>

<LastWriteTime>2008-09-23T12:56:08-07:00</LastWriteTime>

<Name>PizzaOrderingService.dll</Name>

<PackageKey>PizzaOrderService\bin\PizzaOrderingService.dll</PackageKey>

</File>

- <File>

<Attributes>Archive</Attributes>

<CreationTime>2008-09-23T12:56:08-07:00</CreationTime>

<LastAccessTime>2008-09-23T12:56:08-07:00</LastAccessTime>

<LastWriteTime>2008-09-23T12:56:08-07:00</LastWriteTime>

<Name>PizzaOrderingService.pdb</Name>

<PackageKey>PizzaOrderService\bin\PizzaOrderingService.pdb</PackageKey>

</File>

</Files>

<FullPath>C:\HandsOnLabs\Lab6\PizzaOrderingApplication\PizzaOrderService\bin</FullPath>

<Name>bin</Name>

</Directory>

- <Directory>

<Attributes>Normal</Attributes>

- <Directories>

- <Directory>

<Attributes>Normal</Attributes>

- <Directories>

- <Directory>

<Attributes>Normal</Attributes>

<Directories />

<Files />

<FullPath>C:\HandsOnLabs\Lab6\PizzaOrderingApplication\PizzaOrderService\obj\Debug\TempPE</FullPath>

<Name>TempPE</Name>

</Directory>

</Directories>

- <Files>

- <File>

<Attributes>Archive</Attributes>

<CreationTime>2008-09-23T12:56:10-07:00</CreationTime>

<LastAccessTime>2008-09-23T12:56:10-07:00</LastAccessTime>

<LastWriteTime>2008-09-23T12:56:10-07:00</LastWriteTime>

<Name>PizzaOrderingService.dll</Name>

<PackageKey>PizzaOrderService\obj\Debug\PizzaOrderingService.dll</PackageKey>

</File>

- <File>

<Attributes>Archive</Attributes>

<CreationTime>2008-09-23T12:56:10-07:00</CreationTime>

<LastAccessTime>2008-09-23T12:56:10-07:00</LastAccessTime>

<LastWriteTime>2008-09-23T12:56:10-07:00</LastWriteTime>

<Name>PizzaOrderingService.pdb</Name>

<PackageKey>PizzaOrderService\obj\Debug\PizzaOrderingService.pdb</PackageKey>

</File>

- <File>

<Attributes>Archive</Attributes>

<CreationTime>2008-09-23T12:56:10-07:00</CreationTime>

<LastAccessTime>2008-09-23T12:56:10-07:00</LastAccessTime>

<LastWriteTime>2008-09-23T12:56:10-07:00</LastWriteTime>

<Name>PizzaOrderService.csproj.FileListAbsolute.txt</Name>

<PackageKey>PizzaOrderService\obj\Debug\PizzaOrderService.csproj.FileListAbsolute.txt</PackageKey>

</File>

</Files>

<FullPath>C:\HandsOnLabs\Lab6\PizzaOrderingApplication\PizzaOrderService\obj\Debug</FullPath>

<Name>Debug</Name>

</Directory>

</Directories>

<Files />

<FullPath>C:\HandsOnLabs\Lab6\PizzaOrderingApplication\PizzaOrderService\obj</FullPath>

<Name>obj</Name>

</Directory>

- <Directory>

<Attributes>Normal</Attributes>

<Directories />

- <Files>

- <File>

<Attributes>Normal</Attributes>

<CreationTime>2008-09-02T19:50:26-07:00</CreationTime>

<LastAccessTime>2008-09-02T19:50:26-07:00</LastAccessTime>

<LastWriteTime>2008-09-02T19:50:26-07:00</LastWriteTime>

<Name>AssemblyInfo.cs</Name>

<PackageKey>PizzaOrderService\Properties\AssemblyInfo.cs</PackageKey>

</File>

</Files>

<FullPath>C:\HandsOnLabs\Lab6\PizzaOrderingApplication\PizzaOrderService\Properties</FullPath>

<Name>Properties</Name>

</Directory>

</Directories>

- <Files>

- <File>

<Attributes>Normal</Attributes>

<CreationTime>2008-09-02T19:50:26-07:00</CreationTime>

<LastAccessTime>2008-09-02T19:50:26-07:00</LastAccessTime>

<LastWriteTime>2008-09-02T19:50:26-07:00</LastWriteTime>

<Name>DataContracts.cs</Name>

<PackageKey>PizzaOrderService\DataContracts.cs</PackageKey>

</File>

- <File>

<Attributes>Normal</Attributes>

<CreationTime>2008-09-09T15:33:48-07:00</CreationTime>

<LastAccessTime>2008-09-09T15:33:48-07:00</LastAccessTime>

<LastWriteTime>2008-09-09T15:33:48-07:00</LastWriteTime>

<Name>PizzaOrderService.csproj</Name>

<PackageKey>PizzaOrderService\PizzaOrderService.csproj</PackageKey>

</File>

- <File>

<Attributes>Normal</Attributes>

<CreationTime>2008-09-16T19:17:18-07:00</CreationTime>

<LastAccessTime>2008-09-16T19:17:18-07:00</LastAccessTime>

<LastWriteTime>2008-09-16T19:17:18-07:00</LastWriteTime>

<Name>PizzaOrderService.csproj.user</Name>

<PackageKey>PizzaOrderService\PizzaOrderService.csproj.user</PackageKey>

</File>

- <File>

<Attributes>Normal</Attributes>

<CreationTime>2008-09-16T19:17:18-07:00</CreationTime>

<LastAccessTime>2008-09-16T19:17:18-07:00</LastAccessTime>

<LastWriteTime>2008-09-16T19:17:18-07:00</LastWriteTime>

<Name>PizzaOrderService.suo</Name>

<PackageKey>PizzaOrderService\PizzaOrderService.suo</PackageKey>

</File>

- <File>

<Attributes>Normal</Attributes>

<CreationTime>2008-09-16T19:17:18-07:00</CreationTime>

<LastAccessTime>2008-09-16T19:17:18-07:00</LastAccessTime>

<LastWriteTime>2008-09-16T19:17:18-07:00</LastWriteTime>

<Name>PizzaOrderService.xamlx</Name>

<PackageKey>PizzaOrderService\PizzaOrderService.xamlx</PackageKey>

</File>

- <File>

<Attributes>Archive</Attributes>

<CreationTime>2008-09-23T12:43:42-07:00</CreationTime>

<LastAccessTime>2008-11-04T13:21:57.6702032-08:00</LastAccessTime>

<LastWriteTime>2008-11-04T13:21:57.674216-08:00</LastWriteTime>

<Name>Web.config</Name>

<PackageKey>PizzaOrderService\Web.config</PackageKey>

</File>

</Files>

<FullPath>C:\HandsOnLabs\Lab6\PizzaOrderingApplication\PizzaOrderService</FullPath>

<Name>PizzaOrderService</Name>

<AuthenticationMethod>ClearText</AuthenticationMethod>

<Username />

<VirtualPath>/</VirtualPath>

</VirtualDirectory>

</VirtualDirectories>

<VirtualPath>/PizzaOrderService</VirtualPath>

<WindowsAuthentication>false</WindowsAuthentication>

</VirtualApplication>

- <Website>

<Name>Default Web Site</Name>

</Website>

</Module>

</Modules>

<Name>Default</Name>

</Application>

Oltre questo file viene preparata anche la sottodirectory archiveDir che contiene gli effettivi file che costituiscono l'applicazione.

E' possibile poi importare nuovamente l'applicazione (modificandone alcuni dati per adattarli al server su cui si effettua l'import)

14_ApplicationImport_Edit

Visto che il server di import sarà probabilmente diverso dal server da cui si è esportata l'applicazione è possibile modificare il nome dell'applicazione, l'application pool che la ospita e il path fisico in cui scompattare il file zip e su cui punterò la virtual directory.

In un prossimo articolo vedremo un altro strumento di configurazione, a livello di macchina e i servizi che l'application server espone: XAML Activation, Durable Timer Service, Forwarding Service e andremo più nel dettaglio delle configurazioni.

Roberto Brunetti

Posted: Nov 14 2008, 04:27 PM by rob
Filed under: