Programming SQL Server with SQL Server Management Objects Framework

SQL Server Management Objects (SMO) is a great tool for managing SQL Server. Darko Martinovic demonstrates how to use SMO within a C# solution to create tables, backup the database, and more.

The SQL Server Management Objects (SMO) Framework is a set of objects designed for programmatic management of Microsoft SQL Server and Microsoft Azure SQL Database. This article talks about how to manipulate SQL Server objects by using SMO. There are many situations in which you can use SMO, for example:

  • You would like to develop a custom application to install or update your software. It means a solution to distribute a new version to your clients. Part of that solution is to make database changes, save database state before the process of database changes start and revert to the original state if the process of database changes fails.
  • You would like to develop your custom solution, which implements database changes incrementally. Every developer should suggest a set of DDL changes in the standard database model.
  • You have or would like to develop a custom application for monitoring SQL Server.

Of course, there are many more scenarios than what I mentioned here. For example, in Microsoft’s documentation, the following scenarios are listed:

  • An SMO application might be used to include third-party hardware or software seamlessly into the database management application.
  • You might have to create an application for creating and monitoring the efficiency of indexes, etc.

Beginning with SQL Server 2017, SMO is distributed as the Microsoft.SqlServer.SqlManagementObjects NuGet package to allow users to develop applications with SMO.

In this article, I will demonstrate a few practical examples that will show you part of the power of SMO. Explaining everything about SMO could take a lot of time and would not be fit inside one article.

  • In the first example, I will show you how to establish a connection to SQL Server.
  • In the second example, I will show you how to make some DDL changes, create a table and based on that newly created table, create a T-SQL script for the table re-creation.
  • Finally, I will show you how to programmatically make a backup, restore, and build an SMO library, which will help you in some situations. In the example, I will combine SMO and SQLCLR methods.

Installing the SMO NuGet Package

In order to use SMO NuGet package, you must start Visual Studio Community Edition (or any licensed edition). Create a new project using C# (you can use any .NET language, but these examples are in C#) and choose Console App (.NET Framework). Name this project as you wish; for example, TestSmoConnection, as shown in the image below.

Then start the NuGet Package Manager Console.

In the Package Manager Console, paste the following instruction and press enter.

Several DLLs will be loaded into the solution. In the figure below, I highlighted four DLLs that will be used in the examples.

The highlighted SMO assemblies are described in the table below

Microsoft.SqlServer.Management.Smo

Contains instance classes, utility classes, and enumerations that are used to manipulate Microsoft SQL Server programmatically.

Microsoft.SqlServer.SqlEnum

Contains definitions of various enumerators that exists inside SQL Server. For example, database recovery model is defined like in the snippet below

Microsoft.SqlServer.ConnectionInfo

Contains instance classes, utility classes, and enumerations that are used to manipulate connection objects programmatically

Microsoft.SqlServer.SmoExtended

Contains some base classes for backup, restore, devices, etc.

The First Example – Connecting to SQL Server

Once the project is created and the SMO assemblies are added, the next thing to do is to include namespace references like the listing below:

The remainder of the source code is displayed in the listing below. Copy and paste this code snippet into your Program.cs file.

Modify the server connection if needed. This example runs on the AdventureWorks2014 database, so be sure to connect to an instance hosting that database. Then just press F5 and the console application will start and almost immediately finish. At the end Console.ReadLine() is included in order to keep the application from exiting. At this point, you can view the application’s outputs and investigate which kind of connection is established.

At the very beginning of the first example, a connection is established by creating an object of type ServerConnection. The ServerConnection class is used to establish a connection to the instance of SQL Server. There are various ways to establish the connection. Let us explore some of them.

Just calling code as shown below will establish the connection to the default SQL instance.

Another option is to pass the SQL Server name, as well as username and password as is shown in the snippet below.

