Home > SMO … questo sconosciuto ?

SMO … questo sconosciuto ?

Diciamoci la verità. Il lavoro quotidiano di un informatico è quello di trovare soluzioni a problemi che nella maggior parte delle volte sono veramente complicati.

Per poter affrontare queste problematiche, ognuno si crea una propria cassetta degli attrezzi da cui attingere (con un’abilità più da prestigiatore che da informatico) pezzi di codice, tools vari o quant’altro possa servire.

Col passare del tempo però, questa cassetta degli attrezzi rischia di diventare di dimensioni titaniche, troppo grande perché possa effettivamente essere di aiuto.

Per ovviare a questo problema, la prima cosa da fare è sicuramente quella scegliere con estrema attenzione quali strumenti conservare e quali no tra l’enormità di quelli che ci vengono messi a disposizione. Il secondo passo consiste nel manutenere gli strumenti che si è scelto, aggiornarli, e soprattutto non smettere mai di guardarsi attorno, perché tali strumenti rischiano di diventare obsoleti, o addirittura inappropriati allo scopo per cui furono originariamente progettati.

Partendo proprio da questo concetto, visto che spesso per mancanza di tempo o magari solo per pigrizia non si ha modo di guardarsi attorno, ho pensato di scrivere questo articolo su SQL Server Management Objects o più brevemente SMO. Con SMO si andrà ad aggiungere alla propria cassetta degli attrezzi uno strumento che pare essere davvero poco conosciuto seppur potentissimo, abbastanza semplice da usare anche se forse come strumento di lavoro è un tantino … ingombrante !

Ma perché ingombrante ? Vi spiegherò il perché tra breve. Ma prima di cominciarne l’esplorazione, è bene fare una importante premessa: cosa fa esattamente SMO e a cosa serve ?

SMO è una delle infinite libreria di classi che rientra nell’enorme patrimonio del framework .NET.  E’ stato scritto per gestire SQL Server. Il suo scopo principale è solo quello. E’ una libreria che si presta perfettamente a compiti quali, ad esempio, elencare i database di una istanza, elencare le tabelle che vi sono contenute ed elencare le colonne di ogni singola tabella, oltre ovviamente a fornire svariate funzionalità di gestione per ognuno di questi oggetti. Consente inoltre di effettuare procedure di backup e di restore con le relative verifiche, ed altre operazioni di questo tipo, non sempre facilmente implementabili anche utilizzando una libreria potente come ADO.NET.

Il miglior esempio che si possa fare per rendersi immediatamente conto di cosa intendo per gestire, è immaginare una situazione in cui si deve poter amministrare SQL Server da una qualsiasi macchina della propria rete aziendale senza avere a disposizione Management Studio ! Per continuare ad avere la comodità di una interfaccia grafica, si decide di creare un tool ad hoc. Una volta completato il tool scritto con SMO, lo si può copiare sulla propria penna USB, e così sarà possibile accedere all’istanza di SQL Server di proprio interesse ed amministrarla, scrivendo davvero pochissime righe di codice !

Perché allora non usare sempre SMO anziché ADO.NET ?

Le risposte in verità sono più di una, ma la principale è che SMO è molto, ma molto pesante (ed ecco perché prima lo definivo ingombrante). SMO porta sulla vostra macchina, in un oggetto chiamato Server, tutta un’intera istanza di SQL Server ! Ora, non sappiamo quanto può essere grande una rappresentazione di una istanza, ma parlando di valori medi è sicuramente molto, ma molto più grande della rappresentazione di una tabella popolata con il risultato di una query, che poi è quello per cui è indicato ADO.NET.

In SMO, per fare una qualsiasi operazione, dobbiamo prima portarci in locale la copia dell’istanza di SQL Server che ci interessa, e poi, una volta connessi, possiamo cominciare a lavorare (per esempio lanciare una query per la modifica della struttura di una tabella). In ADO.NET, invece, effettuiamo solo la connessione al database e poi, tramite questa connessione, lanciamo la query. Quello che dobbiamo gestire, oltre all’oggetto connessione di per se, è il risultato della query (se c’è). Tale risultato potrà essere inserito, ad esempio, in un DataTable, ma certamente il DataTable risultante sarà più piccolo dell’intera istanza di SQL Server ! Immaginiamo poi solamente il volume di traffico che si verrebbe a generare se tutto questo “spreco” di memoria venisse moltiplicato per il numero di utenti collegati al database ! Appare quindi evidente che SMO non deve essere usato in ambienti in cui si prevedono molte connessioni, quale è appunto quello di un utilizzo “regolare” del database. Per questa ragione SMO viene confinato nell’area di gestione.

Certo, SMO ha degli strumenti per ottimizzare le informazioni, la memoria e il consumo sia di banda di rete che di CPU. Tra questi spicca sicuramente la tecnica chiamata Lazy Instantiation. Grazie a questa tecnica, quando un oggetto viene creato, le sue proprietà non vengono immediatamente lette dal server, eccezion fatta per quelle essenziali ai fini di scripting, risparmiandoci quindi notevole banda di rete. Tuttavia però, nel complesso il discorso non cambia di molto.

Inoltre c’è un secondo punto da sottolineare. SMO è progettato per lavorare solo con SQL Server (anche 2000). Se abbiamo necessità di gestire un database diverso da SQL Server, non possiamo usare SMO, ma dobbiamo usare ADO.NET, che è una libreria progettata per gestire basi di dati anche diverse tra loro.

Questa premessa sembrerebbe, a conti fatti, demolire SMO, ma in verità non è così. SMO, come vedremo tra poco, grazie alle librerie dedicate che lo compongono, consente di fare cose grandiose con pochissime righe di codice, il che, come è risaputo, tende a ridurre l’introduzione di errori, aumentando di riflesso la sicurezza.

Dato che SMO è composto da migliaia di classi. quello che cercherò di fare in questo articolo è di dare al lettore un’idea del funzionamento di SMO, e non di fornire padronanza della libreria in sé, lasciandogli quindi il piacere di continuare l’esplorazione delle classi alla ricerca di maggiori dettagli a seconda di quello che gli può servire.

Per cercare di raggiungere questo scopo, e al tempo stesso semplificare le cose, ho deciso di suddividere l’articolo in due macro aree. La prima riguarda le funzionalità di connessione e di interrogazione, mentre la seconda riguarda la gestione vera a propria.

Per mantenere il codice quanto più coinciso possibile, ho eliminato tutte quelle parti collegate alla buona norma di programmazione, come utilizzare le using e includere il codice in blocchi try-catch.  Tuttavia vi esorto vivamente ad utilizzare queste tecniche, oltre che per una maggiore qualità del codice, anche perché quando si lavora con SMO, spesso di  lavora con oggetti davvero voluminosi, che sicuramente non è bene lasciare in memoria, sparpagliati qua e là.

