Custom Data Type, le annotazioni di struttura delle tabelle

I Custom Data Types (CDT) in Appian utilizzano le annotazioni JPA (Java Persistence API) all’interno della loro definizione XSD (XML Schema Definition) per personalizzare il modo in cui i dati vengono mappati e gestiti in un database relazionale.

Utilizzo delle Annotazioni JPA

Le annotazioni JPA sono essenziali quando si mappa un CDT alle tabelle e colonne del database.

  1. Inclusione nell’XSD: Per utilizzare un’annotazione JPA, essa deve essere inclusa all’interno del tag <xsd:appinfo> che ha l’attributo source impostato su "appian.jpa".
  2. Generazione Automatica: Molte delle configurazioni di base, come la specificazione della chiave primaria (@Id) o la definizione delle relazioni tra tabelle, vengono aggiunte automaticamente da Appian quando si crea un tipo di dati da una tabella di database o quando si definiscono le relazioni nel designer del tipo di dati.

Annotazioni JPA Supportate

Appian supporta una vasta gamma di annotazioni JPA per la mappatura e la definizione del comportamento del database:

CategoriaAnnotazioni Supportate
Mappatura Base@Table, @SecondaryTable, @SecondaryTables, @Column (con attributi a seconda dell’esigenza).
Chiavi@Id, @GeneratedValue.
Relazioni@JoinColumn, @JoinTable, @ManyToOne, @OneToOne, @OneToMany, @ManyToMany.
Ereditarietà/Override@Inheritance, @PrimaryKeyJoinColumn, @MappedSuperclass, @AttributeOverride.
Altro@Transient, @Version, @Basic, @Lob, @OrderBy, @UniqueConstraint, @SequenceGenerator.

Le annotazioni più utili per la definizione di tabelle

In questo paragrafo facciamo una carrellata delle annotazioni strutturali che ritengo più utili per la modellazione delle tabelle. Le annotazioni JPA devono essere impostate prima di pubblicare il data store. Appian legge le annotazioni JPA solo quando crea una tabella o una colonna; le colonne esistenti non vengono mai aggiornate in base alle annotazioni JPA.

L’annotazione @Table, @Id

Possiamo utilizzare l’annotazione @Table per indicare il nome della tabella da utilizzare, bypassando le operazioni per limitare a 27 caratteri i nomi delle tabelle e colonne del database.

<xsd:annotation>
<xsd:appinfo source="appian.jpa">@Table(name="Persona")</xsd:appinfo>
</xsd:annotation>

Colgo l’occasione per fornirvi un piccolo tool per calcolare il nome generato da un nome che già supera i 27 caratteri.

Per indicare una colonna come chiave primaria possiamo usare l’annotazione @id e per abilitare l’auto increment possiamo usare @GeneratedValue

<xsd:element name="id" nillable="true" type="xsd:int">
<xsd:annotation>
<xsd:appinfo source="appian.jpa">@Id @GeneratedValue</xsd:appinfo>
</xsd:annotation>
</xsd:element>


Nota bene: se non esiste un campo id con chiave primaria, appian creerà una colonna a_id con tali caratteristiche .

le annotazioni @Lob e @Column

Quando lavoriamo con JPA, di solito i campi delle entità vengono mappati su colonne “normali” del database, come VARCHAR per le stringhe o INTEGER per i numeri. Questo funziona bene finché i dati hanno dimensioni contenute. Ma cosa succede se dobbiamo memorizzare un testo molto lungo, come un articolo, una descrizione dettagliata, oppure un file binario come un PDF o un’immagine? In questi casi, i tipi standard non bastano: il database ha bisogno di gestire i dati come Large Object, cioè oggetti di grandi dimensioni.

Ecco un esempio di lob:
<xsd:element name="fileContent" nillable="true" type="xsd:string">
<xsd:annotation>
<xsd:appinfo source="appian.jpa">@Lob</xsd:appinfo>
</xsd:annotation>
</xsd:element>

Con questa configurazione, con un database MariaDB verrà creato un campo di tipo “LongText”, per ottenere un “MediumText” bisogna:
<xsd:element name="mediumtext" nillable="true" type="xsd:string">
<xsd:annotation>
<xsd:appinfo source="appian.jpa">@Column(columnDefinition="mediumtext")/xsd:appinfo>
</xsd:annotation>
</xsd:element>

Qui vediamo l’utilizzo per personalizzare l’annotazione @Column che permette di impostare anche il nome e la dimensione della colonna, ad esempio:
<xsd:element name="name" nillable="true" type="xsd:string">
<xsd:annotation>
<xsd:appinfo source="appian.jpa">@Column(length=200,name="NOME")</xsd:appinfo>
</xsd:annotation>
</xsd:element>

Questo ci permette di indicare la colonna sul database con nome “Nome” e lunghezza 200.

L’annotazione @Transient

L’annotazione @Transient è una delle annotazioni JPA (Java Persistence API) supportate da Appian per la definizione dei Custom Data Types (CDT).

Nel contesto dei CDT utilizzati per il mapping a un database relazionale tramite Data Stores, l’annotazione @Transient serve a specificare che un campo definito nell’XSD del CDT non deve essere persistito (memorizzato) nel database.

In sostanza, un campo marcato con @Transient fa parte della struttura dati logica (il CDT) all’interno di Appian, ma viene ignorato dal meccanismo di persistenza di Appian quando scrive o legge i dati dal database sottostante.

Questa annotazione è utile per i campi che contengono dati temporanei, calcolati, o informazioni che sono rilevanti solo all’interno del processo o dell’interfaccia di Appian, ma non devono avere una colonna corrispondente nella tabella del database.

Ecco un esempio:
<xsd:element name="ignora" nillable="true" type="xsd:string">
<xsd:annotation>
<xsd:appinfo source="appian.jpa">@Transient</xsd:appinfo>
</xsd:annotation>
</xsd:element>

L’annotazione per il locking ottimistico: @Version

Abbiamo già visto questa annotazione quando abbiamo affrontato l’argomento delle strategie di locking da adottare alla scrittura di un record, ecco un esempio:

<xsd:element name="versionNumber" nillable="true" type="xsd:int">
    <xsd:annotation>
        <xsd:appinfo source="appian.jpa">@Version</xsd:appinfo>
    </xsd:annotation>
</xsd:element>

Nota bene: il campo deve essere intero.

Conclusioni

I Custom Data Types di Appian, arricchiti dalle annotazioni JPA, offrono agli sviluppatori un modo semplice e potente per modellare i dati in un database. Con poche regole chiare – come l’uso di @Table per nominare le tabelle, @Id per definire la chiave primaria o @Lob per gestire testi e file di grandi dimensioni – è possibile rendere le applicazioni più flessibili e aderenti alle esigenze reali. Queste annotazioni permettono di impostare oculatamente le opzioni desiderate.

Riferimenti

https://docs.appian.com/suite/help/25.4/Supported_XSD_Elements_and_JPA_Annotations.html

https://docs.appian.com/suite/help/25.4/cdt_design_guidance.html

Appian nel segno della continuità con la versione 25.04

