SQLServerCentral Article

Backing Up a Database with SMO

,

Using the SQL Server SMO object to remotely backup a database.

Sure, as a SysAdmin, you can do backups via the Management Studio interface, but what if you've just shut down SSMS for the day and someone comes begging you for a backup? Do you really want to wait for SSMS to pull itself together and get connected to a DB so you can open object explorer or type up the T-SQL required?

Well, okay. Maybe I'm a lazy DBA. I certainly prefer saving code snippets to do all my backup / restores to retyping them from scratch every time I need them. However, when I found out about the new SQL Server SMO object, I just about jumped up and down for joy. Finally, a new toy I could have lots of fun with!

Searching the internet for information on the new SMO object for SQL Server 2005, I found lots of information on how to use the SMO to backup local databases. Unfortunately, what I needed and couldn't find was information on how to backup the databases remotely. After a lot of work, I finally found the solution to my problem and thought I would pass it on to everyone else. Hopefully this will help someone else out. Just make sure the people you give this program to actually have permission to backup the databases or the program will fail.

Important Note: Make sure to save your work intermittently or you may loose everything you're working on if there's a power surge.

Start by creating a project for a new Visual Basic Windows Application. I called mine SMO_BackupDatabase. Okay, so I'm not going to win awards for creative program names, but it gets the point across. @=)

Next, add References to the project by going to Project -> Add Reference. You can add them all at once by using CTRL-Click. The references you need are listed below:

Microsoft.SqlServer.ConnectionInfo
Microsoft.SqlServer.Smo

Rename Form1 in properties and under the project / solution menu to ChooseEnvironment. Also, change the text on the form to "Choose Environment" so the end users know what they are looking at. NOTE: I rename all my objects to descriptive user-friendly names so when a bug occurs, I know exactly where to find it. (See Figure1)

Add a label, rename it to WelcomeLabel, and enter appropriate text as introduction to Program. I used the following:

"Welcome to the SQL SMO Database Backup Program!

Please Select an Environment to get started."

FIGURE 1

Add a GroupBox & as many Radio buttons as you have environments. (I use four, one each for Dev, Test, QC and Production).

Make sure to change your names and the Text properties to something appropriate. I used ServerEnvironment, Development, Test, QC, and Production for mine.

Lastly, add 2 buttons to the bottom of the form. Call the first one ChoseDBButton and set the Text as "Choose Database(s)". Call the second one CancelExitProgramButton and set the Text as "Cancel / Exit" (See Figure 2)

FIGURE 2

Double-click the ChooseEnvironment form. Since it is the initial form you want to start with, set the following code. The FormStartPosition tells the computer where on the monitor to place the form when the program runs.

Public Class ChooseEnvironment
   Private Sub  ChooseEnvironment_Load(ByVal sender As System.Object _
               , ByVal e As System.EventArgs) Handles MyBase.Load
 Me.StartPosition = FormStartPosition.CenterScreen
 End Sub
 
End Class

Double-click each radio button, the ChooseDBButton and the CancelExitProgramButton. You want to put in the following code right above the "End Class" of the ChooseEnvironment code.

Private Sub Development_CheckedChanged(ByVal sender As System.Object _
            , ByVal e As System.EventArgs) _
Handles Development.CheckedChanged
 If Me.Development.Checked = True Then
 MyEnv = "Dev"
 End If
 End Sub
 Private Sub Test_CheckedChanged(ByVal sender As System.Object, _
              ByVal e As System.EventArgs) _
          Handles Development.CheckedChanged
 If Me.Test.Checked = True Then
 MyEnv = "Test"
 End If
 End Sub
 Private Sub QC_CheckedChanged(ByVal sender As System.Object, _
          ByVal e As System.EventArgs) _
      Handles Development.CheckedChanged
 If Me.QC.Checked = True Then
 MyEnv = "QC"
 End If
 End Sub
Private Sub Production_CheckedChanged(ByVal sender As System.Object, _
    ByVal e As System.EventArgs) _
   Handles Development.CheckedChanged
 If Me.Production.Checked = True Then
 MyEnv = "Production"
 End If
 End Sub
 Private Sub CancelExitProgramButton_Click(ByVal sender As System.Object, _
          ByVal e As System.EventArgs) _
    Handles CancelExitProgramButton.Click
 MsgBox("Ending program as requested.")
 CloseObjects()
 End
 End Sub
 Private Sub ChooseDBButton_Click(ByVal sender As System.Object, _
      ByVal e As System.EventArgs) _
Handles ChooseDBButton.Click 
CreateMyConnection(MyEnv)
 My.Forms.BackupDatabase.ShowDialog()
End Sub

When this code is created, there will be several underlines indicating that Visual Studio doesn't know what variables and objects you're talking about. That's okay. We'll fix that in a moment.

Next, create a new code module called CreateInitialConnection. (Right click solution name -> Add -> Module). All commented out code is used for debugging to verify I'm connecting to the correct servers. You can delete it if you want to.

First, the following Import statements go above the module name:

Imports Microsoft.SqlServer.Management.Smo
Imports Microsoft.SqlServer.Management.Common
Imports System.Data.SqlClient

