Search code examples
c#smo

How to iterate through database objects in C# using SMO


I am trying to backup my database objects' scripts to disk. I have been able to put together the below code that does this, but I have to specify each object type (eg Tables, Functions, Stored Procedures).

Is there a way to loop through all these objects without specifying each collection?

using System;
using System.Data;
using Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlServer.Management.Common;
using System.IO;
using System.Text;

public static void BackupDB(Server srv, Database db, string savePath)
{
    string objPath;
    string objFile;
    string objTxt;

    foreach (StoredProcedure obj in db.StoredProcedures) 
    {
        if (obj.IsSystemObject == false)
        {
            objPath = savePath + "Stored Procedures\\";
            Directory.CreateDirectory(objPath);

            objFile = objPath + obj.Schema + "." + obj.Name + ".sql";
            objTxt = GetScriptString(srv, obj);
            File.WriteAllText(objFile, objTxt);
        }
    }

    foreach (Table obj in db.Tables)
    {
        if (obj.IsSystemObject == false)
        {
            objPath = savePath + "Tables\\";
            Directory.CreateDirectory(objPath);

            objFile = objPath + obj.Schema + "." + obj.Name + ".txt";
            objTxt = GetScriptString(srv, obj);
            File.WriteAllText(objFile, objTxt);
        }
    }

static private string GetScriptString(Server server, SqlSmoObject obj)
{
    StringBuilder output = new StringBuilder();
    Scripter scr = new Scripter(server);
    var script = scr.EnumScript(new SqlSmoObject[] { obj });
    foreach (var line in script)
    {
        output.AppendLine(line);
    }
    return output.ToString();
}

EDIT: Using Grant Winney's answer, I was able to put together the below method to get iterate through all the objects without repeating lines:

public static void BackupDB(Server srv, Database db, string savePath)
{
    string objPath;
    string objFile;
    string objTxt;

    Dictionary<string, IEnumerable<ScriptSchemaObjectBase>> dboDict =
        new Dictionary<string, IEnumerable<ScriptSchemaObjectBase>>();

    dboDict.Add("Stored Procedures", db.StoredProcedures.Cast<StoredProcedure>().Where(x => !x.IsSystemObject));
    dboDict.Add("Functions", db.UserDefinedFunctions.Cast<UserDefinedFunction>().Where(x => !x.IsSystemObject));
    dboDict.Add("Tables", db.Tables.Cast<Table>().Where(x => !x.IsSystemObject));
    dboDict.Add("Views", db.Views.Cast<View>().Where(x => !x.IsSystemObject));

    foreach (KeyValuePair<string, IEnumerable<ScriptSchemaObjectBase>> dict in dboDict)
    {
        IEnumerable<ScriptSchemaObjectBase> dboObjects = dict.Value;
        objPath = savePath + dict.Key + "\\";
        Directory.CreateDirectory(objPath);

        foreach (ScriptSchemaObjectBase obj in dboObjects)
        {
            objFile = objPath + obj.Schema + "." + obj.Name + ".sql";
            objTxt = GetScriptString(srv, obj);
            File.WriteAllText(objFile, objTxt);
        }
    }

}

Solution

  • Move the similar code into a separate method, and take advantage of the fact that each of those classes implements the same base class, which is where Schema and Name are located.

    You can use LINQ to filter the items first, then pass the filtered list to the other method.

    public static void BackupDB(Server srv, Database db, string savePath)
    {
        string objPath;
        string objFile;
        string objTxt;
    
        BackupObjects(db.StoredProcedures.Cast<StoredProcedure>().Where(x => !x.IsSystemObject));
        BackupObjects(db.Tables.Cast<Table>().Where(x => !x.IsSystemObject));
    }
    
    private static void BackupObjects(IEnumerable<ScriptSchemaObjectBase> objects)
    {
        foreach (ScriptSchemaObjectBase obj in objects)
        {
            objPath = savePath + "Stored Procedures\\";
            Directory.CreateDirectory(objPath);
    
            objFile = objPath + obj.Schema + "." + obj.Name + ".sql";
            objTxt = GetScriptString(srv, obj);
            File.WriteAllText(objFile, objTxt);
        }
    }