Con l’arrivo di Santa Claus imminente immancabile arriva anche la nuova versione della piattaforma, tanta IA ovviamente intercalate nel BPM low code, vediamo in questo articolo le novità principali che toccano gli sviluppatori. Su tutto viene introdotto Agent Studio, oltre a seguire il cammino di Process HQ.

Ecco Agent Studio

Appian ha introdotto una nuova funzionalità chiamata Agent Studio, la cui funzione principale è di automatizzare processi complessi e orientati agli obiettivi utilizzando agenti di intelligenza artificiale. L’idea di fondo è di esprimere gli obiettivi da raggiungere in linguaggio naturale e far sì che gli agenti AI costruiscano le basi dell’applicazione da realizzare.

Gli agenti sfruttano il data fabric e gli oggetti di design. Dal punto di vista pratico, un agente AI è un oggetto low‑code che si comporta come un collaboratore digitale: interpreta le istruzioni, ragiona sulle alternative e sceglie le azioni più adatte per arrivare al risultato. È un modo diverso di pensare all’automazione, più vicino al linguaggio delle persone che alla logica dei programmatori.

A differenza delle AI Skills, che automatizzano singole attività, gli agenti orchestrano interi processi, adattandosi a situazioni complesse e non prevedibili.

Gli agenti si prestano molto bene per la prototipazione rapida:

  • consentono di passare da un’idea a un’automazione completamente funzionante in poche ore.
  • Sono individuati nella piattaforma come degli oggetti lowcode, come gli smart services, che forniscono un output dopo aver completato il task.

Sviluppo rapido: Appian Composer

Avete mai sentito parlare di Vibe Coding? sicuramente.
Fermo restando la mia volontà di ritornare sul tema con un approfondimento specifico, introduciamo Appian Composer che in questa release diventa generalmente disponibile: con questo tool abbiamo un approccio più strutturato di un semplice “query to code”, ma bensì un “query to plan”.

La funzionalità che permette a partire da un documento dei requisiti in un piano applicativo dettagliato, in cui vengono proposti i possibili processi e le possibili rule da utilizzare con un supporto alla creazione rapida e generazione in un click. Il tab workflow offrirà una panoramica visiva del flusso applicativo, mostrando la sequenza delle attività e dei passaggi come un diagramma interattivo:

Con questo tool salutiamo anche il Quick Apps Designer, il Composer è l’unico modo per costruire nuove applicazioni rapidamente.

Process HQ

  • Creazione immediata di report personalizzati con comandi in linguaggio naturale e anteprima grafica in chat.
  • Disponibilità di AI Copilot direttamente nella home page di Process HQ per esplorare dati e generare report.
  • Controllo centralizzato: gli amministratori possono attivare o disattivare tutte le funzionalità AI (chat, KPI, viste) con un solo clic dalla Console di Amministrazione.
  • I report di Process HQ possono esser inseriti nelle distribuzioni.
  • Gli oggetti report ora possono puntare ai report di Process HQ.

Sviluppo

Il lato sviluppo della piattaforma riflette molti aspetti che abbiamo visto come novità.

  • È stata introdotta la rule a!genAiModels per scoprire i modelli AI generativi disponibili.
  • Lo smart service Execute Generative AI Skill supporta la selezione del modello runtime, che sovrascrive il modello configurato nell’AI skill.
  • È stato introdotto un nuovo componente di campo chat, utilizzabile mediante a!chatField che per impostazione predefinita utilizza il modello linguistico scelto da Appian tramite la funzione a!callLanguageModel, ma consente anche di collegare un modello AI personalizzato.
  • Gli header dei record possono essere disabilitati con un flag “Non mostrare le intestazioni dei record” (do not Show record headers) per aver un maggiore controllo sull’aspetto di questa interfaccia.
  • La rule a!fileUpload si arricchisce con il parametro documentActions che permette di scegliere un’azione da effettuare dopo aver effettuato l’upload, come il rinomina o vedere una preview.
  • a!documentViewerField supporta la navigazione diretta e l’evidenziazione del testo.
  • Introdotta la funzionalità di “skip degli errori della sincronizzazione dei record” (“Skip failed smart service syncs”) che permette di ignorare i fallimenti delle sincronizzazioni lasciando l’accessibilità ai record.
  • UUID Oggetto: L’UUID dell’oggetto è ora visualizzabile nella finestra di dialogo delle proprietà di tutti gli oggetti, ad eccezione di AI skills, robotic tasks e robot pools.

Conclusioni

In questo articolo ho evidenziato le novità più significative di Appian, che confermano la continuità nell’integrazione dell’intelligenza artificiale. La nuova release introduce l’Agent Studio, corredato da numerosi componenti per lo sviluppo, accelera la pianificazione delle attività e la realizzazione tramite Appian Composer, arricchisce Process HQ con un collegamento diretto alle funzionalità applicative e offre diversi miglioramenti pensati per semplificare l’uso dell’AI generativa. Tutte queste innovazioni sono orientate a supportare sviluppatori e clienti nel raggiungimento dei loro obiettivi di business, aumentando al contempo l’efficienza delle attività.

Riferimenti

https://docs.appian.com/suite/help/25.4/Appian_Release_Notes.html

https://docs.appian.com/suite/help/25.4/Chat_Component.html

https://youtu.be/HabxZBSTFt8?list=TLGGuhpgEuzDg7syMzExMjAyNQ

I tipi di dati complessi in Appian

In questo articolo discutiamo le tipologie di dati complessi in Appian: un altro mattoncino basilare per sviluppare su piattaforma.

Appian permette una definizione di struttura dati rigida mediante i Custom Data Types mentre una definizione più flessibile mediante i dictionary o le map.

I Custom Data Types (CDT)

I CDT in Appian sono oggetti che permettono di definire strutture dati che rappresentano raggruppamenti logici di informazioni correlate.

Con i CDT in Appian possiamo:

  • Definizione di modelli dati complessi riutilizzabili in varie applicazioni e processi.
  • Facilitazione dell’integrazione con sistemi esterni attraverso il mapping automatico dei campi dati.
  • Supporto alla conservazione e recupero di dati strutturati.
  • Utilizzo nei moduli dinamici, per creare form che si adattano ai dati sottostanti.

I CDT sono una componente essenziale per costruire applicazioni robuste e scalabili in Appian, migliorando la gestione e l’integrazione dei dati per l’automazione dei processi di business.

Possiamo vedere i CDT come una forma “forte” di tipo per un oggetto, ma Appian prevede diverse forme di struttura che contengono informazioni, ovvero :

  • CDT
  • Dictionary
  • Map

Dictionary e Map

I dictionary e le map (come i CDT) sono oggetti che permettono di rappresentare un insieme di oggetti strutturati come chiave-valore che possono essere visti come una collezione di elementi dove ogni chiave è associata a un valore unico.

Ecco un esempio di codice per ottenere una mappa di oggetti:

