microsoft / DACExtensions

DACExtensions contains samples that extend Data-Tier Applications using DacFx. These samples include deployment contributors and static code analysis rules that can be used with Visual Studio as well as examples of how to use the DacFx public mode

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Any way to get the SQL Database project output folder and dacpac file name programatically?

ErikEJ opened this issue · comments

I cannot get the name of the dacpac file to built.

How can I get the current Output path (for example: "bin/Debug") and file Name of the .dacpac??

It seems to be buried deep inside internal classes in Microsoft.VisualStudio.Data.Tools.Package.dll

This is my HACK for now:


        internal static string BuildSqlProj(DTE dte, string dacpacPath)
        {
            if (dacpacPath.EndsWith(".dacpac")) return dacpacPath;
            
            // path ends with .sqlproj!
            var project = GetProject(dte, dacpacPath);
            if (project == null) return null;

            var files = DirSearch(Path.GetDirectoryName(project.FullName), "*.dacpac");
            foreach (var file in files)
            {
                File.Delete(file);
            }

            if (!project.TryBuild()) return null;

            files = DirSearch(Path.GetDirectoryName(project.FullName), "*.dacpac");
            foreach (var file in files)
            {
                if (File.GetLastWriteTime(file) > DateTime.Now.AddSeconds(-1))
                {
                    return file;
                }
            }

            return null;
        }

@ErikEJ There is a SqlTarget property that's readable. I think that I've found the relevant sample code from our tests to read a property from a project - see below. Let me know if this works.

        /// <summary>
        ///  Get project property.
        /// </summary>
        /// <param name="project">The project.</param>
        /// <param name="propertyName">The name of the property to retrieve.</param>
        /// <returns>The property or null if it cannot be retrieved for the given project.</returns>
        internal static Property GetProjectProperty(EnvDTE.Project project, string propertyName)
        {
            #region Check Arguments
            Debug.Assert(project != null, "project is null");
            if (project == null)
            {
                throw new ArgumentNullException("project");
            }

            Debug.Assert(!string.IsNullOrEmpty(propertyName), "propertyName is null or empty");
            if (string.IsNullOrEmpty(propertyName))
            {
                // throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, PowerToolsStrings.ArgExNullOrEmpty, "propertyName"));
            }
            #endregion

            Property property = null;

            if (project.Properties != null)
            {
                foreach (Property prop in project.Properties)
                {
                    Debug.Assert(prop.Name != null, "Property name is null");

                    if (System.StringComparer.OrdinalIgnoreCase.Compare(prop.Name, propertyName) == 0)
                    {
                        property = prop;
                    }
                }
            }

            return property;
        }

Great, I will try it out. Thanks for all your support. Working on a tool to generate EF Core DbContext from a dacpac!

Does not work sadly, returns null... :-(

By default the dacpac will be ProjectName.dacpac.

In the project properties, on the Build tab, you can set the Build output file name if you want to specify a different name. In the proj file, this will create a

<SqlTargetName>YourCustomNameHere</SqlTargetName>

... element in either the project main property group or one of the conditional property groups (debug/release/etc). The code model is probably the same, if you have a NULL for that property, then the output will be ProjectName.dacpac.

@StingyJack Thanks, but I am unable to read the "SqlTarget" or SqlTargetName property using the Project object model - that I my actual issue!

That property only becomes present in the project if the default name is not used. If the property is not present, use the default name.

The output path is specified in one of the conditional project property groups. This also may be where the SqlTargetName property is at if its been specified for a particular build configuration and not another. I don't know firsthand if the project API you are looking at flattens out this build configuration group based on your current solution configuration, but in either case, if you dont see the property, then the dacpac name is going to be project name.dacpac

Here is an example of how to get the primary project output for a specific configuration. I updated it to include dacpac support. It should illustrate traversal of the project structure to find the output path and file that you seek as the XML is not much different from the project API

Yikes, XML parsing...
If if use an alternate name, I am unable to retrieve any name using the Envdte.project API.

In other words what I need is a programmatic way to get the full name and path of the dacpac file produced with the current build configuration.

Yikes, XML parsing...

The data model is the same for all the abstractions that are laid overtop it. I'm not parsing XML; Powershell has converted the whole XML document into a PSObject that I can traverse just like any class definition. I need to be able to work with projects and solutions easily so I chose a means that does not rely on having EnvDTE present. (Though I can use a powershell script from inside the IDE as it does have access to $dte)

In other words what I need is a programmatic way to get the full name and path of the dacpac file produced with the current build configuration.

Yes. An example of which is in the powershell script linked above. The output path (the folder) and the primary output (the dacpac) are two things that have to be retrieved separately. There isnt one single property that gives you what you seek.

The output path is retrieved by getting the value of OutputPath property in the property group for the desired build configuration. The primary output is derived by checking for SqlTargetName in the same property group. If not present there, then check the project's common property group for it, if not there then use the name of the project.

@StingyJack Thanks a lot - I just wish the SqlProj object model was more on par with other project types.

Aside from using SqlTargetName in addition to AssemblyName, its the same model as other project types. It can and does produce a CLR assembly in addition to a dacpac, the extra property to name the dacpac seems like a sensible option.

However I do wish it was not inferred like it is. The new SDK style project probably had an influence on this decision. I hate that new format as it only serves to hide information or make it secret in some way (like this does), and has only caused bugs for me so far with the way it auto includes any file that may be littering the developer's filesystem. Its like nobody remembers how much of a PITA ASP.NET web site projects (and classic asp) were 10 years ago.