Using Microsoft.Build.Evaluation to publish a database project (.sqlproj)
Asked Answered
R

4

14

I need to be able to publish an SSDT project programmatically. I am looking at using Microsoft.Build to do so but can not find any documentation. It seems pretty simple to create the .dacpac, but how would I either publish to an existing database or at the very least to a .sql file. The idea is to have it do what it does when I right click on the project and select publish. It should compare with a selected database and generate an upgrade script.

This is what I have so far to create the .dacpac:

partial class DBDeploy
{
  Project project;


  internal void publishChanges()
  {
     Console.WriteLine("Building project " + ProjectPath);
     Stopwatch sw = new Stopwatch();
     sw.Start();

     project = ProjectCollection.GlobalProjectCollection.LoadProject(ProjectPath);
     project.Build();
     //at this point the .dacpac is built and put in the debug folder for the project

     sw.Stop();
     Console.WriteLine("Project build Complete.  Total time: {0}", sw.Elapsed.ToString());

  }
}

Essentially I am trying to do what this MSBuild Example shows but in code.

Sorry that this is all I have. The doecumentation on the Build classes is very poor. Any help would be appreciated.

Thanks.

Renfroe answered 3/5, 2012 at 19:40 Comment(0)
J
20

I had to do something similar to this because VSDBCMD which we previously used does not deploy to SQL Server 2012 and we needed to support it. What I found was the Microsoft.SqlServer.Dac assembly which seems to come as part of the SQL Server data tools (http://msdn.microsoft.com/en-us/data/tools.aspx)

When you run this on the client machine you will need the full version of the .NET 4 framework and the SQL CLR types and SQL T-SQL ScriptDOM pack found here: http://www.microsoft.com/en-us/download/details.aspx?id=29065

Code below is from a mockup I made for testing the new deployment method and deploys a given .dacpac file

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Microsoft.SqlServer.Dac;
    using System.IO;

    namespace ConsoleApplication3
    {
        class Program
        {
            private static TextWriter output = new StreamWriter("output.txt", false);
            static void Main(string[] args)
            {

                Console.Write("Connection String:");
                //Class responsible for the deployment. (Connection string supplied by console input for now)
                DacServices dbServices = new DacServices(Console.ReadLine());

                //Wire up events for Deploy messages and for task progress (For less verbose output, don't subscribe to Message Event (handy for debugging perhaps?)
                dbServices.Message += new EventHandler<DacMessageEventArgs>(dbServices_Message);
                dbServices.ProgressChanged += new EventHandler<DacProgressEventArgs>(dbServices_ProgressChanged);


                //This Snapshot should be created by our build process using MSDeploy
                Console.WriteLine("Snapshot Path:");

                DacPackage dbPackage = DacPackage.Load(Console.ReadLine());




                DacDeployOptions dbDeployOptions = new DacDeployOptions();
                //Cut out a lot of options here for configuring deployment, but are all part of DacDeployOptions
                dbDeployOptions.SqlCommandVariableValues.Add("debug", "false");


                dbServices.Deploy(dbPackage, "trunk", true, dbDeployOptions);
                output.Close();

            }

            static void dbServices_Message(object sender, DacMessageEventArgs e)
            {
                output.WriteLine("DAC Message: {0}", e.Message);
            }

            static void dbServices_ProgressChanged(object sender, DacProgressEventArgs e)
            {
                output.WriteLine(e.Status + ": " + e.Message);
            }
        }
    }

This seems to work on all versions of SQL Server from 2005 and up. There is a similar set of objects available in Microsoft.SqlServer.Management.Dac, however I believe this is in the previous version of DACFx and is not included in the latest version. So use the latest version if you can.

Jerilynjeritah answered 13/8, 2012 at 12:3 Comment(4)
Brilliant, using the DACPAC assemblies allow me to take full control of the deployment. Also useful for integration tests!Rizal
Finally, a sensible answer! Thanks - Just want to add that you need to copy/reference the sqlserver.dac.dll from Microsoft Visual Studio ??.0\Common7\IDE\Extensions\Microsoft\SQLDB\DAC\120 - Works really well, exactly what I needed, so simple and fast. This should be accepted!Megrims
Any experience with setting the flag "upgradeExisting" to false? I get an exception telling me "Cannot deploy to existing database when upgrading has been disabled". But I want DacServices to remove the database first. It doesn't work, even if I add options with CreateNewDatabase = trueSidereal
@Sidereal - there is an overload for upgradeExisting: services.Deploy(package, connectionString, upgradeExisting: true);Hydrocephalus
T
3

We need a way tell msbuild how and where to publish. Open your project in Visual Studio and begin to Publish it. Enter all needed info in the dialog, including your DB connection info and any custom SQLCMD variable values. Save Profile As... to a file, e.g. Northwind.publish.xml. (You may then Cancel.) Now we can use this and the project file to build and publish:

// Create a logger.
FileLogger logger = new FileLogger();
logger.Parameters = @"logfile=Northwind.msbuild.log";
// Set up properties.
var projects = ProjectCollection.GlobalProjectCollection;
projects.SetGlobalProperty("Configuration", "Debug");
projects.SetGlobalProperty("SqlPublishProfilePath", @"Northwind.publish.xml");
// Load and build project.
var dbProject = ProjectCollection.GlobalProjectCollection.LoadProject(@"Northwind.sqlproj");
dbProject.Build(new[]{"Build", "Publish"}, new[]{logger});

This can take awhile and may appear to get stuck. Be patient. :)

