Visual Studio Fails to build when adding a predeployment script; - sql-server

I am getting this error when I try to build.
Se
verity Code Description Project File Line Suppression State
Error 04094: "C:\Sources\Repos\Dir\PreDeployment\PreDeployment.sql;C:\Sources\Repos\Dir\PreDeployment\PreDeployment.sql" is an invalid value for the "PredeployItem" parameter of the "SqlScriptDependenciesTask" task. Multiple items cannot be passed into a parameter of type "Microsoft.Build.Framework.ITaskItem". Dir C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Microsoft\VisualStudio\v16.0\SSDT\Microsoft.Data.Tools.Schema.SqlTasks.targets 512
All I have added is a pre deployment script.
<SqlScriptDependenciesTask
PostdeployItem="#(PostDeploy->'%(FullPath)')"
**PredeployItem="#(PreDeploy->'%(FullPath)')"**
BuildExtensionConfiguration="#(BuildExtensionConfiguration)"
DeploymentExtensionConfiguration="#(DeploymentExtensionConfiguration)"
SqlCmdVariables="#(SqlCmdVariables)">
I can't find a solution anywhere?
I have the solution set up as
The post deployment has been working fine. So I added a Pre Deployment. That doesn't seem to
The sqlproj
<ItemGroup>
<PostDeploy Include="PostDeployment\PostDeployment.sql" />
</ItemGroup>
<ItemGroup>
<PreDeploy Include="PreDeployment\PreDeployment.sql" />
</ItemGroup>

Related

SQL Server database project in VS. Order of sql script generation step in build process