In order to view what kind of connection is made (NOTE: the console application should still be running), start SSMS (SQL Server Management Server) and open Activity Monitor. You can find Activity Monitor by right-clicking on the server name. Suppose you are connected as the user sa and, by using Activity Monitor, you can easily find your connection in the Processes section. As you can see in the image below, there is no magic at all. Behind the scenes is the .Net SqlClient Data Provider. SMO is built upon the System.Data and System.Data.SqlClient namespaces. Instead of writing T-SQL inside your .NET code, in the case of SMO, you will use dot notation.

The most interesting property of the ServerConnection object is called LoginSecure, which will switch between integrated and SQL Server security. In order, to establish a connection to the server by using SQL Server Authentication, set this property to false. The following code checks the value to determine which way to log in:

Working with SMO Objects

SMO objects are loaded only when specifically referenced. Object properties are only partially loaded when the object is created. The remaining objects and properties are loaded when they are referenced directly.

To illustrate, first press any key to quit the first demo. Then put the cursor to the highlighted line, as shown in the image below, and press CTRL + F10 or, from the context menu, choose Run To Cursor. The console application will start again and stop on the highlighted line.

Right-click on the server variable and choose Quick Watch from the context menu. You will notice a delay because we are accessing the server properties for the first time and, to determine property values, SMO must execute T-SQL in the background. After expanding server properties by clicking on the plus sign, the Quick Watch window will look like the image below.

For example, if you would like to access the server.BackupDirectory property…

…the following T-SQL is executed in the background, which can be proved easily by starting SQL Profiler.

Stop code execution before continuing with the rest of the article.

Creating Objects

Let’s take another example. The source code of this example is located in CreateTable.cs inside the SmoIntroduction project which you can download here. The purpose of this demo is to show how easy it is to create a table and generate the T-SQL script to recreate the newly created table. At the end of this example, you will have the table and the T-SQL script.

In the project, there is one configuration file named app.config. Open app.config and adjust the connection string settings to point to the SQL Server instance that contains AdventureWorks2014. Make sure that SmoIntroduction is set as the startup project. The startup object in the project should be SmoIntroduction.CreateTable.

After pressing F5, the result of execution will be the creation of the table. The table name is hardcoded into the solution. The result, the newly created table with primary key and clustered index is displayed in the image below.

The T-SQL script is displayed in the Notepad window is shown in the image below.

Stop the execution and open up the CreateTable.cs file to review the code required to create the table. The Table object represents a SQL Server table. There are three possible constructors available to make an instance of the table object. In the example, I used the one in which you have to specify the table and the schema name, as well as the database name as shown in the snippet below.

To add a column to the table, a column object is created with the table name, column name and data type. You can add other properties such as a default value.

You can add constraints, keys, and indexes. The following code shows how to add the primary key index.

You can also create a memory-optimized table if you are running SQL Server 2016 SP1 or later. To create the memory-optimized table instead, right-click the project and change the startup object to SmoIntroduction.CreateMOTable. The SmoIntroduction project should also be set as the startup project.

When creating a memory-optimized table, you have to specify two properties as shown in the snippet below. The source code of this example is located in CreateMOTable.cs inside the SmoIntroduction project.

The table property Durability is the type of DurabilityType, which is an enumerator. As you probably guess, the enumerator values could be SchemaOnly and SchemaAndData.

When working with memory-optimized tables, there are two types of indexes, Hash index and Range index. To create a Range index, you could use the snippet as shown below.

In order to create a Hash index, you have to change the IndexType property to IndexType.NonClusteredHashIndex,and to specify the BucketCount property as shown in the snippet below.

The result is shown in the image below.

Of course, as you probably know, any database that will contain memory-optimized tables needs to have a single MEMORY_OPTIMIZED_DATA filegroup having at least one container, which stores the checkpoint files needed by SQL Server to recover the memory-optimized tables.

You can create the filegroup by using SMO as shown in the snippet below. Notice that it passes as the third parameter in the FileGroup constructor a value FileGroupType.MemoryOptimizedDataFileGroup.

The result is shown in the image below.

The code for creating a container (data file) for memory-optimized data is similar. You can do that using SMO, by using snippet as shown below.

(NOTE: Describing how to work with memory-optimized table is beyond the scope of the article. If you would like to know more about memory-optimized tables, please read SQL Server Internals: In-Memory OLTP (Second Edition) by Kalen Delaney, which can be found here. )