Infine, molte funzionalità sono state solo citate, ma non analizzate. Tuttavia, dove possibile, ho fornito alcuni spunti su cosa cercare per ottenere maggiori dettagli in merito.

Introduzione al codice

Cominciamo subito con l’aggiungere i riferimenti ai namespaces necessari per utilizzare SMO. I namespaces sono in tutto tre :

Microsoft.SqlServer.ConectionInfo
Microsoft.SqlServer.Smo
Microsoft.SqlServer.SmoEnum
 

Nota
Se tra i riferimenti non ci sono i tre namespace indicati, vuol dire che probabilmente sulla vostra macchina non è installata nessuna versione di SQL Server. E’ sufficiente installarne una (anche la versione Express), oppure modificare l’installazione di Visual Studio aggiungendo la parte relativa a SQL Server che probabilmente non avete installato. Questo vi consentirà di aggiungere le librerie necessarie a SMO. Se SQL Server è già installato, potete puntare direttamente alle librerie SMO nella cartella C:\Programmi\Microsoft SQL Server\90\SDK\Assemblies. Se le librerie SMO non dovessero essere presenti, verificare di aver scaricato ed installato l’SDK per SQL Server.

 

Per comodità e compattezza di codice, importiamo due namespaces:

 
Imports Microsoft.SqlServer.Management.Smo
Imports Microsoft.SqlServer.Management.Common
 
 
Connessione e interrogazione

Cominciamo subito con la parte sequenzialmente più logica, relativa alla connessione a SQL Server e, come vedremo, non solo questo. Dato che questa sezione è probabilmente la più importante, sia perché tocca la sicurezza, sia perchè si candida ad essere l’area con le funzionalità che probabilmente si utilizzerà con più frequenza (perchè possono essere utili, ad esempio, per generare aree di interrogazione all’interno dei propri applicativi o di tool per la generazione di codice), ho deciso di dedicarle una particolare attenzione, e quindi la trattazione risulterà piuttosto lunga.

Il primo passo consiste sicuramente nell’effettuare la connessione al database. In SMO una connessione è rappresentata dalla classe ServerConnection facente parte del namespace Microsoft.SqlServer.Management.Common. Questa classe ha sei costruttori, ma per il momento ci occuperemo di solo due di essi. Il primo è il costuttore che non accetta parametri, il che indica la volontà di effettuare una connessione all’istanza di default di SQL Server sulla macchina locale.

L’altro costruttore è quello che accetta come unico parametro il nome del server su cui vogliamo puntare. Se sul server è presente solo una istanza, sarà sufficente il nome del server, altrimenti si dovrà concatenare al nome del server il carattere “\” seguito dal nome dell’istanza alla quale si vuole puntare.

Per esempio, per puntare al server SQLServ_001 su cui gira una sola istanza di SQL Server, sarà sufficiente passare al costruttore la stringa “SQLServ_001”. Se invece sullo stesso server ci sono due o più istanze, e dobbiamo puntare, ad esempio, all’istanza chiamata Ist01, dovremo passare al costruttore la stringa “SQLServ_001\ Ist01”

Ad aiutarci nel compito di generare la stringa da passare al costruttore appena esaminato, SMO ci mette a disposizione un utilissimo strumento, la classe SmoApplication,  ed in particolar modo il suo metodo statico EnumAvailableSqlServers, per il quale conviene sicuramente aprire una parentesi.

Il metodo EnumAvailableSqlServers  ha come scopo quello di rilevare tutti i server su cui è raggiungibile SQL Server, e per ognuno di essi le istanze rilevate. Questo metodo ha tre overload. Il primo, che non accetta parametri, fa esattamente quanto appena detto. Esegue una scansione di tutta la rete, per individuare i server su cui è raggiungibile SQL Server. Gli altri due servono a restringere il range di ricerca solo localmente o a puntare ad un computer specifico.

Per restringere il range di ricerca, si utilizza la versione che accetta il parametro booleano localOnly. Se lo impostiamo su True, effettueremo una  ricerca solo localmente, mentre impostandolo su False, effettueremo una ricerca anche sulla rete. E’ ovvio che in quest’ultimo caso, la ricerca potrebbe durare anche doversi minuti.

Utilizzando invece la versione che accetta un parametro di tipo stringa, chiamato name, si indica il nome del computer su cui è instalalta l’istanza di SQL Server che vogliamo raggiungere, e la ricerca di SQL Server e delle sue istanze verrà eseguita solo su di esso.

Tutte le informazioni raccolte durante la ricerca, vengono rese disponibili tramite un datatable composto dalle seguenti colonne :

 
Name
Server
Instance
IsClustered
Version
IsLocal
 
 

La maggior parte di queste colonne, sono autoesplicative. Ma quelle utili al nostro scopo sono le colonne Name, Server e Istance. La colonna server contiene (guarda un pò), il nome del server, mentre la colonna istance contiene … il nome dell’istanza ! Perché allora esiste una colonna name ? La colonna name contiene il nome del server ed il nome dell’istanza (se ce n’è più di una) già concatenate tra loro, quindi già pronte per poter essere utilizzate dal costruttore dell’oggetto ServerConnection. Nel caso che l’istanza sia una sola, la colonna name e la colonna server conterranno lo stesso valore, mentre la colonna istance sarà vuota.

Anche la colonna Version è molto utile in questa fase. Questa colonna infatti ci dice che versione di SQL Server è in esecuzione sul server rilevato. Se ci interessa la versione 2005, il valore restituito nella colonna dovrà cominciare per 9, mentre per il 2000 dovrà comincerà per 8. Quindi, se dobbiamo collegarci solo a delle istanza di SQL Server 2005, dovremo verificare che il valore contenuto nella colonna Version per il server di nostro insteresse cominci per 9.

A questo punto possiamo vedere un breve frammento di codice che interroga le istanze raggiungibili di SQL Server sulla rete locale e aggiunge i server (con le eventuali istanze) rilevati ad una ComboBox chiamata cmbRegServers :

 
Dim _localDT As DataTable = SmoApplication.EnumAvailableSqlServers(False)
If Not _localDT Is Nothing AndAlso _localDT.Rows.Count > 0 Then
            For Each rw As DataRow In _localDT.Rows
                  cmbRegServers.Items.Add(rw("name"))
            Next
End If
 
 
Dopo questa lunga ma utile parentesi sull’oggetto SmoApplication, possiamo tornare all’oggetto ServerConnection.