I have a SQL Server database project in my solution and several dependencies projects.
When I compiling (in VS) this database project I've get a SQL script and a .dacpac file as result.
But also I want to aggregate all my dependencies projects in one dll and make SQL script/.dacpac file only for this result dll.
I'm using ILMerge.MSBuild.Tasks.ILMerge to aggregate all dll on AfterBuild event in sqlproj. But this aggregation happens after generating SQL script.
How can I enforce SQL script generation in the end?
Build log:
2>------ Rebuild All started: Project: TestCLR, Configuration: Debug Any CPU ------
2> D:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin\Roslyn\csc.exe /noconfig /nowarn:1701,1702,2008 /fullpaths /nostdlib+ /errorreport:prompt /warn:4 /define:DEBUG;TRACE /errorendlocation /preferreduilang:en-US /highentropyva+ /reference:"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\mscorlib.dll" /reference:D:\Work\FF\Sql-Objects\Tools\TestCLR\bin\Debug\TestLogic.dll /reference:"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Data.dll" /reference:"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.dll" /reference:"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Xml.dll" /debug+ /debug:full /optimize- /out:obj\Debug\TestCLR.dll /subsystemversion:6.00 /target:library /warnaserror- /utf8output /langversion:7.3 TestProcedures.cs
2> Loading project references...
2> Loading project files...
2> Building the project model and resolving object interdependencies...
2> Validating the project model...
2> Writing model to D:\Work\FF\Sql-Objects\Tools\TestCLR\obj\Debug\Model.xml...
2> Writing create script to TestCLR_Create.sql...
2> TestCLR -> D:\Work\FF\Sql-Objects\Tools\TestCLR\bin\Debug\TestCLR.dll
2> TestCLR -> D:\Work\FF\Sql-Objects\Tools\TestCLR\bin\Debug\TestCLR.dacpac
2> ILMerge bin\Debug\TestCLR.dll;bin\Debug\TestLogic.dll;bin\Debug\Sider.dll -> D:\Work\FF\Sql-Objects\Tools\TestCLR\bin\Debug\TestCLR.dll
========== Rebuild All: 2 succeeded, 0 failed, 0 skipped ==========
.sqlproj part with ILMerge (Right now it's on the last place. Right before /Project tag )
<UsingTask TaskName="ILMerge.MSBuild.Tasks.ILMerge" AssemblyFile="$(SolutionDir)\packages\ILMerge.MSBuild.Tasks.1.0.0.3\tools\ILMerge.MSBuild.Tasks.dll" />
<Target Name="AfterBuild">
<ItemGroup>
<MergeAsm Include="bin\Debug\*.dll" />
</ItemGroup>
<PropertyGroup>
<MergedAssembly>$(ProjectDir)$(OutDir)RedisCLR.dll</MergedAssembly>
</PropertyGroup>
<Message Text="ILMerge #(MergeAsm) -> $(MergedAssembly)" Importance="high" />
<ILMerge InputAssemblies="#(MergeAsm)" OutputFile="$(MergedAssembly)" TargetKind="SameAsPrimaryAssembly" />
</Target>
Much appreciate Mr. Solomon Rutzky. This solution was wery helpful.
<PropertyGroup>
<SqlBuildDependsOn>
BeforeSqlBuild;
$(SqlBuildDependsOn);
</SqlBuildDependsOn>
</PropertyGroup>
<Target Name="BeforeSqlBuild">
ILMerge things
</Target>
But there was one curious thing. Dll comes to Bin\debug in the end of flow (I think on PostBuildEvent step or near). And if I move ILMerge upper on the flow - it returns error, that there are no DLLs. Changed config to gater dll from obj folder and from dependant project.
Interesting question. I don't know of any easy way to do this, or if it's even possible in the first place. However, there are two options that I know of for altering the build process:
Option 1
Inject a build step / target. SSDT is missing at least two build steps/targets — "BeforeSqlBuild" and "BeforePublish" — because the "AfterBuild" target is too late in the process (as you have discovered).
To get around the "CLR strict security" debacle introduced in SQL Server 2017 (documented in the following post of mine: SQLCLR vs. SQL Server 2017, Part 3: “CLR strict security” – Solution 2), I updated the .sqlproj file by placing the following at the end, just before the closing </Project> tag (to sign the assembly with a certificate):
<PropertyGroup>
<SqlBuildDependsOn>
BeforeSqlBuild;
$(SqlBuildDependsOn);
</SqlBuildDependsOn>
</PropertyGroup>
<Target Name="BeforeSqlBuild">
<Exec Command="{DOS / Windows commands}"/>
</Target>
You can keep the <Exec Command="..."/> and/or add the contents of your existing "AfterBuild" target. HOWEVER, even with this performing the action at the correct time, I'm not entirely certain that you will be able to change the assembly name that it will want to serialize and put in the "Create" script (i.e. switch from using "TestCLR.dll" to "RedisCLR.dll"). You can try re-assigning the build variable, but I've never tried that and am not sure it's allowed. In which case the next option might help.
(Please vote for my enhancement request to improve the SSDT build process:
Add MSBuild predefined Targets for "BeforeSqlBuild" and "BeforePublish" to SSDT SQL Server Database Projects)
Option 2
Use .NET to create a custom build task that has access to the objects, files, and overall process:
Customize Database Build and Deployment by Using Build and Deployment Contributors
BuildContributor Class
BuildContributorContext Class
Walkthrough: Extend Database Project Build to Generate Model Statistics

TeamCity SQL Project : Microsoft.Data.Tools.Schema.SqlTasks.targets was not found

TeamCity
I have a TeamCity project with a .NET CLI (dotnet) build step for a Visual Studio Solution ( .sln ).
The solution includes a SQL Project ( .sqlproj ).
Running this build-step gives error :
[build] D:\TeamCity\buildAgent\work\e9ff385151b10e4c\Services\AccountCharacteristics\Database-RoundHouse-Deploy1\Database\Database.sqlproj(67,3): error MSB4019: The imported project "C:\Program Files\dotnet\sdk\2.2.101\Microsoft\VisualStudio\v11.0\SSDT\Microsoft.Data.Tools.Schema.SqlTasks.targets" was not found. Confirm that the path in the <Import> declaration is correct, and that the file exists on disk.
Database.sqlproj
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">11.0</VisualStudioVersion>
<!-- Default to the v11.0 targets path if the targets file for the current VS version is not found -->
<SSDTExists Condition="Exists('$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\SSDT\Microsoft.Data.Tools.Schema.SqlTasks.targets')">True</SSDTExists>
<VisualStudioVersion Condition="'$(SSDTExists)' == ''">11.0</VisualStudioVersion>
</PropertyGroup>
<Import Condition="'$(SQLDBExtensionsRefPath)' != ''" Project="$(SQLDBExtensionsRefPath)\Microsoft.Data.Tools.Schema.SqlTasks.targets" />
<Import Condition="'$(SQLDBExtensionsRefPath)' == ''" Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\SSDT\Microsoft.Data.Tools.Schema.SqlTasks.targets" />
So it appears that SQLDBExtensionsRefPath = C:\Program Files\dotnet\sdk\2.2.101\Microsoft\VisualStudio\v11.0\SSDT\
Apparently the .sqlproj defaults to 11.0 -- a very old version of Visual Studio.
How does SQLDBExtensionsRefPath get set ? How can I install/config the agent for the correct path ?
My agent has :
C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\MSBuild\Microsoft\VisualStudio\v16.0\SSDT\Microsoft.Data.Tools.Schema.SqlTasks.targets
Also I notice that a NuGet package has been released :
https://www.nuget.org/packages/Microsoft.Data.Tools.Msbuild/
But it's not clear how to use it. It seems clear I need to reference the package in the VS .csproj. But then how to ensure that Visual Studio and Team City point to the Microsoft.Data.Tools.Schema.SqlTasks.targets in the package ( under the packages folder ) ?
Set it as an environment variable with
setx SQLDBExtensionsRefPath "C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\MSBuild\Microsoft\VisualStudio\v16.0\SSDT"
or by adding via the 'environment variables' in the Windows UI
See https://learn.microsoft.com/en-us/archive/blogs/ssdt/part-5-use-your-own-build-and-deployment-agent

How to make SqLite work in Windows 10 at the publication

There is a classic application where Microsoft is used.Entity Framework Core.Sqlite, trying to publish an application through Windows 10 deployment and throws an exception: DllNotFoundException: Unable to load DLL "e_sqlite3": the specified module could not be found. (Exception from HRESULT: 0x8007007E)
Tried adding e_sqlite3 library to " Windows application packaging Project", in end face with the exception: SQLite Error 14: 'unable to open database file' with EF Core code first
I tried different solutions found on the Internet, but not one I did not fit.
Development environment:
Windows 10, Visual Studio 2017, Microsoft.EntityFrameworkCore.Sqlite 2.2.1.0 and Microsoft.Data.Sqlite 2.2.1.0
Here's how I solved both issues.
The first issue is that the native e_sqlite3.dll files are not copied to the Package project's output. The Package project has MSBuild logic in Microsoft.DesktopBridge.targets that is calling the GetCopyToOutputDirectoryItems target of each of its referenced projects (e.g. a WPF project). Since the e_sqlite3.dll files are being included in the referenced project by way of a NuGet package, the way in which they are being included doesn't cause them to be picked up by the GetCopyToOutputDirectoryItems target. I've worked around this by adding the following code to my WPF project:
<Target Name="IncludeNativeBinariesAsOutput" BeforeTargets="GetCopyToOutputDirectoryItems">
<ItemGroup>
<Content Include="$(OutputPath)\x64\e_sqlite3.dll">
<Link>x64\e_sqlite3.dll</Link>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="$(OutputPath)\x86\e_sqlite3.dll">
<Link>x86\e_sqlite3.dll</Link>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<AssignTargetPath Files="#(Content)" RootFolder="$(MSBuildProjectDirectory)">
<Output TaskParameter="AssignedFiles" ItemName="ContentWithTargetPath" />
</AssignTargetPath>
</Target>
The next issue is with the "unable to open database file" error after the necessary native files are where they need to be. I'm thinking this is because it's trying to create the project in a location that is not supported by a Windows Package project. I've handled this by setting a special value that that SqliteConnection looks for to construct a path for the database file. I just added this line to my App constructor class before doing any database operations.
AppDomain.CurrentDomain.SetData("DataDirectory", ApplicationData.Current.LocalFolder.Path);

Automatic publish of sql server project after each build on VS2015

I am using visual studio 2015 and have a SQLServer project, what I want is that whenever i build the project it automatically gets published to sql server. In TFS we can achieve it by following
Update Build definition -> Process -> MSBuild arguments -> /t: Build /t:Publish /p:SqlPublishProfilePath = filePath.publish.xml
On local pc we can use the following command to achieve the task
msbuild ProjectName.sqlproj /t:build /t:Publish /p:SqlPublishProfilePath=filePath.publish.xml
But how can we update the build definition of the project so that when I build it on my local machine it gets published automatically without using command line
how can we update the build definition of the project so that when I build it on my local machine it gets published automatically without using command line
To accomplish this, unload your project, edit the project file ProjectName.sqlproj. Then at the very end of the project, just before the end-tag </Project>, place below scripts:
<Target Name="DeployDB" AfterTargets="build">
<Message Importance="high" Text="************** Star Deploying DB **************"/>
<Message Importance="high" Text="Deploying Project: $(MSBuildProjectDirectory)$(MSBuildProjectName).sqlproj" />
<Message Importance="high" Text="Deployment Profile: $(MSBuildProjectDirectory)PublishProfiles$(MSBuildProjectName).publish.xml" />
<MSBuild Projects="$(MSBuildProjectDirectory)$(MSBuildProjectName).sqlproj” Properties=”SqlPublishProfilePath=$(MSBuildProjectDirectory)PublishProfiles$(MSBuildProjectName).publish.xml" Targets="sqlPublish" />
</Target>
You can use some of MSBuild Reserved and Well-Known Properties like $(MSBuildProjectName) and $(MSBuildProjectDirectory), for complete list
BTW, automatic publish will slow you down, and you probably don't need to publish for every build. Under this condition, you can add a Condition for this target so that you select whether you need to automatic publish of sql server project after each build(Do not forget to add a Configuration with LocalAutoDeploy).
<Target Name="DeployDB" AfterTargets="build" Condition="'$(Configuration)' == 'LocalAutoDeploy'">

How to include heat generated wsx files in a smart way in wix installer?

It's a WPF application, with Wix Installer.
I have resourceses folder and I want to include these files in the installer to put next to the executable. I solved generating a resources.wxs file with necessary information about the files under the resources folder using the heat tool. I managed to includ into the main wxs file. For that reason I modified the .wixproj file, adding a before build target action to generate the wxs and include it in the main wxs.
Concern: .wixproj is kind of hidden, there thing that you cannot modify from visual studio, like adding a before build action (pre build action is a different story)
Question: How can I extract the before build action into a separate file?
The before build action in the .wixproj:
<Target Name='BeforeBuild'>
<Exec Command='"%WIX%bin\heat" dir $(SolutionDir)resources -dr ResourcesDir -var var.ResourcesDir -cg ResourceFilesGroup -gg -g1 -sfrag -srd -out $(ProjectDir)Resources.wxs' />
<ItemGroup>
<Compile Include='$(ProjectDir)Resources.wxs' />
</ItemGroup>
You can extract it into a separate file—most project file types do that already. That's how they provide common targets to all projects of a type. A .wixproj has this:
<Import Project="$(WixVersionTargetsPath)" />
To augment your own, simply:
Create an XML file like:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name='BeforeBuild'>
<!-- tasks -->
</Target>
</Project>
Add an Import element inside the Project element and refer to that file:
<Import Project="custom.targets" />
If such a file primarily has Target elements, the convention is for it to have the file extension ".targets".
But there are two drawbacks with Visual Studio:
Visual Studio caches all the project file dependencies and runs the MSBuild internally. So, it you edit the external file, it won't be part of builds using Visual Studio until the project is next loaded. To quickly unload and reload a project, use the project context menu in the Solution Explorer. Workaround: Call MSBuild yourself.
When Visual Studio loads a project, if it includes non-standard external files, it gives a warning. (You can disable it per user by project file path, in the registry, if I recall.)
As an alternative to calling heat directly, you might want to look at the Harvest* targets that WiX provides. Note: As the documentation says, you don't invoke them directly (they're already invoked by the Build target); You simply add items to the ItemGroup they process and set properties they use.

Resources