Requirements
- Create a command line application that generates the needed SQL scripts to re-create a database (something that can be added as an external tool either to SQL Server Management Studio, or Visual Studio)
- Provide the following argument options:
- Server and Database/Catalog to script
- Directory to dump the files into
- Override default names for the various SQL files: Tables, Stored Procedures, UDFs, and Data
The Console Application
When creating a simple command line application, the first thing you need is a way to parse arguments and to return to the user some “help” documentation on how to use the application commands. There’s no use in re-inventing the parsing algorithms for a simple application, there are plenty out there already written for you. In this case, check out the following two links:- THANKS TO Richard Lopes: http://www.codeproject.com/Articles/3111/C-NET-Command-Line-Arguments-Parser
- IMPROVED BY Jake Ginnivan: http://jake.ginnivan.net/c-sharp-argument-parser
class Program { static void Main(string[] args) { var program = new Program(new Arguments(args), Console.Out); if (!program.Validate()) { program.OutputHelp(); } else { program.Execute(); } } private Arguments _arguments; private TextWriter _logger; public Program(Arguments arguments, TextWriter logger) { this._arguments = arguments; this._logger = logger; } private bool Validate() { if (string.IsNullOrEmpty(_arguments.Single("server")) && string.IsNullOrEmpty(_arguments.Single("s"))) return false; if (string.IsNullOrEmpty(_arguments.Single("database")) && string.IsNullOrEmpty(_arguments.Single("d"))) return false; if (string.IsNullOrEmpty(_arguments.Single("directory")) && string.IsNullOrEmpty(_arguments.Single("dir"))) return false; return true; } private void Execute() { var stacktrace = _arguments.IsTrue("stacktrace"); try { var server = _arguments.Single("server") ?? _arguments.Single("s"); var database = _arguments.Single("database") ?? _arguments.Single("d"); var directory = _arguments.Single("directory") ?? _arguments.Single("dir"); var output = _arguments["output"] ?? new Collection<string> { "tables", "udfs", "procedures", "data" }; var verbose = _arguments.Exists("verbose") ? _arguments.IsTrue("verbose") : (_arguments.Exists("v") && _arguments.IsTrue("v")); var tablesFileName = _arguments.Single("tf") ?? "tables.sql"; var udfsFileName = _arguments.Single("tu") ?? "udfs.sql"; var proceduresFileName = _arguments.Single("tp") ?? "procedures.sql"; var dataFileName = _arguments.Single("td") ?? "data.sql"; string[] files; DatabaseScripter.Execute(server, database, directory, output.Contains("tables"), output.Contains("udfs"), output.Contains("procedures"), output.Contains("data"), tablesFileName, udfsFileName, proceduresFileName, dataFileName, (verbose ? _logger : null), out files); foreach (var file in files) _logger.WriteLine(file); } catch (Exception ex) { _logger.WriteLine("Exception: {0}", ex.Message); if (stacktrace) _logger.WriteLine("StackTrace: \n{0}", ex); } } private void OutputHelp() { _logger.WriteLine("\nMandatory arguments\n"); _logger.WriteLine("{0,-20}{1}", "-server", "The database server."); _logger.WriteLine("{0,-20}{1}", "-database", "The name of the database (catalog)."); _logger.WriteLine("{0,-20}{1}", "-directory", "The directory to output the database scripts to."); _logger.WriteLine("\nOptional arguments\n"); _logger.WriteLine("{0,-20}{1}", "-verbose", "Output information while processing."); _logger.WriteLine("{0,-20}{1}", "-stacktrace", "Include the stack trace in the error message."); _logger.WriteLine("{0,-20}{1}", "-output", "This command can be specified multiple times."); _logger.WriteLine("{0,-20}{1}", "", "Value options: tables, udfs, procedures, data"); _logger.WriteLine("{0,-20}{1}", "", "(by default, all options are selected)."); _logger.WriteLine("{0,-20}{1}", "-tf", "'tables' script output file (default: tables.sql)."); _logger.WriteLine("{0,-20}{1}", "-tu", "'udfs' script output file (default: udfs.sql)."); _logger.WriteLine("{0,-20}{1}", "-tp", "'procedures' script output file (default: procedures.sql)."); _logger.WriteLine("{0,-20}{1}", "-td", "'data' script output file (default: data.sql)."); } }
Scripting the Database
SQL Server Management Studio has a very nice built-in wizard that does everything we need. [one_third][/one_third] [one_third][/one_third] [one_third last][/one_third] [clear] The goal is to use the SMO API to basically reproduce what the wizard does above, all from a command line. The API gives you pretty much everything you need with the Scripter and ScriptingOptions objects. Using these objects, we can easily script Tables, Stored Procedures, UDFs, and Data:var dbServer = new Server(server); var db = dbServer.Databases[database]; var scriptingOptions = new ScriptingOptions { AppendToFile = true, AnsiFile = true, AnsiPadding = true, ChangeTracking = true, ClusteredIndexes = true, ContinueScriptingOnError = false, DriAll = true, IncludeHeaders = true, IncludeIfNotExists = true, Indexes = true, ToFileOnly = true, WithDependencies = true }; var scripter = new Scripter(dbServer) {Options = scriptingOptions}; var paths = new List<string>(); if (includeTables) { var fName = Path.Combine(directory, tablesFileName); ScriptTables(fName, db, scripter, logger); paths.Add(fName); } if (includeUdfs) { var fName = Path.Combine(directory, udfsFileName); ScriptUdfs(fName, db, scripter, logger); paths.Add(fName); } if (includeProcedures) { var fName = Path.Combine(directory, proceduresFileName); ScriptProcedures(fName, db, scripter, logger); paths.Add(fName); } if (includeData) { var fName = Path.Combine(directory, dataFileName); ScriptData(fName, db, scripter, logger); paths.Add(fName); }
Hi,
Can we edit smo script pasted in this link https://gist.github.com/1755925 to have scripts for primary key ddl and triggers and indexes in separate files too. I am new bee to smo .
thanks for your help
jass
I have only one thing to say: WOW
that was fast, thank you very much, it was very helpful.
I have a project where I want to script whole DB to store into SVN
and we do not have any tools that can do that(small company not IT related industry. I am the only one here with SQL and C#. older guy is DBase and excel self taught programmer.).
I am not good with Power Shell, never used it 🙁
Vlad, I’m glad it’s of use to you. It sounds like you have a challenging environment to work in, but also may have opportunities to shine. Happy coding to you.
thanks, the code works like very well except few things that not obviose from the description.
#1. when entering target path need to include “\\” double slash in the path and -verbose option does nothing. I tried it with and without it and got the same result (unless I am missing something)
have a question though (I am doing the research right now but if you or someone know the answer well..)
is it possible to set option to script one file per object in this setup ?
like not all table scripts in one tables file but a file per table script?
thanks
Vlad, I updated the code to account for the verbose option. Not sure about the “\\” slash issue you’re having, maybe check the Arguments.Test.txt file which describes the various command line syntax options available for file paths – at least you got it working :-). I also added a “-if” option which will output each SMO object to a separate file instead (without dependencies). I also added a “database.ps1” file to the download which is a copy with a slight fix (with credits) of https://gist.github.com/1755925 by Vince Panuccio. You may want to check that out, as it does the same thing very nicely using powershell instead.
[…] Scripting your SQL database (using SMO and the command line) (Matt J. Cowan) […]