Una volta istanziato l’oggetto ServerConnection, ed aver stabilito a chi vogliamo connetterci, dobbiamo decidere in che modo dobbiamo auenticarci, ossia se usare la modalità mista oppure la modalità SQL Server (il valore di default è la modalità mista, così come suggerito da Microsoft).

Cominciamo con la tipologia di autenticazione mista (ossia tramite un account di Windows) attivabile tramite la proprietà booleana LoginSecure (di default True). Se impostiamo questa proprietà su su False, stiamo richiedendo una autenticazione tramite un account di SQL Server, mentre se la si imposta (o la si lascia impostata) su True, si stà richiedendo una autenticazione tramite account di Windows.

In quest’ultimo caso, possiamo decidere se usare l’account corrente (quello con cui si è effettuato il login sulla macchina) oppure utilizzare le credenziali di un altro account. Se vogliamo utilizzare un altro account, imposteremo la proprietà ConnectAsUser su True, altrimenti possiamo lasciarla impostata sul suo valore di default, ossia False

Se la proprietà ConnectAsUser viene impostata su True, dovranno, ovviamente, essere fornite anche le credenziali dell’account che si vuole utilizzare. Queste credenziali vengono impostate tramite le proprietà ConnectAsUserName  e ConnectAsUserPassword, che accettano entrambe una stringa.

Ecco un esempio riassuntivo di una richiesta di autenticazione tramite un account Windows, ma diverso da quello corrente (in questo esempio, come negli altri riguardanti le connessioni, si assume una connessione alla macchina locale) :

 
Dim _ServerConnection As New ServerConnection
 
‘ Vogliamo una autenticazione mista
_ServerConnection.LoginSecure = True ‘ In verità non serve perchè è il valore di default, ma viene riportato per completezza
 
‘ Vogliamo usare un account diverso da quello corrente …
_ServerConnection.ConnectAsUser = True
 
‘ … quindi ne forniamo lo user name …
_ServerConnection.ConnectAsUserName = "username"
 
‘ … e la password.
_ServerConnection.ConnectAsUserPassword = "password"
 
 

La seconda possibilità di autenticazione è tramite un account di SQL Server. Questa tipologia di autenticazione, come abbiamo visto, si richiede impostanto la proprietà LoginSecure su False, mentre per fornire le credenziali è sufficiente impostare le proprietà Login e Password, davvero molto semplice !

 

Ecco un esempio di richiesta di autenticazione tramite un account SQL Server del quale si forniscono le relative credenziali (la proprietà ConnectAsUserName ovviamente non occorre più) :

 
Dim _ServerConnection As New ServerConnection
 
‘ Vogliamo una autenticazione SQL Server
_ServerConnection.LoginSecure = False
 
‘ Ne forniamo la login …
_ServerConnection.Login = "login"
 
‘ … e la password.
_ServerConnection.Password = "password"
 
 

Ora che abbiamo istanziato ed inizializzato in nostro oggetto ServerConnection, possiamo finalmente instaurare la connessione fisica con SQL Server. Per poter aprire la connessione, si utilizza il metodo Connect, mentre per chiuderla si utilizza il metodo Disconnect (consentitemi di aggiungere “che fantasia !!”). 

E’ bene sottolineare che spesso la chiusura esplicita di una connessione tramite il metodo Disconnect non serve. Questo perché una connessione viene aperta automaticamente quando richiesta, e allo stesso modo, se il metodo Disconnect non viene invocato, sarà lo stesso SMO a chiuderla automaticamente. Quindi, possiamo non preoccpuarci troppo se dimentichiamo di chiuderla, ma è bene ricordare che è sempre una buona pratica di programmazione chiudere e deallocare manualmente gli oggetti come ad esempo proprio le connessioni.

Tra i quattro costruttori non esaminati dell’oggetto ServerConnection, ne esistono due che consentono di fornire direttamente le credenziali, senza l’utilizzo delle proprietà. Uno dei due, tra l’altro, accetta la password in modo criptato (per maggiori dettagli fare riferimento all’oggetto System.Security.SecureString). 

Sempre restando in tema di credenziali di accesso a SQL Server, una funzionalità che sicuramente può risultare molto utile, consente di modificare la password di accesso ad un account. La password può essere modificata contestualmente all’autenticazione grazie al metodo ChangePassword.

Eccone un breve esempio :

 

Dim _ServerConnection As New ServerConnection

‘ Vogliamo una autenticazione SQL Server
_ServerConnection.LoginSecure = False
 
‘ Ne forniamo la login …
_ServerConnection.Login = "login"
 
‘ … e la password.
_ServerConnection.Password = "password"
 
‘ Accertiamoci che la connessione sia aperta …
_ServerConnection.Connect()
 
‘ Cambiamo la password
_ServerConnection.ChangePassword("nuovapassword")
 
‘ Chiudiamo la connessione
_ServerConnection.Disconnect()
 
 

Infine, vorrei concludere l’argomento connessione con un accenno sui pool delle connessioni. Il comportamento di default di SMO è quello di utilizza il pool delle connessioni. Dato che però l’argomento è davvero vasto, e lo spazio è tiranno, per chi desiderasse approfondire l’argomento, consiglio di cercare informazioni sulle proprietà NonPooledConnection, MinPoolSize, MaxPoolSize, e PooledConnectionLifetime dell’oggetto ServerConnection.

 

A questo punto, dopo aver esaminato come fare a collegarsi ad un server, è arrivato il momento di presentare la classe più importante di tutta la libreira SMO, la classe Server.

La classe Server rappresenta un’intera istanza di SQL Server, e ci consente, con estrema facilità, di interrogare, gestire ed effettuare una vastissdima gamma di operazioni.

La classe Server ha tre costruttori. Il primo accetta un oggetto ServerConnection, che abbiamo esaminato abbondantemente in precedenza. Il secondo accetta una stringa corrispondente al nome del server su cui gira l’istanza di SQL Server a cui vogliamo collegarci e la cui formattazione segue le stesse regole viste per lo stesso costruttore dell’oggetto ServerConnection ed infine il terzo, che non prevede parametri.

Quando si usa il seondo costruttore, ossia quello che accetta il nome del Server, in verità è come se si stesse usando il primo costruttore, ossia quello che accetta un oggetto ServerConnection, oggetto creato a sua volta utilizzando il costruttore che accetta il nome del server.

Per capirci meglio, esaminiamo un piccolo frammento di codice chiarificatore.
Usare questo codice :

Dim _Server As New Server("Server_name")
è equivalente a questo codice :
Dim _ServerConnection As New ServerConnection("Server_name")
Dim _Server As New Server(_ServerConnection)
 
 