Then it's time to declare the objects and write the module code. You will need a Server object, a ServerConnection object, and 2 Public string objects. I actually force the users to close the program if they want to choose a different environment. You can change this to disconnect and reconnect to the server objects if you want to, but I'm currently leaving it out of the scope of this discussion.

Module CreateInitialConnection Public ProdServer As New Server()
 Public ProdConn As ServerConnection
 Public MyServerEnv As String
 Public MyEnv As String
 Public Sub CreateMyConnection(ByVal Environment As String)
 If MyServerEnv = Nothing Then
 MyServerEnv = Environment
Else
 MsgBox("Sorry.The program must be closed and restarted to choose a new environment.")
 End
 End If
'MsgBox("MyServerEnv is " & MyServerEnv)
 ProdConn = ProdServer.ConnectionContext
 Select Case MyServerEnv
 Case "Dev"
 ProdConn.ServerInstance = "MyDevServer, 1433"
 Case "Test"
 ProdConn.ServerInstance = "MyTestServer, 1433"
 Case "QC"
 ProdConn.ServerInstance = "MyQCServer, 1433"
 Case "Production"
 ProdConn.ServerInstance = "MyProdServer, 1433"
 Case Else
 MsgBox("You must choose an environment first.")
 End Select
 'MsgBox("Connection String is " & ProdConn.ServerInstance.ToString)
End Sub
End Module

Explanation: For each ServerInstance of the connection object, I set the ServerName and Port# manually. Replace the above with your own ServerName, Port#. This is easier, and faster if you only have a few servers, to code than enumerating your servers. However, if server names change, and they can, you'll have to edit your code and redeploy your program as needed.

Once this code is completed, you will notice all the underlines from the ChooseEnvironment code MyEnv object will disappear.

Add a new module. Call it CloseConnObjects. This code will reset all variables and close all objects when it is called. I do not use the Me.Close() in this because it closes the program the instant I try to open it. Instead, I call this module in my various Exit Program buttons.

Module CloseConnObjects
 Public Sub CloseObjects()
 ProdServer = Nothing
 ProdConn = Nothing
 MyServerEnv = Nothing
 MyEnv = Nothing
 End Sub
End Module

Add a new form. Right click the solution name -> Add -> Windows Form. Call it BackupDatbase.

Drag a Listbox over to the new form and enlarge to a size where the user will be able to see all database names easily. Call it DatabaseList.

Add 2 new buttons to BackupDatabase form. Call one BackupDatabaseButton and the other ExitProgramButton. Change the text on the first to "Backup Database" and the text on the other to "Exit Program". Resize buttons as needed. (See Figure 3)

FIGURE 3

Double-click the BackupDatabase form and it will take you to a new code window.

You will need to add the following import statements above your BackupDatabase form code:

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

You will also need to append a last import statement, using the name of your solution and the new module. This will make sure that your CreateInitialConnection actually connects.

Imports SMO_BackupDatabase.CreateInitialConnection

The below code enumerates all the databases in the environment (or instance) that the user has selected. It does not show Snapshots or system databases.

Public Class BackupDatabase
 Private Sub BackupDatabase_Load(ByVal sender As System.Object, _
         ByVal e As System.EventArgs) _
Handles MyBase.Load
 Me.StartPosition = FormStartPosition.CenterScreen
 '##### Add list database code here #####
 Dim db As Database
 For Each db In  ProdServer.Databases
 If Not db.IsDatabaseSnapshot And Not db.Name = "tempdb" Then
 DatabaseList.Items.Add(db.Name)
 End If
 Next
 Try
BackupDatabaseButton_Click(DatabaseList.SelectedItem.ToString, e)
 Catch
             MyExeception   As System.NullReferenceException
   'Call MsgBox("You must select a database to continue.")
 End Try
 db = Nothing
 'Clear out values
End Sub

Above the "End Class", you'll need to add the Backup

Database button code. It uses the dating format that SQL Server's database

maintenance plans use when backing up the databases.