load(
  local!mappa: a!map(
  chiave1: "Valore 1",
  chiave2: "Valore 2",
  chiave3: 123,
  chiave4: true
  /* a seguire è un dizionario*/
  {
     /*Accesso ai valori */
    local!mappa.chiave1,   /* restituisce il valore 1 */
    local!mappa.chiave3,   /* restituisce 123 */
   }
)

Il dizionario si ottiene direttamente da SAIL con la sintassi {…} (un esempio è /* a seguire è un dizionario*/) mentre per la map bisogna utilizzare la funzione a!map. Considera inoltre che i modelli di processo non supportano il tipo Dictionary.

Le differenze

La differenza principale tra un dictionary e le map è che all’interno di una map, ogni chiave è associata a un valore e il tipo di quel valore viene mantenuto. I valori memorizzati in una mappa non vengono incapsulati in varianti, mentre un dictionary incapsula ogni valore in un tipo AnyType (variant): di conseguenza con le map non bisogna preoccuparsi del type casting.

Il dizionario si ottiene direttamente da SAIL con la sintassi {…} mentre per la map bisogna utilizzare la funzione a!map. Considera inoltre che i modelli di processo non supportano il tipo Dictionary.

Riferimenti

Appian Community

Map Appian 25.03

Appian, le novità della versione 25.03

Ormai le vacanze sono agli sgoccioli e puntuale come ogni anno ecco la terza iterazione delle nuove funzionalità, come di consueto in questa serie di articoli, introduciamo le novità principali di di Appian 25.03, focalizzandoci su alcuni aspetti relativamente allo sviluppo su piattaforma.

AI Copilot per utenti business

In questa nuova versione Copilot restituirà risultati più pertinenti grazie alla miglior comprensione del significato delle query, non solo delle parole chiave, tale novità si rifletterà anche sul chatbot su Data Fabric. La ricerca intelligente mostrerà fino a 10 corrispondenze semantiche, anche su campi testuali lunghi, con messaggi di errore più chiari e notifiche in caso di problemi di indicizzazione.

AI Agents

Per la skill AI Advanced IDP Tools vengono introdotte le query di ricerca per estrarre dati aggiuntivi dai documenti, rispondendo a domande e indicando i riferimenti.

Le skill AI generative ora supportano file JPEG, PNG e TIFF (inclusi TIFF multipagina) come input di documenti, senza bisogno di conversioni o plugin. Anche la skill AI Advanced IDP Tools supporta file TIFF multipagina.

È possibile aggiornare dinamicamente il prompt utilizzato dallo smart service Execute Generative AI Skill direttamente nel modello di processo. Questo permette di adattare l’input per casi d’uso specifici limitando copie superflue.

Process HQ

Per Process HQ la novità di rilievo è l’Integrazione nel Sito Appian: è possibile aggiungere la Libreria di Report e Dashboard di Process HQ come pagina dedicata all’interno di un sito Appian, consentendo agli utenti di accedere e gestire report e dashboard direttamente dal sito.

Gli altri miglioramenti di rilievo sono:

  • La possibilità di configurare report con funzionalità di drill-down, permettendo di collegare diverse visualizzazioni dei dati e accedere a informazioni più dettagliate direttamente, inclusi link diretti alla vista riepilogativa del record.
  • La creazione di report è stata integrata direttamente nella pagina Report e Dashboard di Process HQ, senza uscire dal site.
  • Si può contrassegnare come preferiti report e dashboard per un accesso rapido. Ogni report/dashboard si apre automaticamente in una propria scheda.
  • Nuove metriche di processo per gli eventi record che calcolano la durata dei processi attraverso campi record personalizzati. Queste metriche possono essere monitorate e visualizzate tramite Process HQ.

Data Fabric

Per Data Fabric sono stati apportati miglioramenti nella sincronizzazione dei dati, gestione documentale e performance delle query. In particolare sono state introdotte le sincronizzazioni incrementali: permettono di pianificare (anche per tipi di record basati su database) un sottoinsieme di record consentendo aggiornamenti frequenti (minuti/ore) e la flessibilità di programmare anche sincronizzazioni complete (di solito in orari off business).

Segnalo anche questi miglioramenti :

  • Monitoraggio avanzato delle query: La scheda “Query Performance” ora mostra il numero di campi inclusi in ogni query; sono stati migliorati i log sail_details.csv e introdotto interface_query_record_type_details.csv.
  • Interrogazione di più dati correlati: La funzione a!relatedRecordData() può ora recuperare più dati da relazioni uno-a-molti (fino a 100 record in a!queryRecordType()/griglie, fino a 250 in a!queryRecordByIdentifier())

Interfacce

Per creare interfacce con migliori prestazioni è stata introdotta la funzione a!asyncVariable() per caricare dati in modo asincrono.
Per la gestione dei documenti è stato introdotto l’upload con preview, inoltre viene generata una summary view e la visualizzazione dei documenti correlati.

Anche sul fronte accessibilità sono stati fatti dei passi in avanti, migliorando le griglie editabili, i colori preconfigurati con contrasto ottimizzato.

Le altre novità di rilievo sono le seguenti:

  • un nuovo banner accessibile per i messaggi
  • Un nuovo template sidebar per i layout di tipo “wizards”
  • la possibilità di scrollare i pannelli all’interno dei form layouts
  • Il parametro “visibility” è stato rinominato in “Show When”.

Record Action

Per le record actions è possibile includere sia tipi di record correlati one-to-one che one-to-many. Per le relazioni one-to-many, Appian genera automaticamente una griglia editabile nell’interfaccia dell’azione record.

Conclusioni

In questa terza iterazione della versione 25 di Appian, viene ulteriormente potenziata la sezione IA, tenendo sempre il focus verso le prestazioni con alcuni accorgimenti anche facile da introdurre su progetti esistenti.

L’evoluzione del Data Fabric mostra una chiara volontà di rendere il dato più fluido e governabile, mentre le nuove Interfaces offrono strumenti di design più potenti e inclusivi, capaci di migliorare l’esperienza utente su ogni dispositivo. Le funzionalità di AI e automazione continuano a crescere, semplificando attività ripetitive e suggerendo soluzioni intelligenti in tempo reale.

Riferimenti

https://docs.appian.com/suite/help/25.3/Appian_Release_Notes.html

Appian 25.3 Release Highlights (Guarda il video su YouTube)

25.3 Webinar di annuncio del prodotto (Guarda il video su YouTube)

Appian, un primo approccio alle distribuzioni

Scopriamo assieme le distribuzioni di appian, focalizzandoci sulle distribuzioni manuali

In Appian, le distribuzioni (o “deployments” in inglese) rappresentano il processo di trasferimento delle modifiche da un ambiente sorgente a un ambiente di destinazione. Questo processo è fondamentale per spostare applicazioni, configurazioni e dati tra diverse fasi del ciclo di sviluppo, come da sviluppo a test o da test a produzione.

Tra le tematiche per chi arriva da contesti “classici” ed approccia Appian rimane piacevolmente sorpreso dalla semplicità di utilizzo delle distribuzioni, successivamente si rimane anche piacevolmente sorpresi dalle possibili modalità.

In particolare esistono tre metodi principali per distribuire un pacchetto in Appian:

1. Confronta e Distribuisci (Diretto): Questo è il metodo più semplice e consigliato. Permette di distribuire applicazioni, pacchetti, plugin e script di database direttamente tra ambienti connessi tramite passaggi guidati. Richiede la configurazione di ambienti connessi.

2. Tramite API (Esterno): Consente di personalizzare il processo di distribuzione tramite le API native di Appian, utilizzabili con strumenti esterni come Jenkins.

3. Esporta e Importa (Manuale): Implica l’esportazione manuale di un pacchetto o un’applicazione dall’ambiente sorgente e la successiva importazione nell’ambiente di destinazione. È consigliato se non si dispone di ambienti connessi.

Il metodo Confronta e Distribuisci

Il metodo Confronta e Distribuisci è la modalità più assistita che mette a disposizione Appian, consente dopo aver creato il pacchetto di confrontarlo con l’ambiente (connesso) e quindi verificare anzitempo eventuali dipendenze mancanti e/o errori di sorta.

Con questo metodo non ci sono file e operazioni manuali: il tutto avviene all’interno della piattaforma guidato. La piattaforma controlla se ci sono errori o conflitti. Se qualcosa va storto, è possibile tornare indietro facilmente grazie alla funzione di rollback.

Il metodo Manuale

Questo metodo è di fatto risulta l’unica opzione per chi lavora su ambienti disconnessi senza usare strumenti di DevOps.

Il contro più importante di utilizzare questa modalità è che ci costringe a dover “verificare”, meglio conosciuta come ispezione, dei pacchetti solo al momento in cui bisogna installare.

Gli esiti di una ispezione sono i seguenti:

  • Nessun errore, in tal caso è possibile installare il pacchetto
  • Avvisi, possiamo installare ma non è assicurato l’uniformità dell’installazione
  • Errori, non potremo installare il pacchetto.

Sia che ci sia un Avviso, sia che ci siano errori, consiglio sempre di non distribuire a meno che non si effettui una review puntuale sugli oggetti.

In presenza di errori, ignorare non è una possibilità: significa compromettere la distribuzione, che potrebbe risultare incompleta o addirittura fallire del tutto. Gli errori di distribuzione indicano che uno o più oggetti nel pacchetto non sono definiti correttamente, e Appian non è in grado di importarli nell’ambiente di destinazione. Se non vengono corretti, si rischia di incorrere in errori di runtime una volta che l’applicazione è in uso.

Le cause più comuni sono riferimenti a oggetti che sono stati eliminati oppure l’uso di campi di tipo record non validi. Per aiutare a risolverli, Appian fornisce una griglia dedicata agli errori di distribuzione, dove ogni voce è accompagnata da un link diretto all’oggetto problematico e da una descrizione dettagliata nella colonna “Problemi”. Questo consente di individuare rapidamente la causa e intervenire in modo mirato.

Esempio ispezione in errore

Il metodo programmatico: le WebApi

Le WebApi permettono di usare fondamentalmente le funzionalità disponibili per le distribuzioni manuali mediante appunto delle WebApi Rest, che hanno il vantaggio che possono esser integrate in un ciclo DevOps.

Questa modalità rispetto al metodo Confronta e Distribuisci chiaramente perde il confronto diretto tra ambienti sorgente e destinazione, ma rispetto al metodo manuale guadagna la possibilità di creare script che rendono il processo di distribuzione automatizzabile e ripetibile.

Conclusioni

La conclusione di questo primo approccio alle distribuzioni è che in generale le distribuzioni si effettuano sempre allo stesso modo:

  1. Confronto tra il pacchetto e l’applicazione target, disponibile solo per i metodi Confronta e Distribuisci.
  2. Verifica del pacchetto inviato (valido per tutti i metodi)
  3. Installazione del pacchetto (valido per tutti i metodi)

Il vantaggio dei metodi 1 e 2 sono innegabili e per chi usa il terzo metodo… proviamo e riproviamo le installazioni delle nostre distribuzioni.

Riferimenti

https://docs.appian.com/suite/help/25.2/inspect-deployment-packages.html

https://docs.appian.com/suite/help/25.2/Deploy_to_Target_Environments.html

Appian, un BPM votato alla Robotic Process Automation

Vediamo le caratteristiche essenziali di Appian, un BPM che sta’ guadagnando sempre più fette di mercato.

Appian è un software per la modellazione di processi di business. Tra le funzionalità più pubblicizzate abbiamo tempi di sviluppi molto rapidi. Appian come piattaforma si divide in più ambienti:

  • Tempo
  • Designer
  • Console di amministrazione
  • Sito

Tutte queste funzionalità sono fruibili tramite browser, quindi lo sviluppatore non dovrà installare nulla sulla propria macchina.

In questo primissimo articolo partiremo dalla componente base di appian: il custom data type, che altro non è che il modo di rappresentare di appian delle entità e poi daremo un brevissimo escursus sul linguaggio di scripting utilizzato.

I Custom Data Type (CDT)

I Custom Data Type rappresentano le entità di base di Appian, descrivono oggetti d’interesse della piattaforma.
Nei custom datatype dobbiamo mettere tutto ciò che ci occorre per descrivere l’entità, ad esempio, se vogliamo descrivere l’oggetto persona semplicemente possiamo usare: il nome e il cognome come testo, mentre la data di nascita come data.

I CDT possono essere innestati fra loro ovvero possono contenere un altro CDT al suo interno, ad esempio possiamo considerare la città di nascita un oggetto di tipo Citta, che potrebbe contenere al suo interno Nome, Regione e Provincia di appartenenza e altro.  I CDT possono avere anche particolari campi che possono essere un elenco di informazioni come ad esempio un elenco di numeri di telefono.

Appian è lowcode: il linguaggio

Appian ha un suo linguaggio proprietario, il cui nome non viene mai specificato e che informalmente chiameremo SAIL. Tra le caratteristiche principali abbiamo:

  • È un linguaggio Funzionale
  • È debolmente tipato
  • È utilizzato sia per la costruzione di rule che di interfacce
  • Non ha un sistema di cattura e gestione delle eccezioni

SAIL, nella visione di Appian, è un linguaggio semplice da imparare che permette allo sviluppatore di avere la sufficiente espressività per automatizzare i processi di business.

SAIL possiede, in sintesi, le seguenti tipologie di funzioni:

  • Costrutti dichiarativi
  • Le variabili e i dizionari
  • I costrutti condizionali e di selezione
  • I costrutti ciclici
  • Funzioni di sistema e funzioni di custom

Nel proseguo dei nostri articoli vedremo nel dettaglio tutte queste tipologie di funzioni.

Appian è attenta al mondo RPA

Appian è particolarmente attenta alla Robotic Process Automation. In particolar modo, il BPM è integrato con l’applicativo RPA di Blueprism. Tuttavia in futuro verrà estesa l’integrazione anche verso l’altro competitor di mercato: UiPath.

L’integrazione in particolar modo rende semplice l’invocazione dei robot all’interno di un modello di processo: la chiamata viene effettuata con uno Smartservice che conserverà al suo interno tutti i dati per effettuare la chiamata, al pari di una integrazione verso un server mail o altri servizi della piattaforma. Vedremo in futuro tale integrazione.

Programmazione SAS, i dataset

In questo articolo approfondiamo la struttura base per l’elaborazione dei dati in SAS.

Principi base

Il dataset, molto semplicemente, è una struttura che contiene i dati da elaborare o mostrare.
I dataset all’interno di SAS vengono custoditi in librerie che raccolgono un insieme di dataset.
la libreria base di SAS si chiama work bisogna considerare tale libreria come se lavorasse esclusivamente in memoria: se avviene un riavvio (o anche un fail) del sistema il contenuto di tale libreria verrà azzerato. L’esplicitazione della libreria work è facoltativa.

La dichiarazione più semplice di un dataset è la seguente:

Data targetdataset;
set work.mydataset;
run;

Il dataset “targetdataset” ora punterà al valore presente nel dataset “mydataset”, ricordiamo che entrambi i dataset sono contenuti nella libreria work.

Caricamento dataset da codice

Il dataset può essere caricato mediante file, mediante database o in altri modi, in questo contesto ci limitiamo a mostrare il caricamento del dataset da codice utilizzando l’istruzione datalines:

data weight2;
input IDnumber $ Week1 Week16;
WeightLoss2=Week1-Week16;
datalines;
2477 195 163
2431 220 198
2456 173 155
2412 135 116
;

L’istruzione input ci permette di definire quali colonne verranno lette tramite l’istruzione datalines.
Utilizzare colonne in questo caso è improprio: essendo SAS nato per la statistica pertanto è più pertinente chiamare le colonne variabili.

Il simbolo del $ permette di dire che la variabile “IDnumber” è una  stringa, mentre Week1 e Week16 sono da considerarsi numeri.

Tra l’istruzione datalines e il ; sono presenti l’insieme delle tuple presenti nel dataset.

Anche utilizzare tuple in questo caso è improprio: è pertinente chiamare le tuple osservazioni.

Logica dei dataset

Bisogna considerare che SAS all’atto della definizione di un dataset ne effettua una vera e propria costruzione ciclica. In particolare utilizzando l’istruzione data si crea un ciclo per cui ad ogni elemento in input viene creato un elemento nel dataset. Ad esempio:

Data targetdataset;
retain acc;
  set work.mydataset;
  acc = acc + col;
run;

Nel dataset targetdataset per ogni occorrenza presente in mydataset verrà aggiunta alla variabile acc il valore di col. L’istruzione retain permette di conservare il valore di acc attraverso le iterazioni.

Selezionare le variabili

Si possono proiettare dal dataset le variabili utilizzando l’istruzione drop. Per il mondo database è come indicare le colonne nella clausola “select”.

Esempio:

Data targetdataset;
  set work.mydataset;
  drop mywork;
run;

Nel dataset targetdataset verrà eliminata la variabile mywork che tuttavia resterà nel dataset mydataset.

L’istruzione keep è il duale dell’opzione drop poiché con la stessa sintassi permette di mantenere nell’esempio precedente solo la variabile mywork.

Selezionare le osservazioni

Per selezionare le osservazioni dal dataset si utilizza, mutuandola dal mondo database l’opzione where.  Anche la sintassi in questo caso è mutuata dal mondo database.

Data targetdataset;
  set work.mydataset;
  where country=’av’ and job_title contains ‘rep’;
run;

Nel caso in cui un valore della variabile è missing, l’intera esprezione risulterà missing. Questo ha un forte impatto sopratutto nelle espressioni aritmetiche poiché laddove si potrebbe pensare a una valorizzazione con 0, l’esistenza di missing dev’essere considerata attentamente.

Missing è simile al “null” del paradigma Object Oriented, ma molto diverso da 0. Ad esempio:

Data targetdataset;
set work.mydataset;
c=a+b;
where c > 0;
run;

Se a =4  e b è missing il valore di c sarà missing. La where non prenderà mai in considerazione l’osservazione e non la mostrerà a video.

Serializzazione in C#

In questo articolo illustro le basi della serializzazione in C Sharp

La serializzazione è quel processo per cui si vuole che delle istanze di un determinato Tipo possano essere trasformate in oggetti manipolabili dalla CRL mediante strutture generiche come stringhe e file. Ad esempio, consideriamo come tipo serializzabile la classe “Persona” una sua istanza, ovvero un oggetto di questo tipo, può esser serializzata per i scopi più vari, ad esempio:

  1. La trasmissione di una istanza ad un server o ad un client sulla rete
  2. La persistenza di una istanza su file system
    e così via…

La serializzazione avviene considerando un set di componenti base da utilizzare i quali sono : un serializzatore e una fonte di input dei dati. La fonte dei dati può essere: uno stream o una stringa. Il  serializzatore ci permette di definire il tipo di serializzazione che vogliamo usare nel nostro software: i tipi più famosi di serializzazione sono Binario, XML e JSON.

In C# abbiamo due “modi di pensare” della serializzazione, in sostanza, si può decidere di definire un contratto chiamato DataContract oppure di serializzare integralmente l’oggetto, annotando solo le variazioni richieste.

DataContract

La classe DataContract permette di serializzare le classi mediante “un contratto” da definire all’interno delle stesse.
La definizione delle classi avviene mediante annotazioni, indipendentemente dalla visibilità degli elementi annotati, in particolare si usano:

  1. L’annotazione DataContract, che definisce il contratto di serializzazione dell’oggetto in elementi atomici
  2. L’annotazione DataMember che definisce un elemento della serializzazione.

A seguire un esempio di classe annotata mediante DataContract.

[csharp]
[DataContract(Name = "Persona", Namespace = "http://www.persona.com")]
class Persona
{
[DataMember(Order =1, Name ="NomeDiBattesimo")]
public string nome {
get { return _nome; }
set { _nome = value; }
}
private string _nome;

[DataMember(Order = 2,IsRequired = false,Name ="Cognome", EmitDefaultValue = true)]
public string cognome
{
get;
set;
}
[/csharp]

L’attributo DataContract può avere le seguenti opzioni:

  • Name : Attribuisce un nome la contratto utilizzato, risulta utile per evitare conflitti di nomi.
  • NameSpace : Attribuisce il namespace da utilizzare, risulta utile nei file XML.
  • IsReference : Permette di istruire il serializzatore affinché vengano gestiti i riferimenti circolari all’interno del file XML.

Considera, ad esempio, che l’oggetto persona abbia il riferimento ad un elenco di persone associate: in questo caso due persone (ad esempio dei figli) possono essere associati ad una sola persona (il padre) e pertanto verrà visualizzato un attributo nel file XML risultante che collega il figlio con il suo padre.

L’attributo DataMember può avere le seguenti opzioni:

  • Order : Permette di definire l’ordine di apparizione rispetto al padre del nodo, nel nostro esempio, nome apparirà prima di cognome.
  • Name : Attribuisce l’etichetta visualizzata dal membro.
  • EmitDefaultValue : permette di utilizzare comunque il membro anche se non valorizzato o presente, considerando il valore di default.
  • IsRequired : se il campo non è valorizzato in fase di deserializzazione (lettura dalla sorgente), viene lanciata un’eccezione. In fase di serializzazione l’utilizzo dev’essere in accordo con l’opzione EmitDefaultValue: ovvero se il campo è obbligatorio (IsRequired è true) allora dev’essere valorizzato nell’oggetto oppure EmitDefaultValue dev’essere true.

Con il DataContract possiamo ottenere la serializzazione in due differenti tipologie di formato: XML e JSON ottenibili rispettivamente con le classi DataContractSerializerDataContractJsonSerializer.

Entrambe le classi sono specializzazioni della classe astratta XmlObjectSerializer, per entrambe le classi bisogna passare l’oggetto Type relativo all’oggetto da serializzare.

[csharp]
DataContractSerializer dcs = new DataContractSerializer(typeof(Persona));
Persona p = new Persona();
p.cognome = "CognomePersona";
p.nome = "NomePersona";
p.Indirizzo = "via dei martiri 28";

FileStream fs = File.OpenWrite("serializzato.xml");
dcs.WriteObject(fs,p);
fs.Flush();
fs.Close();
[/csharp]

In questo caso abbiamo scelto di scrivere su file system l’oggetto persona, leggendo il file mediante File.OpenRead
e utilizzando il metodo ReadObject possiamo leggere il file. Osserva che è necessario un Cast per convertire l’oggetto in modo tale da assegnarlo ad un reference del tipo “Persona.”

[csharp]
FileStream fsr = File.OpenRead("serializzato.xml");
Persona per = (Persona)dcs.ReadObject(fsr);
[/csharp]

Nel caso dobbiamo creare dei file JSON invece dei file XML, dobbiamo semplicemente sostituire la classe  DataContractSerializer con la classe DataContractJsonSerializer.

NetDataContractSerializer

L’ultimo esempio di serializzatore con contratto che vedremo è il : NetDataContractSerializer.

Questo serializzatore è molto simile al DataContractSerializer, infatti serializza gli XML con una limitazione: vi p bisogno che il tipo serializzato è presente nella CLR che deserializza il file.

La serializzazione è simile, eccetto per il fatto che non vi è bisogno di passare il tipo da serializzare nel costruttore, tale tipo infatti dev’essere presente nel software. Il serializzatore è in grado di serializzare gli oggetti con
DataContractAttribute  oppure con i SerializableAttribute e serializza anche i tipi che implementano ISerializable (infatti è possibile usare Serialize e Deserialize).

[csharp]
NetDataContractSerializer dcs = new NetDataContractSerializer();
….
FileStream fs = File.OpenWrite("serializzato.xml");
dcs.WriteObject(fs,p);

[/csharp]

Ometto per brevità la deserializzazione che è la medesima.

Serializzazione senza contratto

Precedente alla serializzazione con DataContract esiste anche la serializzazione basata sulla struttura degli oggetti. Tale tipo di serializzazione è decisamente più macchinosa, ma permette di avere maggior controllo sulla serializzazione.

Normalmente si possono serializzare gli oggetti in due formati, in Binario e in XML rispettivamente con il BinaryFormatter e il XmlSerializer.

BinaryFormatter

Tale tecnica consiste nel salvare le classi utilizzando il formato binario, in questo caso il serializzatore salverà integralmente l’oggetto affinché possa essere recuperato, contrariamente ai metodi con DataContract il metodo da utilizzare sono Serialize Deserialize.

Innanzitutto aggiorniamo la classe Persona affinché sia pulita per la serializzazione senza contratto.

[csharp]
[Serializable]
public class Persona
{
public string nome {
get { return _nome; }
set { _nome = value; }
}
private string _nome;

public string cognome {
get { return _cognome; }
set { _cognome = value; }
}
private string _cognome;

public string Indirizzo
{
get {return indirizzo;}
set {indirizzo = value;}
}
private string fattiSpece;

public void setMiaFattispece(String str) {
fattiSpece = str;
}
private string indirizzo;

}

static void SerializzazioneBinaria(){
Persona p = new Persona();
p.cognome = "Ronaldo";
p.nome = "Cristiano";
p.Indirizzo = "Napoli, via dei martiri 1";
p.setMiaFattispece("assente");

//creo lo stream di scrittura
FileStream fs = File.OpenWrite("serializzato.bin");
BinaryFormatter b = new BinaryFormatter();
b.Serialize(fs,p);
fs.Flush();
fs.Close();
}

FileStream fsr = File.OpenRead("serializzato.bin");
Persona np = (Persona)b.Deserialize(fsr);
fsr.Close();
[/csharp]

In questo caso otterremo che l’oggetto verrà serializzato nel suo complesso, infine notiamo che l’uso dell’attributo Serializable è obbligatorio per effettuare la serializzazione binaria.

Infine se non vogliamo serializzare alcuni attributi pubblici è possibile utilizzare l’attributo NonSerialized.

XmlSerializer

Operativamente il serializzatore XmlSerializer è molto simile al BinaryFormatter.

[csharp]
XmlSerializer dcs = new XmlSerializer(typeof(Persona));
Persona p = new Persona();
p.cognome = "Milik";
p.nome = "Arek";
p.Indirizzo = "via dei martiri 13";
p.setMiaFattispece("assente");
Console.WriteLine(p.ToString());

FileStream fs = File.OpenWrite("serializzato.xml");
dcs.Serialize(fs,p);
fs.Flush();
fs.Close();

FileStream fsr = File.OpenRead("serializzato.xml");
Persona per = (Persona)dcs.Deserialize(fsr);

Console.WriteLine("Deserializzato da file");
Console.WriteLine(per.ToString());
Console.ReadKey();

fsr.Close();
[/csharp]

Facendo girare l’esempio osserveremo una grande prima differenza, il field fattiSpecie non verrà serializzato, perché sono serializzati soltanto gli attributi pubblici.

Inoltre XmlSerializer permette di definire degli attributi per regolare in maniera fine la serializzazione:

  1. XmlRootAttribute, permette di definire gli attributi del nodo root dell’oggetto, in particolare si possono utilizzare le seguenti opzioni:
    1. ElementName, permette di definire il nome del nodo da visualizzare come root.
    2. Namespace, permette di definire il namespace da utilizzare.
    3. IsNullable, permette di impostare, tramite true o false, se l’elemento può essere nullo.
    4. DataType, preleva o setta, tramite uno schema XSD da utilizzare nella serializzazione.
  2. XmlElement, permette di impostare il campo come un elemento dell’albero XML.
    1. ElementName, permette di definire il nome del nodo da visualizzare nel file XML.
    2. DataType, permette di impostare, tramite una stringa, il tipo dell’elemento.
    3. Form, permette di attribuire un valore che permette di esprimere se un elemento è qualificato.
    4. IsNullable, permette di impostare, tramite true o false, se l’elemento può essere nullo,
    5. Namespace, permette di definire il namespace da utilizzare.
    6. Order, permette di definire l’ordine di apparizione del campo all’interno del file serializzato.
    7. Type, permette di impostare, tramite una stringa, il tipo dell’elemento.
  3. XmlAttribute, permette di impostare il campo come un attributo del nodo radice. Se si vuole impostare l’attributo ad un nodo intermedio, il nodo itermedio dev’essere convertito a classe ed effettuare materialmente una inclusione.
    1. ElementName, permette di definire il nome dell’attributo del nodo.
    2. DataType, preleva o setta, tramite uno schema XSD da utilizzare nella serializzazione.,
    3. Form, permette di attribuire un valore che permette di esprimere se un elemento è qualificato.
    4. Namespace, permette di definire il namespace da utilizzare.
    5. Type, permette di impostare, tramite una stringa, il tipo dell’elemento.
  4. XmlIgnore, permette di ignorare il campo dell’oggetto anche se questo è public.
Differenze tra DataContractSerializer e XmlSerializer

A questo punto è lecito porsi una domanda: quando conviene utilizzare un contratto?

La risposta (come sempre nel caso dell’informatica) è dipende.

Sostanzialmente nel caso degli XmlSerializer c’è la possibilità di dichiarare gli attributi utilizzando il tag “XmlAttribute” cosa che non è possibile effettuare nel DataContract, tuttavia il DataContract permette di passare da JSON a XML con molta semplicità.

Un caso particolare, il JavaScriptSerializer

Il JavaScriptSerializer è un serializzatore senza contratto che permette di effettuare le operazioni di serializzazione di JSON su stringa. L’utilizzo di tale serializzatore è scoraggiato da Microsoft ed è utilizzato solo per fini interni al framework .Net.

Vediamone l’utilizzo:

[csharp]
JavaScriptSerializer dcs = new JavaScriptSerializer();
StringBuilder fs = new StringBuilder();

Persona p = new Persona();
p.cognome = "Pavoletti";
p.nome = "Leonardo";
p.Indirizzo = "via dei martiri 13";
p.setMiaFattispece("assente");
Console.WriteLine(p.ToString());

dcs.Serialize(p, fs);
string fsr = fs.ToString();
Persona per = (Persona)dcs.Deserialize(fsr, typeof(Persona));
[/csharp]

La serializzazione avviene seguendo le annotazioni utilizzate per la classe XMLSerializer.

Pertanto quest’ultimo modo di serializzare che vediamo, rende la serializzazione senza contratto ugualmente potente rispetto alla serializzazione con contratto.

Facilitazioni alla serializzazione senza contratto

Opzionalmente è possibile implementare l’interfaccia ISerializable e utilizzare i SerializableAttribute.

ISerializzable

L’interfaccia ISerializzable consente ad una classe di disporre del metodo GetObjectData che permette di eseguire delle operazioni prima della serializzazione al fine di arricchire ulteriormente le informazioni della serializzazione.

SerializableAttribute

L’attributo SerializableAttribute permette di indicare che una classe è Serializzabile, si può abbreviare utilizzando semplicemente Serializzable (che abbiamo visto per la serializzazione binaria).

Inoltre è possibile utilizzare degli attributi che permettono di definire dei metodi da eseguire durante la serializzazione, questi metodi sono:

  • OnSerializing, il metodo verrà chiamato prima di effettuare la serializzazione.
  • OnSerialized, il metodo chiamato appena terminata la serializzazione.
  • OnDeserializing, il metodo chiamato prima di effettuare la deserializzazione.
  • OnDeserialized, il metodo chiamato appena terminata la deserializzazione.

[csharp]
[OnSerializing()]
internal void OnSerializingMethod(StreamingContext context)
{
//Utile per impostare dei valori ad-hoc della serializzazione
cognomeLegale = "Cognome";
}

[OnSerialized()]
internal void OnSerializedMethod(StreamingContext context)
{
//utile per resettare i valori dopo la serializzazione
cognomeLegale = "";
}

[OnDeserializing()]
internal void OnDeserializingMethod(StreamingContext context)
{
//utile per impostare dei valori ad-hoc durante la deserializzazione
cognomeLegale = "Cognome";
}

[OnDeserialized()]
internal void OnDeserializedMethod(StreamingContext context)
{
//utile per resettare i valori a valle della deserializzazione
cognomeLegale = "";
}
[/csharp]

Conclusioni

Abbiamo visto che la serializzazione è quel processo per cui si vuole che delle istanze di un determinato Tipo possano essere trasformate in oggetti manipolabili dalla CRL mediante strutture generiche come stringhe e file. Abbiamo visto che vari serializzatori che ci permettono di definire il tipo di serializzazione che vogliamo usare nel nostro software: i tipi più famosi di serializzazione sono Binario, XML e JSON ottenibili con: BinaryFormatter, DataContractSerializer, XmlSerializer e DataContractJsonSerializer.

Abbiamo visto i due “modi di pensare” della serializzazione, ovvero tramite il DataContract oppure senza, infine abbiamo visto le varie “eccezioni” ovvero: JavaScriptSerializer, NetDataContractSerializer.

Ho tentato di essere quanto più sintetico ed essenziale nell’articolo, ma la mole di informazioni è notevole. Se riscontrate degli errori, vi invito ad utilizzare i commenti.

Gestire la fine del ciclo di vita degli oggetti

In questo articolo voglio illustrare la gestione della fine del ciclo di vita degli oggetti in C-Sharp.

Il nostro software gira in una porzione del sistema operativo chiamata Common Language Runtime Environment (CLR). Il ruolo di tale compente nel sistema è tener traccia di tutte le risorse proprie del software e di permettere l’accesso alle risorse esterne ad esso. Il software in C-Sharp è composto da una pletora di oggetti che (si spera) abbiano responsabilità e ruoli ben definiti.

Come avviene in Java, anche in C-Sharp un oggetto termina la sua vita all’interno del nostro software per mano del Garbage Collector (GC). Questo serial killer è la mano armata del CLR, viene chiamato in causa quando c’è scarsità di memoria e pertanto è necessaria una pulizia per fare spazio. Il GC determina quali sono gli oggetti da eliminare mediante un algoritmo di raggiungibilità di tipo “mark and compact“, ma quello che ci serve sapere è che esamina la memoria heap nel sistema al fine di trovare oggetti non più raggiungibili, se ne trova uno, lo distrugge.

Un modo gentile di procedere alla eliminazione degli oggetti è di porne i riferimenti a null, questa è la prassi consigliata. Porre a null il riferimento permette di rendere irraggiungibile l’oggetto rendendolo eligibile per il GC.

Non tutti gli oggetti hanno la stessa natura e pertanto non tutti gli oggetti si possono “smaltire” in questo modo così semplice, gli oggetti rappresentano le tipologie di risorse più varie e le risorse si suddividono in due grandi gruppi:

  • Risorse Gestite, ovvero le risorse che utilizzano gli oggetti direttamente controllabili dal framework e quindi di cui si può conoscere immediatamente lo stato, ad esempio un ArrayList è una risorsa gestita.
  • Risorse non gestite, ovvero le risorse che utilizzano oggetti non direttamente controllabili dal framework, esterne al software, l’esempio tipico di questa categoria sono i File. 

Il framework .NET tiene a cuore che il buon sviluppatore tenda ad ottimizzare l’uso della memoria, o almeno a farne un utilizzo criteriato delle risorse disposizione, pertanto ha creato un modello di utilizzo delle risorse chiamato disposable pattern e un’interfaccia ad-hoc, la IDisposable, per agevolare gli sviluppatori alla gestione corretta della memoria.

L’interfaccia IDisposable mette a disposizione il solo metodo Dispose.

L’utilizzo dell’interfaccia IDisposable è fortemente consigliato quando si utilizzano risorse non gestite, ma in generale si può implementare anche per liberare risorse gestite dalla CLR, inoltre l’implementazione dell’interfaccia IDisposable permette di utilizzare il costrutto Using che rende naturale il concetto di instanziazione e dismissione dell’oggetto utilizzato: infatti l’ultima istruzione chiamata al termine del costrutto Using è il metodo dispose.

Da qui capiamo che implementando IDisposable il CLR non chiama “da sé” il metodo Dispose per dismettere l’oggetto, bensì chiama un altro metodo, il Finalizzatore.

Il finalizzatore è chiaramente collegato al discorso dell’interfaccia IDisposable, ma interrompiamo un momento il discorso sull’interfaccia per comprenderne meglio il ruolo di questa funzione nel nostro software.

Il finalizzatore è una funzione particolare, ha il compito di distruggere l’oggetto e non può essere chiamato direttamente dal codice ma solo dal GC. Infine il finalizzatore non può essere dichiarato public static. 

Il codice del finalizzatore da un primo sguardo sembra un costruttore, infatti ha una sintassi simile se non per il fatto che prima del nome della classe è presente una tilde.

[csharp]
~Persona()
{
//qui chiamiamo il codice per finalizzare l’oggetto
}[/csharp]

Nel Finalizzatore bisogna inserire il codice che elimina solo le risorse non gestite.
Il motivo è immediato: se viene chiamato il finalizzatore dell’oggetto vuol dire che dev’essere dismesso e che tutte le risorse collegate dall’oggetto sono già state distrutte.

Da quello che abbiamo appena riportato, va da sé che un possibile codice che effettua la distruzione dell’oggetto potrebbe essere:

[csharp]
public void Dispose(){
dispose(true);
System.GC.SuppressFinalize(this);
}

public void Dispose(bool managedResources){
if(managedResources){
//liberiamo le risorse gestite
liberaRisorseGestite();
}
//liberiamo le risorse non gestite
liberaRisorseNonGestite();
}
}

~Persona(){
Dispose(false);
}
[/csharp]

Il metodo System.GC.SuppressFinalize(this) permette di rimuovere l’oggetto dalla lista di Finalizzazione degli oggetti, poiché il passaggio del GC sarebbe inutile, visto che è stato già distrutto.
Nel caso dell’utilizzo del nostro oggetto nel costrutto Using avviene che al termine dell’utilizzo l’oggetto verrà distrutto. Mentre se sarà il GC a distruggere il nostro oggetto, allora verranno dismesse le sole risorse non gestite, poiché solo quelle risorse non possono essere cancellate in autonomia dal CLR.

Pertanto come dobbiamo comportarci nell’implementare l’interfaccia?

  • Se abbiamo solo delle risorse non gestite dobbiamo implementare l’interfaccia IDisposable e il finalizzatore.
  • Se abbiamo solo delle risorse gestite dobbiamo implementare solo l’interfaccia IDisposable.
  • Se abbiamo delle risorse gestite e non gestite dobbiamo implementare l’interfaccia IDisposable e il finalizzatore.

Ultima nota, tutt’altro che marginale è che chiamare due volte il dispose, non deve causare il sollevamento di eccezioni!!!!

Gerarchia di Costruttori in C#

In questa breve panoramica, voglio illustrare il concetto di Gerarchia di costruttori così come interpretata da C# confrontandola con Java.

Consideriamo ad esempio le classi Persona e Manager: Persona è superclasse di Manager.

In Java il costruttore per Manager è strutturato di default in questo modo:

[java]
public Manager(){
super();
}
[/java]

Dove super sarà il costruttore di default di Persona, questo comporta che se la classe Persona non ha il costruttore di default allora avremo un errore in compilazione.

Pertanto assumiamo che la classe Persona definisca il costruttore che segue:

[java]
public Persona(String nome, String cognome) {
this.nome=nome;
this.cognome=cognome;
}[/java]

Dobbiamo modificare il costruttore di senza parametri Manager:

[java]
public Manager(){
super("cognome","nome");
}[/java]

Volendo essere più puliti, aggiungiamo un costruttore parametrico.

[java] public Manager(String cognome, String nome){
super(cognome,nome);
}[/java]

Ricordiamo che la prima istruzione del nuovo costruttore dev’essere la chiamata al costruttore della superclasse.
A questo punto ci poniamo una domanda: Come chiamiamo un’altro costruttore della stessa classe?

Ovvero come possiamo chiamare Manager(String cognome, String nome) da Manager()?

Utilizzeremo la keyword this, che in questo caso assume un significato diverso dal normale riferimento implicito all’oggetto.

[java] public Manager(String cognome, String nome){
this();
this.cognome=cognome;
this.nome=nome;
}[/java]

In java abbiamo più comportamenti ambigui:

La chiamata al costruttore deve essere la prima istruzione del costruttore, non sono ammesse più chiamate a più costruttori. Non aspettatevi chiamate del tipo:

[java] public Manager(String cognome, String nome){
this();
this(nome,cognome,indirizzo) //errore di compilazione.
this.cognome=cognome;
this.nome=nome;
}[/java]

Alla riga 3 avremo errore di compilazione.

In C# il concetto resta il medesimo, cambia in modo rilevante la sintassi che impedisce di fatto di avere errori di compilazione come visto nell’ultimo esempio.

La tecnica è questa: Il costruttore da richiamare non viene inserito come prima istruzione del costruttore stesso, ma come suffisso all’istruzione che dichiara il costruttore.

[csharp]public Persona():this("Nome","Cognome")
{
System.Console.WriteLine("Persona");
}

public Persona(String nome, String cognome)
{
System.Console.WriteLine("Persona più parametri");
}
[/csharp]

L’output in questo caso sarà:

Persona più parametri

Persona

Identico al comportamento di Java.

Se si vuole chiamare un particolare costruttore della superclasse bisognerà utilizzare la keyword “base” (in java abbiamo visto che è “super“).

[csharp]
public Manager():base("nome","cognome")
{
System.Console.WriteLine("Manager");
}

[/csharp]

La differenza tra i due linguaggi può sembrar “effimera”, tuttavia bisogna osservare che in effetti in Java la chiamata ad un altro costruttore non passa facilmente in secondo piano, avendo a tutti gli effetti dignità di istruzione.
Mentre in C# viene vista, in un certo senso, come una “aggiunta” al costruttore, appesantendone la sintassi e rendendo macchinosa e non immediata la comprensione, specialmente nel caso in cui ci siano molti parametri nel costruttore.

ad esempio, in java:

[java] public Manager(String nome){
this(nome,"Cognome");
}

public Manager(String nome, String Cognome){
this.nome= nome;
this.cognome = cognome;
}
[/java]

Mentre in C# avremo:

[csharp]
public Manager(String nome):base(nome,"cognome")
{ }
public Manager(String nome, String cognome){
this.nome= nome;
this.cognome = cognome;
}
[/csharp]

Per concludere, in C# abbiamo una sintassi orientata ad evitare errori in compilazione,
in Java si vuole che sia chiara e immediata la comprensione del fatto che si utilizza un gerarchia di costruttori.