Lo stesso dicasi per il terzo e ultimo costruttore dell’oggetto Server, quello che non prevede parametri. Anche in questo caso, si stà creando un oggetto Server come se si stesse utilizzando il costruttore dell’oggetto ServerConnection tramire il suo costruttore senza parametri.

 
Quindi il seguente codice :
Dim _Server As New Server()
è equivalente a questo codice :
Dim _ServerConnection As New ServerConnection()
Dim _Server As New Server(_ServerConnection)
 
 

A prescindere da come si è creato l’oggetto Server, e a prescindere da come si è deciso di autenticarsi, ora si ha a disposizione, tramite l’oggetto Server stesso, una copia dell’istanza a cui ci si vuole collegare, ed è possibile quindi cominciare a lavorarci in modo più concreto.

Il modello di oggetti SMO

Il modello di oggetti di SMO ha come scopo principale quello di consentire l’interrogazione di una istanza di SQL Server rappresentandola in modo gerarchico. Ed è per questa ragione che ho deciso di inserire qui questa breve introduzione. Fino ad ora non abbiamo interrogato una istanza di SQL Server, ma soltanto “trasferito” tale istanza nell’oggetto preposto a rappresentarla, ossia l’oggetto Server, e per far questo, abbiamo usato solo delle classi accessorie.

Ora che abbiamo una istanza di SQL Server, e che dobbiamo cominciare concretamente a lavoraci su, ecco che il modello di oggetti vero e proprio di SMO entra realmente “in funzione“, e questa breve spiegazione introduttiva certamente renderà più chiara la lettura del resto dell’articolo.

Abbiamo detto che SMO rappresenta gli oggetti di una istanza in modo gerarchico. Alla cima della piramide troviamo sempre l’oggetto Server. L’oggetto Server deriva, a sua volta, dall’oggetto SqlSmoObject. Per estendere la sua gerarchia, l’oggetto Server utilizza delle collections di oggetti figlio, tra cui spiccano sicuramente i database. Queste collections ereditano tutte dall’oggetto SmoCollectionBase.

Ogni oggetto figlio, contenuto nella collection dell’oggetto padre, deriva a sua volta dall’oggetto SqlSmoObject, quindi condivide buona parte degli attributi dell’oggetto padre. Anche l’oggetto figlio, a sua volta, contiene collection (sempre ereditate dall’oggetto SmoCollectionBase) per estendere a sua volta la gerarchia degli oggetti.

Facendo un esempio pratico, partiamo dall’oggetto Server, e quindi dalla cima della piramide. L’oggetto Server espone (tra le tante) una collection chiamata Databases contenente degli oggetti Database (i nomi degli oggetti sono assolutamente autoesplicativi, quindi non occorre vi dica cosa rappresentano !) . Ogni oggetto Database (che deriva a sua volta dall’oggetto SqlSmoObject) facente parte di questa collection, espone a sua volta varie collections, tra le quali la collection chiamata Tables, contenente oggetti Table. Ogni oggetto Table di questa collection espone a sua volta varie collections, tra cui la collection chiamata Columns contenente oggetti Column, e così via, sempre rispettando questa struttura gararchica, fino ad arrivare agli oggetti foglia.

Dato che tutti questi oggetti derivano dall’oggetto SqlSmoObject,  e tutte le collections derivato dall’oggetto SmoCollectionBase, il risultato finale sarà una assoluta omogeneità di tutta la libreria, che ne rende estremamente semplice e intuitivo l’utilizzo.

 

Interrogazione dell’istanza

I database

Nota
Da questo momento in poi, si presume che sia disponibile un oggetto Server chiamato _server, correttamente inizializzato.

Per cominciare, vediamo come elencare tutti i database presenti nell’istanza corrente indicando anche se si tratta di un database di sistema oppure no :

 
For Each db As Database In _Server.Databases
         ‘ IsSystemObject indica una database di sistema …
         If db.IsSystemObject Then
             Debug.WriteLine("Rilevato un db di sistema : " & db.Name)
         Else
             Debug.WriteLine("Rilevato un db : " & db.Name)
End If
Next
 
 

Come possiamo vedere da questo frammento di codice, stiamo utilizzando la collection Databases dell’oggetto _server, che elenca tutti i database presenti nell’istanza, ognuno dei quali è rappresentato da un oggetto Database. Usando un ciclo for each, leggiamo un’istanza di databse alla volta, e per ognuna di essa verifichiamo la proprietà IsSystemObject, che ci dice se si tratta di un database di sistema (True) oppure no (False). Questa proprietà è utilissima anche quando si vole lavorare solo con oggetti non di sistema, dato che la stessa identica prorpietà la troviamo anche nell’oggetto Table, che rappresenta una tabella.

 

Quindi, per ogni database, viene utilizzata la proprietà Name, che ci restituisce il nome del database. Prima del nome del database, viene concatenata una stringa che ci indica se si tratta di un database di sistema oppure no.

Ovviamente, trattandosi di collections, anziché ciclare tutti i database di una istanza, possiamo puntare direttamente al database che ci interessa per nome, o per indice. Vediamo come ottenere un’istanza di un database puntandovi via nome :

Dim _databasecercato As Database = _server.Databases("_nome_del_database_cercato")

L’oggetto Database espone un numero enorme di proprietà, metodi e collections utilissimi sia nell’ottica gestionale che di interrogazionne. A titolo di esempio, visto che ci vorrebbe un libro solo per citarli, ne ho elencati qui alcuni che potrebbero essere particolarmente utili :

Proprietà
Valore restituito
(*)L’oggetto fa parte del namsespace Microsoft.SqlServer.Management.Smo
Descrizione
(Tutti gli oggetti elencati fanno parte del namsespace Microsoft.SqlServer.Management.Smo)
ActiveDirectory
DatabaseActiveDirectory (*)

Restituisce una collection di oggetti DatabaseActiveDirectory il quale consente la gestione delle impostazioni di Active Directory.

Assemblies
SqlAssemblyCollection (*)
Restituisce una collection di oggetti SqlAssembly che rappresenta tutti gli assemblies definiti in una tabella.
CreateDate
Date
La data di creazione del database
DefaultSchema
String
Lo schema di default dell’utente
FileGroups
FileGroupCollection (*)
Restituisce una collection di oggetti  FileGroup il quale rappresenta il filegroup definito nel database.
Roles
DatabaseRoleCollection (*)
Restituisce una collection di oggetti  DatabaseRole il quale rappresenta una regola definita nel database.
Rules
RuleCollection (*)
Restituisce una collection di oggetti  Rule il quale rappresenta tutti I ruoli definiti in una istanza di SQL Server.
Schemas
SchemaCollection (*)
Restituisce una collection di oggetti  Schema il quale rappresenta uno schema del database referenziato.
StoredProcedures
StoredProcedureCollection (*)
Restituisce una collection di oggetti  StoredProcedure il quale rappresenta una stored procedure dichiarata nella tabella.
Triggers
DatabaseDdlTriggerCollection (*)
Restituisce una collection di oggetti  DatabaseDdlTrigger il quale rappresenta un trigger definito in una tabella (trigger DDL).
Urn
Urn (*)