Once the table is in place, the Script method of the table object is used to generate a table creation script.

In this example, the function MakeOptions(), is used to control exactly what will be scripted out. The partial function is shown below.

A key concept in using the SMO object library is the Unique Resource Name (URN). The URN uses a syntax similar to XPath. The XPath is a hierarchy path used to specify an object in which each level has qualifiers and functions. In SMO, the URN has two elements, the path and attribute naming that has limited functionality. The path is used to specify the location of the object whereas the attribute naming allows for a degree of filtering.

An example of a URN for our table is .

The URN of an object can be retrieved by referencing its URN property.

Another method of scripting database and object properties in SMO can be accomplished by the Scripter class. The Scripter class also uses URNs as parameters that pass object references to the method of the Scripter object. An example of using URN is shown in the snippet below. Notice that the code passes table.URN to the Scripter object.

Using SMO for Managment

And finally, let’s take a look at our most complex example.

There is a situation when you have to do a lot of DDL changes, as a part of applying the new version of your software to your client. In that case, there are a couple of tasks you have to accomplish.

  • First, you have to check that the database is online.
  • Then you have to save the database state before start process of DDL changes. Unfortunately, this can not be accomplished by taking a database snapshot because database snapshot is limited only to Enterprise Edition. The most general solution is to take a copy-only backup. But, before taking a copy-only backup ( NOTE : A copy-only backup is a SQL Server backup that is independent of the sequence of conventional SQL Server backups. Usually, taking a backup changes the database and affects how later backups are restored. However, occasionally, it is useful to take a backup for a special purpose without affecting the overall backup and restore procedures for the database. Copy-only backups serve this purpose), you have to be sure that there is enough disk space to save the copy-only backup file.
  • Then you have to put your database in Restricted User Access mode in order to prevent connections to the database when the process of DDL changes start.
  • After that, it’s safe to start the process of DDL changes.
  • If the process of DDL changes does not finish successfully, you have to restore the database state by starting restore process using the backup taken at the very beginning.
  • And finally, you have to put the database back in Multi-User Access mode.

Simplified, the process looks like in the diagram below.

This is a very simplified scenario in which SMO & SQLCLR can help for databases up to 100GB and works with SQL Server Standard Edition.

Before explaining how to accomplish some of the tasks listed above, let’s see how the source code that comes with the article is organized.

In the solution, you can find four projects as shown in the image below.

The table below explains what each of the solution projects does do.

DBHelper

SMO library which has, as a result, a DLL. In the library, you can find a lot of methods to work with SQL Server database.

IOHeLper

SQLCLR project which has, as a result, a DLL that could be published on the database server. The resulting assembly contains methods to determine free disk space on the particular drive, list files in a particular directory and delete a file in particular directory.

Tester

A console application that could be useful to make some tests.

SmoIntroduction

A console application that was our first example

At this stage, you have Visual Studio already started and open to the solution file dbHelper.sln. If not so, please do that. Next thing to do is to publish the SQLCLR project on your database server.

Using SMO with SQLCLR

In the solution, there is an SQLCLR project named IOHELPER. Because this is the article about SMO, I will just briefly describe IOHELPER. The result of compiling the IOHELPER project is an assembly as shown in the image below.

The assembly SimpleTalk.SQLCLR.IoHelper should be deployed to the database server. It is an UNSAFE assembly and should be signed with the asymmetric key. You should change the key path and password in the PreDeployment.sql file before publishing the assembly, as shown in the listing below.

After publishing the assembly, the database model is extended by two stored procedures and one function as shown in the image below.

IOHELPER.FreeSpace determines free space on the specific drive. The logic of determining free space on the particular drive is accomplished in DriveHelper.cs.

IOHELPER.DeleteFiles, as the name implies, deletes files on the file system. It takes two mandatory input parameters, @path, and @filter. The source code is located in DeleteFiles.cs.

Finally, IOHELPER.FileHelper will give us information about files in a specified directory. .NET code is located in FileHelper.cs. In order to list all files in specified directory, the DirectoryInfo class is used.

