NHibernate IUserType in combinazione con JSON.NET

Nel precedente articolo ci siamo lasciati con la definizione del nostro dominio applicativo. Abbiamo introdotto l'interfaccia "IUserType" di NHibernate, utilizzata per "insegnare" all'OR/M come trattare tipi di dati non nativi e persisterli sulla base dati.

Non è mia intenzione spiegare tutte le peculiarità e le sfaccettature dell'utilizzo di IUserType: ci sono moltissimi tutorial in rete (molti dei quali veramente ben scritti) che spiegano in dettaglio ogni singolo aspetto. Tuttavia, per completezza di informazione ripoterò una brevissimo estratto della definizione dell'interfaccia nuda e cruda: Essenzialmente, quello che deve essere fornito all'OR/M, per renderlo in grado di trattare il dato, è la specifica del tipo di colonna su cui dovrà persistere l'informazione stessa. Nel nostro caso l'obiettivo è salvare un oggetto strutturato in formato JSON sulla base dati: quale miglior (e conveniente) scelta se non un campo "stringa" senza limite sul numero di caratteri?

Per Microsoft SQL Server verrà quindi utilizzata una colonna NVARCHAR(MAX), per Oracle un CLOB (o tipo analogo), e via dicendo... Il tipo "astratto", nativo di NHibernate, che fungerà a nostro scopo è un "StringClobSqlType"; ma poichè l'interfaccia di definizione dei tipi ("SqlTypes") permette di esplicitare più di un riferimento, la mappatura deve essere ritornata in un array di elementi. E' inoltre fondamentale specificare, come ritorno della proprietà "ReturnType" il tipo dell'elemento di uscita; per intenderci, il tipo della proprietà che sarà esposta sul modello dati ("IList di RemoteResource").

Una volta specificato l'input e output dei tipi che andremo a trattare, quello che manca da fare è specificare il modo con cui NHibernate deve manipolare e trasformare le informazioni per convertire una lista di oggetti nel tipo stringa su cui abbiamo deciso di salvare il JSON. I metodi "NullSafeGet" e "NullSafeSet" fungono a questo scopo: usando un "DataReader" e un "DbCommand" dobbiamo codificare il passaggio di serializzazione e deserializzazione delle informazioni, assicurandoci che tutte le possibili casistiche siano contemplate e gestite (valori nulli, mancanza di informazioni, validità dei cast, etc.).

L'argomento, essendo di natura piuttosto teorica, è difficile da illustrare a parole. Aggiungeteci pure il fatto che ho voluto generalizzare l'implementazione e costruire uno "usertype" riutilizzabile per diverse situazioni. Come spesso succede, qualche riga di codice è molto più esplicativa di un fiume di inutili sproloqui...quindi a seguire riporto il contenuto della classe astratta che implementa "IUserType". Chiaramente, al fine di utilizzare il nostro user type custom, la classe astratta va specializzata, specificando come parametro generico del tipo l'elemento singolo della lista di oggetti da "trasformare": la classe "RemoteResource". L'unico metodo che è stato necessario implementare esplicitamente, come è possibile vedere dal codice sopra riportato, è "DeepCopy". Questo metodo, "wrapper" tipizzato dell'interfaccia base di "IUserType", richiede allo sviluppatore di fornire la logica di duplicazione di uno degli oggetti presenti nella lista; nel nostro caso è sufficiente creare una nuova instanza della classe "RemoteResource" e copiarci dentro le informazioni dell'oggetto sorgente.

L'interfaccia "IUserType" di NHibernate è incredibilmente versatile; permette di immettersi nella "pipeline" dell'OR/M in maniera efficace, intervenendo nei processi di archiviazione e recupero delle informazioni dalla base dati sottostante. L'utilizzo che ne ho fatto in questo articolo è tutto sommato banale: nonostante questo mi ha permesso di risparmiarmi parecchie ore dedicate alla definizione delle mappature ".hbm.xml", e diverse tabelle sul database (della utilità quantomeno dubbia). Alla prossima...

M.

Commenti

Post popolari in questo blog

Restore di un database SQL Server in un container Docker

Cancellazione fisica vs cancellazione logica dei dati

WCF RESTful service: esposizione del servizio