UGISS Community

Il sito della community dello User Group Italiano di SQL Server
Welcome to UGISS Community Sign in | Join | Help
in Search

Alessandro Alpi's blog

Parliamo di integration services e non solo..

settembre 2007 - Posts

  • Classe Sql Server Management Objects (SMO) per la gestione degli oggetti di un database

    Molte delle nostre applicazioni, siano essere parte del nostro lavoro oppure solo scritte per diletto/utilità, necessitano almeno di un accesso a database.
    Già altri blog parlano della problematica, e già in tanti almeno una volta, si sono posti il problema di evitare la scrittura di codice ripetuto aumentando, conseguentemente la produttività.
    Un primo livello di analisi può essere la scrittura di una classe utile a stabilire i vari tipi di connessione (OLEDB, ODBC, SQL, ecc..) magari da utilizzare come classe base di altre, più orientate agli oggetti del database.

    Nell'azienda per la quale lavoro l'idea di ridurre la scrittura del codice ripetitivo era già stata implementata ed ora è stata ulteriormente migliorata, utilizzando i "nuovi" costrutti del framework .net 2.0 (prima era in 1.1) e seguendo una filosofia più ad oggetti della precedente versione. Grazie all'applicativo creato ad hoc, tutta la parte di interfacciamento a database, per ogni oggetto del db, è da non scrivere Stick out tongue. E questo è ottimo per ogni progetto che si deve implementare all'interno della nostra realtà, anche se credo che chiunque utilizzi basi di dati nelle proprie applicazioni debba almeno pensare di investire un po' di tempo e studio nello scrivere questo tipo di programmi Wink.

    Dopo questa parentesi, passiamo a come SMO interagisce con l'argomento. Pensando infatti alla generazione automatica di un layer di Data/Business, può essere comodo creare un'interfaccia Windows utile a scorrere i database di un server ed i relativi oggetti. Trattandosi di SQL Server Management Objects, la connessione è ovviamente solo verso SQL Server 2005 Big Smile.
    Comunque ho deciso di condividerla per dare la possibilità anche a chi non li conosce di approfondire l'argomento. E' più che altro una classe di lettura delle collezioni degli oggetti SQL Server 2005, che segue il solo ramo SERVER --> DATABASE --> TABLE.

    Più in dettaglio, la classe espone metodi e proprietà che:
    - consentono la connessione verso un server (o una named instance) sia in Windows Authentication sia in SQL Server Authentication
    - la lettura dei database di un server
    - la lettura delle tabelle di un database
    - la lettura della struttura di una tabella dall'elenco delle tabelle (lettura di una tabella dalla collezione)

    Qui di seguito il codice, che dovrebbe autodocumentarsi da solo Geeked.
    Ovviamente sono ben accetti commenti per miglioramenti o giudizi..

     

    Imports Microsoft.SqlServer.Management.Smo
    Imports Microsoft.SqlServer.Management.Common

    Public Class SmoConnector

        Private _server As Server
        Private _database As Database
        Private _table As Table
        Private _serverExists As Boolean = False
        Private _databasesExists As Boolean = False
        Private _serverName As String
        Private _userName As String
        Private _password As String
        Private _isWindowsAuthentication As Boolean = False

    #Region "Costruttori"

        ''' <summary>
        ''' Inizializza l'oggetto
        ''' </summary>
        Sub New()

        End Sub

    #End Region

    #Region "Proprietà Pubbliche"

        ''' <summary>
        ''' Password per la login di SQL Server
        ''' </summary>
        Public Property Password() As String
            Get
                Return _password
            End Get
            Set(ByVal value As String)
                _password = value
            End Set
        End Property

        ''' <summary>
        ''' Ottiene o imposta l'Utente per la login di SQL Server
        ''' </summary>
        Public Property UserName() As String
            Get
                Return _userName
            End Get
            Set(ByVal value As String)
                _userName = value
            End Set
        End Property

        ''' <summary>
        ''' Ottiene o imposta il tipo di connessione al server
        ''' </summary>
        Public Property IsWindowsAuthentication() As Boolean
            Get
                Return _isWindowsAuthentication
            End Get
            Set(ByVal value As Boolean)
                _isWindowsAuthentication = value
            End Set
        End Property

        ''' <summary>
        ''' Ottiene il server selezionato
        ''' </summary>
        Public ReadOnly Property Server() As Server
            Get
                Return Me.ServerInstance
            End Get
        End Property

        ''' <summary>
        ''' Ottiene o imposta il server selezionato in un contesto privato
        ''' </summary>
        Private Property ServerInstance() As Server
            Get
                Return _server
            End Get
            Set(ByVal value As Server)
                _server = value
            End Set
        End Property

     
        ''' <summary>
        ''' Ottiene la tabella selezionata
        ''' </summary>
        Public ReadOnly Property Table() As Table
            Get
                Return _table
            End Get
        End Property

        ''' <summary>
        ''' Ritorna true se i database sono accessibili
        ''' </summary>
        Public ReadOnly Property DataBasesExists() As Boolean
            Get
                Return _databasesExists
            End Get
        End Property

        ''' <summary>
        ''' Ottiene o imposta la stringa del nome del server
        ''' </summary>
        Public Property ServerName() As String
            Get
                Return _serverName
            End Get
            Set(ByVal value As String)
                _serverName = value
            End Set
        End Property

        ''' <summary>
        ''' Ottiene la lista dei database di un server
        ''' </summary>
        Public ReadOnly Property DataBases() As DatabaseCollection
            Get
                ' se il server esiste ricavo l'eventuale lista dei db

                Try
                    ' ricavo la lista dei database, se ne esistono
                    If Me.Server.Databases.Count > 0 Then

                        Return Me.Server.Databases
                    Else
                        ' lancio l'eccezione
                        Throw New SmoException(String.Format("Non vi sono database disponibili sul server {0}", Me.ServerName))
                    End If

                Catch ex As Exception
                    ' lancio l'eccezione sql
                    If Me.IsWindowsAuthentication Then
                        Throw New SmoException(String.Format("Errore di connessione al server, SQL Server inesistente o accesso negato sul
                                                              server '{0}' con Windows Authentication"
    , Me.ServerName))
                    Else
                        Throw New SmoException(String.Format("Errore di connessione al server, SQL Server inesistente o accesso negato sul
                                                              server '{0}' con login '{1}'"
    , Me.ServerName, Me.UserName))
                    End If

                End Try
            End Get
        End Property

    #End Region

    #Region "Metodi pubblici"

        ''' <summary>
        ''' Inizializza l'oggetto per la WindowsAuthentication
        ''' </summary>
        Public Sub Connect()

            ' controllo se il nome del server è presente
            If String.IsNullOrEmpty(Me.ServerName) Then
                Throw New NullReferenceException("Server name non valido. Impostare la proprietà 'ServerName'")
            End If

            ' imposto user e password solo se non ho Win Auth
            If Not Me.IsWindowsAuthentication Then

                ' imposto nome del Server e tipo di connessione
                Dim conn As New ServerConnection(Me.ServerName)
                conn.LoginSecure = Me.IsWindowsAuthentication

                ' controllo se il nome del login è presente
                If String.IsNullOrEmpty(Me.UserName) Then
                    Throw New NullReferenceException("Username non valida. Impostare la proprietà 'UserName'.")
                End If

                ' imposto UserName e Password
                conn.Login = Me.UserName
                conn.Password = Me.Password

                ' ritorno un'istanza di server in contesto privato
                Me.ServerInstance = New Server(conn)

            Else
                ' in windows authentication non c'è da impostare nulla. Torno solo l'istanza in base al nome del server.
                Me.ServerInstance = New Server(ServerName)
            End If

        End Sub

        ''' <summary>
        ''' Ritorna la lista delle tabelle di uno schema
        ''' </summary>
        Public Function GetTables(ByVal DataBaseName As String) As TableCollection

            ' se il server contiene il database ritorno l'elenco delle eventuali tabelle
            If Me.Server.Databases.Contains(DataBaseName) Then
                Return Me.Server.Databases(DataBaseName).Tables
            Else
                Throw New SmoException(String.Format("'{0}' non è più disponibile", DataBaseName))
            End If

        End Function

        ''' <summary>
        ''' Ritorna l'oggetto tabella
        ''' </summary>
        Public Function GetTable(ByVal DatabaseName As String, ByVal TableName As String, ByVal SchemaName As String) As Table

            If Me.GetTables(DatabaseName).Contains(TableName, SchemaName) Then
                Return Me.GetTables(DatabaseName)(TableName, SchemaName)
            Else
                Throw New SmoException(String.Format("'{0}' non è più disponibile", TableName))
            End If

        End Function

        ''' <summary>
        ''' Ricava la primary key dalla tabella
        ''' </summary>
        Public Function GetPrimaryKeyField(ByVal smoTable As Table) As PrimaryKeyCollection
            Dim PK As New PrimaryKeyCollection

            For Each c As Column In smoTable.Columns
                If c.InPrimaryKey Then
                    PK.Add(c.Name)
                End If
            Next

            Return PK
        End Function

           ''' <summary>
        ''' Genera lo script per la creazione di una tabella
        ''' </summary>
        Public Function GenerateScript(ByVal FileName As String, ByVal TableName As String) As String

            Dim strScript As String = String.Empty
            ' oggetto per le opzioni di output dello script
            Dim scriptOpt As New ScriptingOptions()

            ' imposto le opzioni

            ' script del clustered
            scriptOpt.ClusteredIndexes = True
            ' script delle dipendenze
            scriptOpt.DriForeignKeys = True
            scriptOpt.DriPrimaryKey = True
            scriptOpt.DriUniqueKeys = True
            ' script della clausola if not exists
            scriptOpt.IncludeIfNotExists = True
            ' script del solo drop
            scriptOpt.ScriptDrops = False

            ' output
            scriptOpt.FileName = FileName

            ' Generazione script
            Try


                Dim str As String = String.Empty

                ' per ogni stringa ricavata dal metodo Script (con opzioni preimpostate)
                For Each s As String In Me.DataBases("DWH_IBetParadise").Tables("DimCustomerAccounts", "Customers").Script(scriptOpt)
                    str &= s
                Next

                Return str

            Catch ex As Exception

                Return String.Empty

            End Try

        End Function
     

    #End Region

    End Class

     

    Public Class PrimaryKeyCollection
        Inherits List(Of String)

        ''' <summary>
        ''' Se = true la primary key è composta da più colonne
       
    ''' </summary>
        Public ReadOnly Property ChiaveMultipla() As Boolean
           
    Get

                Return Me.Count > 1

            End Get

        End Property

        ''' <summary>
       
    ''' Nome della colonna della primary key a Chiave Singola
       
    ''' </summary>
        Public ReadOnly Property NomeColonnaChiaveSingola() As String

            Get

                If Me.Count = 0 Then

                    Throw New ApplicationException("Chiave Primaria non definita")

                End If

                 Return Me(0)

            End Get

        End Property

     

        ''' <summary>

        ''' Inserisce una colonna

        ''' </summary>

        Public Shadows Sub Add(ByVal ColumnName As String)

            If _FindIndex(ColumnName) = -1 Then

                MyBase.Add(ColumnName)

            End If

        End Sub

        ''' <summary>
       
    ''' Ricerca l'indice di una colonna
       
    ''' </summary>
      Private Function _FindIndex(ByVal ColumnName As String) As Integer

            For i As Integer = 0 To Me.Count - 1

                If Me(i) = ColumnName Then

                    Return i

                End If

            Next 

            Return -1 
        End Function

    End Class

     

    Ho aggiunto un metodo, quello che genera gli script di una tabella con SMO. Altri metodi, come la generazione degli script di altri tipi di oggetto oppure la parametrizzazione delle opzioni dello script da demandare all'utente, la lascio a voi come studio Big Smile.. è semplicissimo! 


    Stay tuned! 

  • Articolo sui SQL Server Proxy Account

    Ho scritto un articolo relativo all'utilizzo dei SQL Server Proxy Accounts..
    Chi utilizza frequentemente i SSIS Packages e accede a risorse esterne a SQL Server non può fare a meno di conoscere ed utilizzare i Proxy Account (o SQL Server Agent Proxies).
    L'articolo cerca di illustrare una problematica comune per capire come si usano e come possono tornare comunque utili anche al di fuori di SSIS, seppure l'esempio sia incentrato sugli Integration Services.

    Potete leggerlo e commentarlo qui

    Aspetto vostri consigli e commenti, mi raccomando!!

    Stay Tuned! Wink