L’oggetto Urn consente un accesso diretto via codice ad un oggetto specifico a mezzo del suo Uniform Resource Name (URN) che lo identifica univocamente tra gli oggetti di SQL Server.

 

Ho volutamente lasciato a proprietà URN in coda, perché vorrei aprire una breve parentesi in merito. Oltre a quanto visto fino ad ora, SMO identifica gli oggetti anche attraverso un altro metodo, ossia utilizzando gli URN. Un URN indica la locazione di un oggetto nell’ambito del  Server, ma, cosa più importante, è persistente.  Da questo ne consegue che conoscendo l’URN di un oggetto, è possibile puntarvi direttamente senza dover prima attraversare tutta  la gerarchia degli oggetti.

Per ottenere l’URN di un oggetto, si usa la proprietà Urn. Ad esempio, per conoscere l’URN di una tabella chiamata Tabella1 in un oggetto database chiamato Database1, si può usare il seguente codice (Database1 è un oggetto Database correttamente valorizzato) :

 
Dim _urn As Microsoft.SqlServer.Management.Smo.Urn
_Urn = Database1.Tables("Tabella1").Urn
 
 

Se si è conservato l’URN della tabella, e vi si vuole accedere in un secondo momento, si utilizzerà il metodo statico GetSmoObject dell’oggetto Server, che accetta come unico parametro l’URN dell’oggetto cercato. Se l’oggetto esiste, ne verrà restituito un riferimento, altrimenti verrà restituito Norhing.

Gli URN sono un argomento molto complesso perché, tra l’altro, per poter essere utilizzati in modo profiquo, richiedono una buona  conoscenza di Xpath. Per questa ragione, l’argomento non verrà ulteriormente approfondito in questa sede. Tuttavia, data la sua indubbia utilità, non sarebbe una cattiva idea decidere di approfondire l’argomento.

Le tabelle

Da questo momento in poi procederemo più spediti, visto che abbiamo focalizzato i punti principali in merito all’utlilizzo di SMO e quindi possiamo concentrarsi solo sul codice finalizzato ad un determinato fine.

Abbiamo già accennato, ma ripetiamo per completezza, che in SMO una tabella è rappresentata dall’oggetto Table e che per elencare tutte le tabelle presenti in un database, si utilizza la collection Tables dello stesso oggetto database.

Questa collection si allinea perfettamente a tutte le collection, quindi è possibile scorrete tutte le tabelle di un oggeto database oppure puntare direttamente ad una di esse tramite indice o nome. Ecco un semplice esempio in merito. In questo codice istanziamo un oggetto Database puntandovi usando il suo nome, cicliamo le tabelle che vi sono contenute (scartando quelle di sistema) e con i nomi delle tabelle rilevate man mano popoliamo una listbox chiamata ListBoxTables. Infine associamo a due controlli PropertyGrid rispettivamente l’oggetto Database e la prima tabella tra quelle rilevate, in modo da poterne esaminare le rispettive proprietà più comodamente :

Nota
Il controllo PropertyGrid non è disponibile di default, e va aggiunto esplicitamente nella  barra degli strumenti. Per far questo selezionare la voce “Scegli elementi …” dal menu contestuale della barra stessa, poi il tab “Componenti di .NET Framework” ed infine spuntare il controllo PropertyGrid.

 

 
Dim _localDB As Database = _server.Databases("database_name")
 
        For Each tb As Table In _localDB.Tables
            If Not tb.IsSystemObject Then
                ListBoxTables.Items.Add(tb.Name)
            End If
        Next
 
PropertyGridDatabase.SelectedObject = _localDB
PropertyGridTables.SelectedObject = _localDB.Tables(0)
 
 
Le colonne

Continuando ad addentrarci sempre più in profondità nella gerarchia degli oggetti, incontriamo l’oggetto colonna. Una colonna è rappresentata, dall’oggetto Column. E anche nel caso delle colonne, è possibile avvalersi di una collection, la collection Columns, che elenca tutte le colonne in una tabella rappresentata dall’oggetto Table.

Ecco quindi un esempio che istanzia un oggetto Table ed elenca le colonne che lo compongono aggiungendone i nomi e alcuni attributi ad una listbox chiamata ListBoxColums.

 
Dim _localTB As Table =_server.Databases("database_name").Tables("table_name")
 
For Each cl As Column In _localTB.Columns
ListBoxColums.Items.Add("Nome : " & cl.Name & " Colonna identity : " & cl.Identity & " Chiave primaria " & _
cl.InPrimaryKey) ‘ ecc.       
Next
 
 

Anche in questo caso, per esaminare più comodamente le proprietà dell’oggetto Column, possiamo associare ogni singolo oggetto Column ad una PropertyGrid, ad esempio a seguito dell’evento SelectedIndexChanged della listbox ListBoxColums, come in questo esempio :

 
 