Private Sub BackupDatabaseButton_Click _
  (ByVal sender As System.Object, ByVal e As System.EventArgs) _
  Handles BackupDatabaseButton.Click
 ' If no database is selected, exit this event handler
 If DatabaseList.SelectedIndex = -1 Then Exit Sub
 MsgBox("Database being backed up. Please be patient.")
 '##### Add backup code here #####
 Dim dbName As String = DatabaseList.SelectedItem.ToString
 Dim MyBackup As New Backup
 Dim ProcessDate As Date = Now()
 Dim BackupTime  As String
 Dim DeviceLocation    As String
 Select Case MyServerEnv
 Case "Dev"
 DeviceLocation = "MyDevServer\SQL_BAK_DEV\"
  Case "Test"
 DeviceLocation = "MyTestServer\SQL_BAK_TEST\"
 Case "QC"
 DeviceLocation = "MyQCServer\SQL_BAK_QC\"
 Case "Production"
 DeviceLocation = "MyProdServer\SQL_BAK\"
 Case Else
 DeviceLocation = Nothing
 MsgBox("You must choose an environment first.")
 Me.Close()
 End Select
 'Sets the share name where the backup will be stored
  BackupTime = ProcessDate.ToString("yyyy") _
           & ProcessDate.ToString("MM") _
  & ProcessDate.ToString("dd") _
           & ProcessDate.ToString("HH") _
   & ProcessDate.ToString("mm")
 MyBackup.Action = BackupActionType.Database
 MyBackup.BackupSetName = dbName & "Backup"
 MyBackup.Database = dbName
 Dim MyDevice As BackupDeviceItem = New BackupDeviceItem( _
 "\\" & DeviceLocation & dbName _
        & "_db_" _
  & BackupTime _
  & ".BAK", DeviceType.File)
 MyBackup.Devices.Add(MyDevice) 
 MyBackup.SqlBackup(ProdServer)
 MessageBox.Show(dbName & " has been successfully backed up in " _
       & "the SQL_BAK folder with the following FileName: " _
 & CStr("\\" & DeviceLocation & dbName _
       & "_db_" & BackupTime _
 & ".BAK"))
 dbName = Nothing
 MyBackup = Nothing
 ProcessDate = Nothing
 BackupTime = Nothing
 MyDevice = Nothing
 DeviceLocation = Nothing
 DatabaseList.SelectedIndex = -1
 'Clear all variables
End Sub

Above the "End Class", but below the BackupDatabaseButton code, add the following code for the Exit Program button.This code tells the user it is closing the program and closes all objects as it does so.

Private Sub ExitProgramButton_Click(ByVal sender As System.Object _
     , ByVal e  As System.EventArgs) _
  Handles ExitProgramButton.Click
 MsgBox("Program terminating as requested.")
 CloseObjects()
 End
End Sub

Then verify in the ChooseEnvironment code that all the

underlines have disappeared. If you've done everything correctly, you should

be good to go.

Save the program, then go to Debug -> Start Debugging and

test it on your servers. I recommend testing on Dev first. It usually takes a

moment for the database list to appear, so if you want to stick an additional

message box in the ChooseDBButton code that tells the end user to wait, that

might help.

The most common error with debugging here is forgetting to

substitute your real ServerName\InstanceName and Port # in the

CreateInitialConnection and BackupDatabaseButton sections of code. So if

you're running into connection errors, those are the first two places you

should look.

The way I've got this coded is that you can only backup 1

database at a time. However, you do not have to close the program to backup

another database in the same environment. Just keep choosing databases and

clicking the "Backup Database" button to your heart's content. When

you're done playing with one environment, close the program and re-open to

switch to another environment.

Once you've debugged your code, it's time to create the

program for distribution. This is a great opportunity to allow Accounting to

do a database backup before they run month end or the development team to

backup a database before they put in a release.

Building your application installation file

Please note, this is a brief summary intended for those

(like myself) who have never done this before. The scope of this article does

not include all the possible variations or security options available to you.

Double-click My Project on Solution Explorer. Make sure the

Application tab is highlighted and that the Startup Form is listed as

ChooseEnvironment.

Using the main menu in VS, navigate to File -> Add ->

New Project -> Other Project Types -> Setup and Deployment. The default

name of the new project is Setup1 and it will bring up a new page in your

project called File System. (See Figure 4)

FIGURE 4

Click Setup1 in Solution Explorer. Look at the properties window. Here is where you will enter useful information such as Author's name, version #, localization information and a description of the program. (See Figure 5)

FIGURE 5

Right-click Setup1 and choose Properties. VS will bring up a window telling you the default location of where the .msi file will be created. You can change this if you want, but don't change it too much. Keeping the .msi file with your project will make finding it much easier at a later date. Click Cancel to exit.

Right-click the Application Folder and choose Add -> Project Output. A window will pop up asking what output. You'll want to make sure Primary output and a configuration of Active are chosen. Click OK. (See Figure 6)

FIGURE 6

Right-click the Primary Output and choose "Create Shortcut to Primary Output..." which will force a shortcut to be created. (See Figure 7) Rename the shortcut to something appropriate and user-friendly. Drag the new item into either the "User's Desktop" or "User's Program Menu" folders. Upon installation, a shortcut in the User's Start -> Program Files menu and/or the User's desktop will be created. If you want one of each, you have to create 2 shortcuts to the application folder and drag them to the appropriate folder.

FIGURE 7

Right-click Setup1 and choose Build this time. This will build your .msi file for later distribution.

If you left the defaults, you'll find your .msi under SMO_BackupDatabase\Setup1\Debug. You can distribute it to whomever might need the ability to backup the database. Double-clicking the .msi installs it automatically.

When all is said and done, this code should work perfectly for remotely backing up databases, as long as you have permission do to so. I'm still learning VB .Net myself, so there's a lot of things missing from this version of my code (error handling, re-initializing the server connection to switch environments, progress bars, etc.). If anyone has any thoughts on how this could be improved, I'd be glad to hear them.

Having fun coding!

by Brandie Tarvin

MSDBA, MSCA

Rate

4.75 (4)

You rated this post out of 5. Change rating

Share

Share

Rate

4.75 (4)

You rated this post out of 5. Change rating