Search code examples
sql-serverpowershellsql-server-data-toolsdacpac

Convert dacpac into folder structure of database objects with powershell


I'm working on integrating SQL Server databases into our in-house version control/deployment utility, which is built with powershell,and uses Github as a repository.

Using the excellent sqlpackage.exe utility, I have been able to add a process whereby a developer can extract their current changes into a dacpac and store it in Github, then do the opposite in reverse when they want to get the latest version. However, because the .dacpac is a binary file, it's not possible to see differences in git. I have mitigated this somewhat by unzipping the dacpac before storing in in source control, so contained xml files are added instead. However, even though these files are text-based, they are still not easy to look through and find differences.

What I would like to do, is convert the dacpac into a folder structure similar to what would be seen in SSMS (with all the database objects such as triggers, sprocs etc in their respective folders), store that in Github, and then convert it back into a dacpac when a client checks out the code. However, there doesn't seem to be any function in sqlpackage.exe for this, and I can't find any documentation. Is there any command line tool I can use to this through Powershell?


Solution

  • Using the public APIs for DacFx you can load the dacpac, iterate over all objects, and script each one out. If you're willing to write your own code you could write each one to its own file based on the object type. The basic process is covered in the model filtering samples in the DacExtensions Github project. Specifically you'll want to do something like the ModelFilterer code that loads a dacpac, queries all objects, scripts them out - see the CreateFilteredModel method. I've put a sample that should mostly work below. Once you have this, you can easily do compare on a per-object basis.

    using (TSqlModel model = new TSqlModel(dacpacPath))
    {
        IEnumerable<TSqlObject> allObjects = model.GetObjects(QueryScopes);
        foreach (TSqlObject tsqlObject allObjects)
        {
            string script;
            if (tsqlObject.TryGetScript(out script))
            {
                // Some objects such as the DatabaseOptions can't be scripted out.
    
                // Write to disk by object type
                string objectTypeName = tsqlObject.ObjectType.Name;
                // pseudo-code as I didn't bother writing.
                // basically just create the folder and write a file
                this.MkdirIfNotExists(objectTypeName);
                this.WriteToFile(objectTypeName, tsqlObject.Name + '.sql', script);
            }
        }
    }
    

    This can be converted into a powershell cmdlet fairly easily. The dacfx libraries are on nuget at https://www.nuget.org/packages/Microsoft.SqlServer.DacFx.x64/ so you should be able to install them in PS and then use the code without too much trouble.