Tempered answered 7/6, 2012 at 14:4 Comment(4)
I needed to reference both the Microsoft.Build and Microsoft.Build.Framework assemblies for this code to work.Campobello
Make sure to check the return value of Build to see if it was successful or not.Campobello
To monitor build events, use a ConfigurableForwardingLogger and set BuildEventRedirector to a custom IEventRedirector. You can check for errors by checking if buildEvent is a BuildErrorEventArgs.Campobello
Its ok if you want to publish to one DB the whole time, but the DAC seems to allow me to target any server, or databases, so I can create and drop databases as needed, based on the DACPAC.Megrims
C
1

You should use SqlPackage.exe to publish your dacpac.

SqlPackage.exe 
  /Action:Publish 
  /SourceFile:C:/file.dacpac 
  /TargetConnectionString:[Connection string]

Also instead of passing too many parameters you could save your settings into DAC Publish Profile (this can be done from visual studio)

Calfskin answered 11/6, 2012 at 18:48 Comment(1)
should ... should be could :)Megrims
A
1

I wanted to build and publish a database based on a sqlproj file and log helpful information to console. Here's what I arrived at:

using Microsoft.Build.Framework;
using Microsoft.Build.Execution;

public void UpdateSchema() {
    var props = new Dictionary<string, string> {
        { "UpdateDatabase", "True" },
        { "PublishScriptFileName", "schema-update.sql" },
        { "SqlPublishProfilePath", "path/to/publish.xml") }
    };

    var projPath = "path/to/database.sqlproj";

    var result = BuildManager.DefaultBuildManager.Build(
        new BuildParameters { Loggers = new[] { new ConsoleLogger() } },
        new BuildRequestData(new ProjectInstance(projPath, props, null), new[] { "Publish" }));

    if (result.OverallResult == BuildResultCode.Success) {
        Console.WriteLine("Schema update succeeded!");
    }
    else {
        Console.ForegroundColor = ConsoleColor.Red;
        Console.WriteLine("Schema update failed!");
        Console.ResetColor();
    }
}

private class ConsoleLogger : ILogger
{
    public void Initialize(IEventSource eventSource) {
        eventSource.ErrorRaised += (sender, e) => {
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine(e.Message);
            Console.ResetColor();
        };
        eventSource.MessageRaised += (sender, e) => {
            if (e.Importance != MessageImportance.Low)
                Console.WriteLine(e.Message);
        };
    }
    public void Shutdown() { }
    public LoggerVerbosity Verbosity { get; set; }
    public string Parameters { get; set; }
}

This is for .NET 4 and above. Be sure and include assembly references to Microsoft.Build and Microsoft.Build.Framework.

Apomixis answered 8/7, 2016 at 18:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.