SQLCLR objects can be used with SMO as shown in the snippet below.

Testing the Solution

After publishing the SQLCLR project to the database, it is time to test the functionality.

In order to test the solution, right-click on the project Tester and from the context menu choose Set as a Startup Project. The Tester project does what its name implies.

In the project, there is only one program file named Program.cs and one configuration file named app.config. Open app.config and adjust the connection string settings to point to the SQL Server instance that contains AdventureWorks2014 or another database. To clarify, you can perform the rest of tests with a database of your choice.

The very beginning of the Program class checks the status of the database. To do that, it simply calls IsTheDatabaseOnLine which is just a wrapper around the database property Status.

DBGeneral is a static class found in the DBHelper project that contains a few static methods. One of them is IsTheDatabaseOnLine. That method uses the property of the SMO class Database called Status to determine the status of the database. The Database class, as the name implies, represents an SQL Server database.

It is interesting to find out what T-SQL command is executed by SMO behind the scenes. In the code, you will find every static method with the T-SQL equivalent.

The result of the first test is displayed in the console windows as shown in the image below.

The next test demonstrates how to put the database in Restricted User Access Mode and after that revert back to Multiple User Access Mode. In order to do that, the code calls PutDbInUserMode as shown in the listing below.

As the name implies, the enumerator DatabaseUserAccess which has three values Single, Restricted and Multiple is required. The second parameter here is boolean which determines whether to kill the current user processes. If the database already in the specified mode, nothing happens. Otherwise, the UserAccess property is set to the new value. After that, you should call Alter and Refresh methods of the database object, as shown in the snippet below.

It is very interesting which T-SQL command is executed by SMO behind the scenes. SMO is interested only in user processes that perform locking. So, the following T-SQL is executed to find out session ids. And after that SMO uses a simple KILL<session_id>.

In order to put the database in Restricted User Access Mode, following T-SQL command is executed behind the scenes.

The output from the console application looks similar like in the image below.

The next test shows how to do a copy-only backup. SMO classes include two categories: instance classes and utility classes. The instance classes represent SQL Server objects such as servers, databases, tables, triggers, and stored procedures. On the other hand, the name Utility could be confusing and someone may think that this class is not instantiated and that they are static. That is not true, these classes use the new operator in order to create objects. The word Utility means that they are used to accomplish some tasks.

In SMO, the Backup class and the Restore class are utility classes that provide the tools to accomplish the specific tasks of backing up and restoring a database. A Backup object represents a specific backup task that is required instead of a Microsoft SQL Server object on the server instance.

As you can see in the snippet below, there are only a few parameters passed to the BackupDatabase method. The method is just wrapper around the Backup and Restore classes defined in SMO.

Part of the method implementation is displayed in the snippet below.

As I wrote earlier, the test will take a copy-only backup. In order to avoid user input in determining backup file name, the naming convention is used. Every backup file has following naming convention.

The backup file name is written as an extended property LastBackup on the database level.

The backup file name is as shown in the image below as an extended property.

The backup file is saved on the server.BackupDirectory which is described earlier in the article. Compression is turned on by default in the function, and there is an option to verify the backup file. So, if the backup file does not pass verification, the method returns False. In the snippet below is shown the process of verifying the backup file.

You can explore several of the tests on your own. In short, they include shrinking the database log, changing the database recovery model, determine the log size, etc. The two last tests determine free disk space on the server drive and deleting old backup files.

In order to determine free disk space( NOTE : There is a possibility to determine free disk space by executing T-SQL EXEC master..xp_fixeddrives, but it has many limitations), there is no need to access an ADO.NET method. Instead, it is possible to call ExecuteWithResults in context of database objects. The method returns an object of type DataSet, as shown in the following snippet.

Summary

Instead of a conclusion, as a final word I would like to say that SMO is a great framework, which will help you in many situations. When in combination with SQLCLR, it can help you develop a very robust solution. Instead of using T-SQL syntax inside the .NET code, you can develop solutions by using standard dot notation.

The solution source code can be found on GitHub here.