Private Sub ListBoxColums_SelectedIndexChanged(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles ListBox2.SelectedIndexChanged
 
Dim _localTB As Table = _server.Databases("database_name").Tables("table_name")
PropertyGridTeble.SelectedObject = _localTB
 
End Sub
 
 

Ovviamente con questi oggetti e queste collection è possibile scrivere strumenti certamente più sofisticati e più utili di quanto proposto fin’ora, ma per me, vorrei ribadirlo ancora, l’importante era presentare il funzionamento generale di SMO e farvi comprendere le sue potenzialità.

 

In quest’ottica, direi che a questo punto si sia esaminata sufficentemente l’are di interrogazione e che quindi si possa passare alla seconda area della libreria SMO, dedicata alle funzionalità di gestione.

 

Gestione

Creazione ed eliminazione di un database

Con questo argomento, andiamo ad esaminare l’area più gestionale di SMO. Per area gestionale si intende una raccolta di funzionalità dedite allo svolgimento di un compito, il quale può essere un backup piuttosto che un restore, la menutenzione delle tabelle (creazione, rimozione o altro), generazione degli script (che rientra anche in una forma di backup parallelo) e via dicendo. Per prima cosa andremo ad esaminare la gestione di un database e, più specificatamente, come crearlo e come eliminarlo.

Abbiamo già presentato l’oggetto Database in precedenza, quindi non ripeteremo ancora quei concetti qui. Esaminiamo invece una serie di metodi utili alle operazioni di gestione sui database cominciando dalla creazione di un database nuovo.

Per creare un database, è sufficiente chiamare il metdodo Create. Il metodo Create ha due overload, uno senza parametri, che crea il database direttamente nell’istanza indicata dall’oggetto Server, e il secondo che accetta un parametro booleano chiamato forAttach che indica se il database deve essere creato per essere poi collegato (True) oppure no (False).

Per eliminare un database, si utilizza il metodo Drop.

Nell’esempio seguente, creeremo un nuovo database chiamato Test e subito dopo lo elimineremo :

 

Dim _database As Database = New Database(_server, "database_name")

_database.Create(False)
_database.Drop()
 
 

In questo frammento di codice possiamo esaminare anche il secondo costruttore dell’oggetto Database (il primo non accetta parametri) che accetta (ovviamente) un oggetto Server e il nome del database. Immediatamente dopo la sua creazione, il database viene eliminato grazie al metodo Drop.

La creazione di un database di per se, non serve a gran che se non è possibile aggiungervi all’interno delle tabelle, e nelle tabelle le colonne con le relative chiavi, indici, costraints, e quant’altro serva ad un database operativo. Ecco quindi un esempio un po’ più completo per la creazione di un database chiamato DatabaseDiTest e l’aggiunta di una semplice tabella chiamata Test composta da tre colonne chiamte ID (che è una colonna identity), RagioneSociale e DataDiInserimento. Inoltre, sulla colonna ID, creeremo una chiave primaria:

 
‘ Abbiamo sempre il nostro oggetto _server correttamente istanziato …
Dim _database As Database = New Database(_server, "DatabaseDiTest")
 
‘ Creiamo il db nell’istanza rappresentata dall’oggetto _server
_database.Create()
 
‘ Creiamo una tabella chiamata Test
Dim TestTable As New Table(_database, "Test")
 
‘ Creiamo un oggetto colonna che utilizzaremo più volte …
Dim _column As Column
 
‘ Creiamo la colonna ID
_column = New Column(TestTable, "ID", DataType.Int)
_column.Identity = True
 
‘ La aggiungiamo alla tabella …
TestTable.Columns.Add(_column)
 
‘ Creiamo la colonna Ragione Sociale
_column = New Column(TestTable, "RagioneSociale", DataType.VarChar(50))
 
‘ … e la aggiungiamo alla tabella …
TestTable.Columns.Add(_column)
 
‘ Creiamo la colonna DataDiInserimento
_column = New Column(TestTable, "DataDiInserimento", DataType.DateTime)
 
‘ … e la aggiungiamo alla tabella …
TestTable.Columns.Add(_column)
 
‘ Istanziamo un oggetto chiave primaria …
Dim _chiavePrimaria As New Index(TestTable, "PK_ID")
_chiavePrimaria.IndexKeyType = IndexKeyType.DriPrimaryKey
 
‘ … lo associamo alla colonna ID
Dim IndexedColumn As New IndexedColumn(_chiavePrimaria, "ID")
_chiavePrimaria.IndexedColumns.Add(IndexedColumn)
 
‘ … e alla fine lo creiamo.
_chiavePrimaria.Create()
 
 
Gli script di generazione delle tabelle

Tra le funzionalità dei database piuttosto complesse da gestire senza strumenti visuali quali Menagement Studio, c’è sicuramente la generazione degli script delle tabelle. Questa procedura è molto utile anche nell’ambito dei backup, perché si potrebbe prevedere il salvataggio, oltre ai dati di per se, anche gli script di generazione dei database, perché non si sa mai ! Come è possibile immaginare, con SMO, questa operazioen viene estremamente semplificata.

Come molti altri argomenti, anche questo argomento è molto vasto, non per la generazione degli script di per se, ma più che altro perchè la creazione degli script racchiude in se molte opzioni e sfumature, tra cui includere negli script l’eventuale eliminazione di oggetti già esistenti, oppure la gestione delle dipendenze, e così via.

In questa parte dell’articolo, mi limiterò a presentare solo la parte più semplice (e direi sostanziale) della generazione di uno script per l’oggetto tabella. Se occorre una trattazione più dettagliata dell’argomento, consiglio di esaminare gli oggetti Scripter o ScriptingOptions  (giusto per citarne alcuni), tramite i quali si può ottenere molta più granularità e molto più controllo (oltre che varietà) sulla generazione degli script in genere.

Per generare uno script di una tabella, si utilizza la collection Script dell’oggetto Table. Come il nome stesso ci suggerisce, questa collection contiene lo script tramite il quale è possibile rigenerare la tabella genitrice. Ma perchè una collection ? Perchè in verità l’oggetto Table espone lo script come una serie di comandi sqeuenziale dove quindi troveremo per primo, il comando SET ANSI_NULLS ON (questo è quanto successo secondo la configurazione di SQL Server sul mio PC, ma è una regola piuttosto generica), poi il comando SET QUOTED_IDENTIFIER ON ed infine lo script di generazione vero e proprio. Tutto questo ha il fine principale di offrire una maggiore granularità sulla generazione dello script stesso. Quindi per generare l’intero script, è necessario davvero poco codice :

 
Dim _localTB As Table =_server.Databases("database_name").Tables("table_name")
 
For Each tsql As String In _localTB.Script
txtScript.Text &= tsql & vbCrLf
Next
 
 
Trasferimento di una tabella da un database ad un altro
 

Procediamo velocemente verso un nuovo argomento, il trasferimento di una o più tabella da un database ad un altro. In questo caso entra in gioco l’oggetto Transfer che, come il nome ci suggerisce, si occupa di gestire l’effettiva operazione di trasferimento. Ma prima di esamniare il codice, c’è una cosa molto importante da dire. Perché l’oggetto Transfer possa fare il suo lavoro, è necessario che sia installato DTS su entrambi i server, altrimenti verrà sollevata una eccezione. Esaminiamo ora il codice (dato il caso particolare, ho incluso il codice in un blocco try catch :

 
Try
 
   Dim tb_trans As New Transfer
 
      ‘ Accetta un oggetto Database
      tb_trans.Database = _server.Databases("source_database_name")
 
      ‘ Accetta una stringa
      tb_trans.DestinationDatabase = "destination_database_name"
 
      ‘ Accetta una stringa
      tb_trans.DestinationServer = _server.Name
 
      ‘ Usiamo una autenticazione di Windows …
      tb_trans.DestinationLoginSecure = True
 
      ‘ Vogliamo copiare tutte le tabelle …
      tb_trans.CopyAllTables = True
 
      ‘ … anche lo schema (il default è False) …
      tb_trans.CopySchema = True
 
      ‘ … e anche i dati …
      tb_trans.CopyData = True
 
‘ Ci deve essere DTS run time installato !!!!
      tb_trans.TransferData() ‘ Effettuaimo il trasferimento …
 
      Return
 
      Catch smoex As SmoException
            MessageBox.Show(smoex.Message, "Errore", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
      Catch ex As Exception
            MessageBox.Show(ex.Message, "Errore", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
 
End Try
 
 

Se si desidera selezionare in modo più granulare gli oggetti da trasferire, dovremo aggiungere questi oggetti all’ArrayList ObjectList. Come esempio, nel frammento di codice seguente supporremo di voler copiare solo le tabelle selezionate da una listbox chiamata lbSourceTables che è stata popolata con tutte le tabelle del database sorgente :

 
‘ Le proprietà Database, DestinationDatabase, DestinationServer e
‘ DestinationLoginSecure sono valorizzate come nell’esempio
‘ precedente, mentre le proprietà CopySchema e CopyData vengono
‘ valorizzate a seconda che occorrano oppure no. CopyAllTables
‘ non viene utilizzata.
 
‘ Aggiungiamo le tabelle all’ArrayLiat ObjectList …
 
Dim sob As ListBox.SelectedObjectCollection = lbSourceTables.SelectedItems
 
For contatoreRighe As Int32 = 0 To sob.Count – 1
tb_trans.ObjectList.Add(_server.Databases("source_database_name").Tables( _
_Convert.ToString(sob.Item(contatoreRighe))))
Next
 
‘ Ci deve essere DTS run time installato !!!!
tb_trans.TransferData() ‘ Effettuaimo il trasferimento …
 
 
Backup, restore e verifica dei database

Proseguiamo questa rapidissima carellata esaminando come effettuare una tra operazioni più importanti che un DBA deve pianificare e soprattuto far funzionare ! Il backup e il restore dei dati con relativa verifica.

Cominciamo dal backup. Il backup viene gestito dall’oggetto Backup. Il backup più semplice richiede che vengano specificate almeno quattro informazioni. Prima di tutto, bisogna specificare l’azione che si vuole intraprendere, tramite la proprietà Action. Questa proprietà accetta uno dei valori esposti dall’enumerazione BackupActionType, ed indica sostanzialmente di cosa si vuol fare il backup.

La seconda informazione è il database vero e proprio.

La terza informazione è il device su cui effettuare il backup. Il device viene impostato tramite il metodo AddDevice della lista di generic Devices dell’oggetto Backup. AddDevice accetta due parametri. Il primo parametro, di nome name è una stringa, ed indica appunto il nome della periferica oppure, nel caso di files, il percorso del file di backup stesso. Il secondo parametro, chiamato deviceType, è una enumerazione, ed indica la tipologia di device che si vuole utilizzare.

Con la quarta ed ultima informazione, si andrà ad indicare se si vuole un backup incrementale oppure no tramite la proprietà booleana Incremental.

Una volta che si è terminato di impostare questi parametri, si puà lanciare il backup vero e proprio tramite il metodo SqlBackup che richiede un oggetto Server.
Andiamo ad esaminare un frammento di codice riassuntivo :

 

‘ Creo l’oggetto Backup

Dim _backup As New Backup
 
‘ Decido di cosa voglio fare il backup
 
‘ Supponiamo che la combo box chiamata ComboBoxAction consenta
‘ di scegliere tra i tre valori elencati di seguito …
 
Select Case ComboBoxAction.SelectedIndex
Case 0
_backup.Action = BackupActionType.Database
Case 1
_backup.Action = BackupActionType.Files
Case 2
_backup.Action = BackupActionType.Log
End Select
 
‘ Specifico il database (accetta una stringa)
_backup.Database = "database_name"
 
‘ Aggiungo il device al backup, ovvero l’unita fisica su cui fare il backup.
‘ Il secondo parametro, che come abbiamo visto è una enumerazione, espone i seguenti valori :
 
‘ File
‘ LogicalDevice
‘ Pipe
‘ Tape
‘ VirtualDevice
 
‘ Nel notro caso scegliamo un file …
_backup.Devices.AddDevice("percorso_di_backup", DeviceType.File)
 
‘ Vogliamo un backup incrementale …
_backup.Incremental = True
 
‘ Lanciamo il backup !
_backup.SqlBackup(_server)
 
 

Una volta effettuato il backup, è sempre buona usanza verificare che tale backup sia valido ! Per la verifica di un backup si utilizza l’oggetto Restore, che è lo stesso che verrà utilizato in seguito per effettuare il restore del backup appena effettuato, ma dato che quello che ci serve per ora è solo fare una verifica, affronteremo l’oggetto Restore dopo, con maggiore dettaglio.

Quello che ci serve al momento per la verifica è solo un device da cui leggere i dati, e il metodo SqlVerify (che richiede anch’esso un oggetto Server) esposto dallo stesso oggetto Restore. Se il metodo SqlVerify restituisce True la verifica è andata a buon fine, altrimenti qualcosa non ha funzionato.

Ecco un breve esempio :

 
‘ L’oggetto Restore
Dim _rst As New Restore
 
‘ Aggiungo un device … presumibilmente lo stesso del backup !
_rst.Devices.AddDevice("percorso_di_backup", DeviceType.File)
 
‘ Effettuo la verifica …
If _rst.SqlVerify(_server) Then
‘ La verifica è riuscita.
MessageBox.Show("Verifica riuscita !", "Verifica backup", MessageBoxButtons.OK, MessageBoxIcon.Information)
Else
‘ Il backup ha qualche problema, per cui potrebbe
‘ non essere possibile effettuare il restore …
MessageBox.Show("Verifica fallita !", "Verifica backup", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
End If
 
 

L’ultimo passaggio, consiste nell’effettuare il restore. Come già accennato, il restore avviene a mezzo dell’oggetto Restore. Il restore di un backup è un’operazione quasi speculare al backup stesso, a parte per le opzioni che non sono rilevanti per un restore come ad esempio l’essere incrementale. Il restore vero e proprio viene lanciato a mezzo del metodo SqlRestore. Anche il metodo SqlRestore richiede un oggetto Server.

Ecco un breve frammento di codice di esempio :

 
Dim _res As New Restore
_res.Database = "database_name"
 
‘ La stessa combo vista per il backup …
Select Case ComboBoxAction.SelectedIndex
Case 0
_res.Action = RestoreActionType.Database
Case 1
_res.Action = RestoreActionType.Files
Case 2
_res.Action = RestoreActionType.Log
End Select
 
‘ Presumibilmente lo stessa device …
_res.Devices.AddDevice("percorso_di_backup", DeviceType.File)
 
‘ Effettuiamo il restore dei dati …
_res.SqlRestore(_server)
 
 
Un’alternativa a SMO

Prima di concludere l’articolo, vorrei tornare velocemente a quanto da me detto nell’introduzione, e più precisamente al raffronto tra SMO e ADO.NET. Vorrei farvi vedere, giusto per un discorso di completezza, come eseguire un’operazione fatta da SMO con ADO.NET. Nell’esempio specifico elencheremo le informazioni di un database.

Nella versione 2.0 di ADO.NET (quindi se si fa riferimento alle versioni precedenti alla 2.0 il sistema più rapido per elencare le tabella di un database era effettuare una query filtrata su INFORMATION_SCHEMA.TABLES) viene messo a disposizione dell’oggetto SqlConnection il metodo GetSchema.

Il metodo GetSchema ha tre versioni. La prima, non accetta parametri. Se utilizzata restituisce un datatable contenente una serie di informazioni del database tra cui i nomi delle tabelle, delle viste e così via. Quindi, per poter trovare ciò che ci serve, dobbiamo filtrare questi risultati.

La seconda versione accetta un parametro collectionName di tipo stringa. Questo parametro ci consente di indicare esattamente che cosa vogliamo cercare. Quindi, se stiamo cercando solo le tabelle, valorizzeremo questo parametro con la stringa “Tables”. Per semplificare questa operazione, ADO.NET fornisce l’enumerazione SqlClientMetaDataCollectionNames, che di fatto ci evita l’oneroso compito di imparare a memoria tutte le stringhe associate ai singoli oggetti. Quindi, per elencare tutte le tabelle, ma utilizzando questa volta l’enumerazione, passeremo al metodo non la stringa “Tables”, ma bensì il valore SqlClientMetaDataCollectionNames.Tables.

Ecco di seguito un esempio :

 
‘ _cn è una SQLConnection già istanziata
_cn.GetSchema(SqlClientMetaDataCollectionNames.Tables)
 
‘ anzichè
_cn.GetSchema("Tables")
 
 

Infine, andiamo ad esaminare la terza versione del metodo GetSchema. Questa versione accetta come la precedente un parametro collectionName di tipo stringa, ma in più accetta un secondo parametro restrictionValues che è un array di 4 stringhe. Tramite questo parametro, è possibile aggiungere un ulteriore granularità alle ricerche. I valori assumibili da questo secondo parametro possono combinarsi tra loro, ma nell’uso più frequente si passerà il nome del db, lo schema, il nome della tabella e un valore Nothing. Ecco un esempio inerente il caso appena descritto :

 
‘ Restituisce solo le colonne della tabella table_name
‘ nello schema schema_name
_cn.GetSchema(SqlClientMetaDataCollectionNames.Columns, _
New String() {"database_name", "schema_name", "table_name", Nothing})
 
 

P.S. Un ringraziamento enorme a David Sceppa e al suo libro “Programmare ADO.NET 2.0” da cui ho attinto varie cosuccie per completare questo paragrafo

Gli esempi che ho voluto riportare qui sono davvero minimi, così come la trattazione dell’argomento stesso. Ma quello che volevo far risaltare è la differenza che esiste tra le due librerie. A parte il fatto che usare il metodo GetSchema con il parametro restrictionValues può portare molto facilmente a errori (si pensi al caso in cui si inizializzi l’array restrictionValues con valori passati come parametri), una delle cose che sicuramente balzerà agli occhi è che a  prescindere dal risultato, ottenuto anche applicando molti filtri, non si avrà mai un’istanza dell’oggetto ricercato, e quindi, per poterlo gestire, si dovranno comunque eseguire delle query dirette sul database, con tutte le problematiche che questo approccio comporta, dalle autorizzazioni fino agli errori di sintassi. Ed inoltre, pare anche lampante come SMO offra in questo frangente una sintassi più chiara e semplice rispetto ad ADO.NET.

E’ ovvio che se ci dobbiamo limitare ad elencare le tabelle e le colonne di un database, e poi effettuare comunque solo operazioni di gestione dei DATI, ADO.NET è sicuramente più efficiente e performante rispetto a SMO. Ma se dobbiamo cominciare ad introdurre un discorso di gestione, quindi di padronanza degli oggetti che compongono SQL Serve, è lampante come SMO sia l’unico strumento adeguato a questo compito.

Conclusione

Non mi stancherò mai di ripeterlo. SMO è un vero e proprio universo, che richiederebbe un intero libro per essere usato in modo profiquo, quindi figuriamoci cosa è possibile trattare con solo un articolo. Tuttavia, spero di essere riuscito, con queste mie indicazioni, nell’intento di stuzzicare la vostra curiosità tanto da spingervi ad approfondire lo studio di una tra le librerie del Framework che sicuramente ha più da offrire, e che vi consentirà di lavorare meglio, più rapidamente, in modo più sicuro e, perché no, anche divertendovi.

Tra le tante cose che non ho potuto affrontare o approfondire, c’è tutta la parte di personalizzazione dell’ottimizzazione automatica di SMO nella creazione degli oggetti, un approfondimento sulla gestione delle Stored Procedure, dei Trigger e di altre componenti di SQL Server, così come varie ottimizzazioni tra cui il backup asincrono dei database.

Purtroppo lo spazio a mia disposizione non è infinito, e quindi ho cercato di ottimizzarlo il meglio possibile. Cercherò eventualmente di agiungere altre informazioni con altri articoli o tips, vedremo. Per ora ringrazio tutti coloro che hanno deciso di dare un’occhiata a questo mio primo articolo, sperando che sia stato di loro gradimento !

Per qualsiasi suggerimento, commento, correzione, annotaziome o quant’altro possa venirvi in mente, potete tranquillamente farlo contattandomi a questo indirizzo : gentile.renato@gmail.com. Ve ne sarò comunque grato !

Quindi grazie ancora a tutti e alla prossima !

Chi è Davide Mauri

Microsoft Data Platform MVP dal 2007, Davide Mauri si occupa di Data Architecture e Big Data nel mondo dell'IoT. Attualmente ricopre il ruolo di "Director Software Development & Cloud Infrastructure" in Sensoria, societa specializzata nella creazione di Wearables e sensori per l'IoT applicati a tessuti ed oggetti sportivi.

Leggi Anche

Azure Key Vault e certificati code-signing: Strategie per la conformità post 1° Giugno 2023!

In questi giorni, ho avuto l’opportunità di esplorare il rinnovo di un certificato di code-signing …