Publish not transforming Web.config for Website project [duplicate] - asp.net

It doesn't seem to be possible to change the Build Configuration of Visual Studio 2010 Website Projects (as opposed to Visual Studio Web Applications), and changing the Build Configuration is a key part of enabling Web.config transformations (it's not possible to change the configuration to anything except Debug).
How do I get Web.config transformations to work with Visual Studio 2010 Website projects if it's not possible to change the Build Configuration?

I'd prefer not to use entire an Web Application Project solution out of box.
My solution is to use the XmlTransform task defined in Microsoft.Web.Publishing.Tasks.dll directly (this task is the core of WebConfigTransformation)
This way it is flexible enough and does exactly what you expect it to do.
For example here is the WebSiteTransformator.csproj I'm using for transforming web.config.
Here also is an example of flexibility that is impossible to reach with original WebConfigTransformation: it takes web.Template.config, applies web.$(Configuration).config over it and writes web.config. This allows us to add web.config itself into ignore list in source control. It is still valid csproj to be referenced by website:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<SchemaVersion>2.0</SchemaVersion>
<OutputType>Library</OutputType>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<OutputPath>$(TEMP)\TransformWebConfig\bin</OutputPath>
<BaseIntermediateOutputPath>$(TEMP)\TransformWebConfig\obj\</BaseIntermediateOutputPath>
<IntermediateOutputPath>$(BaseIntermediateOutputPath)$(Configuration)\</IntermediateOutputPath>
<WebFolderName>$(SolutionDir)\MyWebSite\</WebFolderName>
</PropertyGroup>
<ItemGroup>
<Compile Include="Dummy.cs" />
</ItemGroup>
<UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.Tasks.dll"/>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="BeforeBuild">
<TransformXml Source="$(WebFolderName)Web.Template.config"
Transform="$(WebFolderName)Web.$(Configuration).config"
Destination="$(WebFolderName)Web.config" />
</Target>
</Project>

I found a pretty good blog post describing a solution to this here:
http://andrewtwest.com/2010/02/25/using-web-config-transformations-in-web-site-projects/
In short: create an empty project (as long as it is not another website project) in your solution that contains the website. The empty project will give you access to msbuild through its project file, which will allow you to perform transforms on your website web.config.

I used a slightly alternative approach. Still a bit of a hack, but I think a lot more straightforward. This worked for me, but obviously there are a lot of different configurations available so I can't guarantee it'll work for everyone. This revolves around the way that a website is first packaged up in to your AppData folder before being published...
Manually add a Web.Release.config file to the website and add the necessary transforms - obviously there's no 'Add Config Transform' option for websites, hence having to do this manually. Example Web.Release.config:
<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<appSettings>
<add key="MySetting" value="NewValue" xdt:Transform="Replace" xdt:Locator="Match(key)" />
</appSettings>
<system.web>
<compilation xdt:Transform="RemoveAttributes(debug)" />
</system.web>
</configuration>
Inside the website.publishproj file, ensure the configuration is set to Release:
<Configuration Condition=" '$(Configuration)' == '' ">Release</Configuration>
Add the following to the very bottom of website.publishproj (just before </Project>):
<Target Name="AfterBuild">
<MakeDir Directories="$(PackageArchiveRootDir)\..\CSAutoParameterize\original" />
<TransformXml Source="Web.config" Transform="Web.$(ConfigurationName).config" Destination="$(PackageArchiveRootDir)\..\CSAutoParameterize\original\Web.config" StackTrace="false" />
</Target>

Create a publish profile in VS 2017 and then right click on the .pubxml profile in App_Data\PublishProfiles and select Add Config Transform.
See https://social.msdn.microsoft.com/Forums/vstudio/en-US/31f41991-abb2-41de-ad0b-c1379cc7c806/vs-2013-express-for-web-webconfig-transforms?forum=visualstudiogeneral&prof=required

As mentioned in Andriy's comment above, Solution wide build events definitely seems like a cleaner way to do this.
I am adding this as a separate answer, as it gets a little lost in the comment, but IMHO is the best answer. Props to Andriy K and Sayed Ibrahim.

If you would prefer not to need a Web.Template.config, I used this:
<PropertyGroup>
<_tempSourceFile>$([System.IO.Path]::GetTempFileName())</_tempSourceFile>
<_tempTransformFile>$([System.IO.Path]::GetTempFileName())</_tempTransformFile>
</PropertyGroup>
<Copy SourceFiles="$(ProjectDir)Web.config" DestinationFiles="$(_tempSourceFile)"/>
<Copy SourceFiles="$(ProjectDir)Web.$(Configuration).config" DestinationFiles="$(_tempTransformFile)"/>
<TransformXml Source="$(_tempSourceFile)"
Transform="$(_tempTransformFile)"
Destination="$(ProjectDir)Web.config"
StackTrace="false" />
Adapted from an answer here.

Related

How to exclude APP_DATA directory from publishing using custom target "PublishToFileSystem"

I am using a custom target for publishing my web site to a local folder.
The target (found here) looks like:
<Target Name="PublishToFileSystem"
DependsOnTargets="PipelinePreDeployCopyAllFilesToOneFolder">
<Error Condition="'$(PublishDestination)'==''"
Text="The PublishDestination property must be set to the intended publishing destination." />
<MakeDir Condition="!Exists($(PublishDestination))"
Directories="$(PublishDestination)" />
<ItemGroup>
<PublishFiles Include="$(_PackageTempDir)\**\*.*" />
</ItemGroup>
<Copy SourceFiles="#(PublishFiles)"
DestinationFiles="#(PublishFiles->'$(PublishDestination)\%(RecursiveDir)%(Filename)%(Extension)')"
SkipUnchangedFiles="True" />
</Target>
The corresponding msbuild command looks like:
msbuild projectfile.csproj /p:DeployOnBuild=true /p:VisualStudioVersion=14.0 /p:configuration=Release /p:PublishDestination=C:\inetpub\wwwroot\WebSite /T:PublishToFileSystem
That works fine so far. However, I would like to exclude the APP_DATA directory from publishing.
So, is there a way to exclude the APP_DATA directory from publishing? Maybe by excluding it from the file set defined with <PublishFiles Include="$(_PackageTempDir)\**\*.*" />?
Environment:
Visual Studio 2015
MSBuild Tools 2015
The easiest way I found is to set this inside the publish profile.
<ExcludeApp_Data>False</ExcludeApp_Data>
You can do it from solution property. Just right click on solution and choose its property. You will get Package/Publish tab there you just need to check "Exclude files from the App_Data Folder". You can check at attached screen shot.
Hope this can help you.
It is possible to set the option (suggested by #Vipin Rathore) within the project file:
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<ExcludeFoldersFromDeployment>APP_DATA</ExcludeFoldersFromDeployment>
</PropertyGroup>

asp.net publish - do not copy web.config to output directory

Is it possible to not include web.config in output directory, but still use web.config during building the application?
Unfortunatelly setting Build action to "None" doesn't copy web.config but application cannot compile because web.config is not used then.
I recommend you to look into web.config transformations.
They allow you to have one version for debug and one for release (deployment).
The most common transformation is for connection strings. You can do that like this:
<connectionStrings>
<add name="local" connectionString="Data Source=IPAddress,Port;Initial Catalog=SomeOtherDB;User ID=TopSecretUsername;Password=SecurePassword"
xdt:Transform="SetAttributes" xdt:Locator="Match(name)"/>
</connectionStrings>
Another very common transformation is removing the debug attribute for release:
<compilation xdt:Transform="RemoveAttributes(debug)" />
Read more here:
http://www.asp.net/mvc/overview/deployment/visual-studio-web-deployment/web-config-transformations
A great (free) video course can be found here:
http://www.pluralsight.com/courses/table-of-contents/aspdotnet-mvc3-intro
Can you do something like what's suggested in this SO question?
something like replacing in your prj file where you find the include for web.config with (untested):
<Choose>
<When Condition=" '$(Configuration)'=='DEBUG' ">
<ItemGroup>
<Reference Include="web.config" />
</ItemGroup>
</When>
</Choose>
As Mihai-Andrei Dinculescu so 'Angryly' puts it - unless you have a good reason to really want the file not to be copies rather then use transformations, you should go with transformations. If you really have to not copy the file or transformations are not suited for your needs, you can try this one

How do you use Web.config Transformation?

When I create a deployment package web.config is changed, but I don't understand this part:
I have two web.config transformation files, web.debug.config and web.release.config.
Are these transformation files only available or working when we make a web deployment or make a deployment package? Are the web.config transformations not used when the project runs locally from visual studio (e.g. via IIS Express)?
You are correct.
Config transformations are applied when you deploy or run a deployment package.
They do not transform on compilation.
If you need the transformed config file during compilation, you can get it by editing the project file (.csproj) and adding the below code.
<Target Name="AfterBuild">
<TransformXml Source="$(SolutionDir)WCFServices\Web.config"
Transform="$(SolutionDir)WCFServices\Web.Release.config"
Destination="$(OutDir)WebRelease.config"
StackTrace="true" />
</Target>
Multiple TransformXml tags can be added to get all the required config files. Also, This can be done before or after build.
You can invoke it using MSBuild and an extension called SlowCheetah.
There is an other VS extension called Configuration Transform good for this. If you don't want to install it, but to achieve this, just follow the examples shown in the demo solution to add different build config files and add some new MSBuild tasks in the project files. The download link for the demo solution can be found on the extension's Visual Studio Gallery webpage. This approach doesn't require any extra packages since MSBuild uses XSLT to do the XML tranformation.
Below are the MSBuild tasks added into a project file from the demo solution. In my case, when I followed it for a VS2015 ASP.NET MVC project, I didn't have to put <UsingTask TaskName="TransformXml" AssemblyFile=... in.
<UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.Tasks.dll" />
<Target Name="AfterCompile" Condition="Exists('App.$(Configuration).config')">
<!--Generate transformed app config in the intermediate directory-->
<TransformXml Source="App.config" Destination="$(IntermediateOutputPath)$(TargetFileName).config" Transform="App.$(Configuration).config" />
<!--Force build process to use the transformed configuration file from now on.-->
<ItemGroup>
<AppConfigWithTargetPath Remove="App.config" />
<AppConfigWithTargetPath Include="$(IntermediateOutputPath)$(TargetFileName).config">
<TargetPath>$(TargetFileName).config</TargetPath>
</AppConfigWithTargetPath>
</ItemGroup>
</Target>
<!--Override After Publish to support ClickOnce AfterPublish. Target replaces the untransformed config file copied to the deployment directory with the transformed one.-->
<Target Name="AfterPublish">
<PropertyGroup>
<DeployedConfig>$(_DeploymentApplicationDir)$(TargetName)$(TargetExt).config$(_DeploymentFileMappingExtension)</DeployedConfig>
</PropertyGroup>
<!--Publish copies the untransformed App.config to deployment directory so overwrite it-->
<Copy Condition="Exists('$(DeployedConfig)')" SourceFiles="$(IntermediateOutputPath)$(TargetFileName).config" DestinationFiles="$(DeployedConfig)" />
</Target>
Here is the way I applied in my .csproj file, quite simple:
<Target Name="AfterBuild" Condition="Exists('Web.$(Configuration).config')">
<Exec Command="attrib -R Web.config" />
<TransformXml Source="Web.config" Transform="Web.$(Configuration).config" Destination="Web.config" StackTrace="true" />
</Target>
Also there is a good post on this.
Further, for web.config transformation, Since VS2012 we can add a publish profile - Publish.pubxml (ProjectFolder/Properties/PublishProfiles/Publish.pubxml) to do a FileSystem publish, thus the web.config transformation will happen by default then. Below is a sample
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<WebPublishMethod>FileSystem</WebPublishMethod>
<SiteUrlToLaunchAfterPublish />
<publishUrl Condition="$(OutDir) != ''">$(OutDir)\_PublishedWebsites\$(ProjectName)</publishUrl> <!-- For MSBuild -->
<publishUrl Condition="$(OutDir) == ''">$(MSBuildThisFileDirectory)..\..\_PublishedWebsite\</publishUrl> <!-- For Visual Studio...cant use $(ProjectName) -->
<DeleteExistingFiles>True</DeleteExistingFiles>
</PropertyGroup>
</Project>

Web Deployment Project AfterBuild path problems

I'm currently in the process of setting up a build server for a web project. I'm using Web Deployment Project to create a deployable package and I want to do some simple file administration (copy webDeploy.config -> web.config and delete .csproj files).
My target looks as follows:
<Target Name="AfterBuild">
<Delete Files="$(OutputPath)\*.csproj" />
</Target>
However, inspecting the output of the WDP gives me this
Target "AfterBuild" in file "C:\project\Deployment\Project.Deployment.wdproj":
Task "Delete"
File ".\Debug\*.*" doesn't exist. Skipping.
Done executing task "Delete".
Done building target "AfterBuild" in project "Project.Deployment.wdproj".
The Deployment path does indeed contain a Debug path. What am I doing wrong?
If you want to use wildcards you will have do so in an item list. The item list will take care of expanding the wild cards for you. So in your case:
<Target Name="AfterBuild">
<ItemGroup>
<FilesToDelete Include="$(OutputPath)\*.csproj" />
</ItemGroup>
<Delete Files="#(FilesToDelete)" />
</Target>
I tried it myself and was stunned but the explanation is simple: You cannot use wildcards (MSBuild Team Blog).
Sample:
<ItemGroup>
<ProjectConfigFiles Include="$(OutputPath)\*.csproj" />
</ItemGroup>
<Target Name="AfterBuild">
<Delete Files="#(ProjectConfigFiles)" />
</Target>

VBC + NAnt. Error compiling WinForm

It should first be noted that I am trying to avoid rewriting all my scripts to use msbuild.
I have noticed that there are several problems when using NAnt with the VBC task and compiling a WinForms application. The main problem seems to be that VBC can't find Sub Main. This is odd, since from within VS, there is no indication that there is any sort of difference between my call to vbc and msbuild's call to vbc.
Does anyone have any insight into a solution to this problem or a way to force the creation of the rest of the partial classes that might/might not be being produced by MSBuild/VS?
Sample Build Script:
<?xml version="1.0" encoding="utf-8" ?>
<project xmlns="http://nant.sf.net/release/0.85/nant.xsd" name="Test" default="build">
<target name="build">
<vbc target="winexe" output="C:\Test.exe" main="WindowAppNantTest.My.MyApplication" verbose="true" rootnamespace="WindowAppNantTest">
<imports>
<import namespace="Microsoft.VisualBasic"/>
<import namespace="System.Windows.Forms"/>
</imports>
<sources>
<include name="**/**/*.vb"/>
</sources>
</vbc>
</target>
</project>
Error(s):
[vbc] vbc : error BC30420: 'Sub Main' was not found in 'WindowAppNantTest.My.MyApplication'.
It appears like the problem is coming from the main and rootnamespace attributes. What happens when you switch them to be something like the following:
<vbc target="winexe" output="C:\Test.exe" main="MyApplication" verbose="true" rootnamespace="WindowAppNantTest.My">
<imports>
<import namespace="Microsoft.VisualBasic"/>
<import namespace="System.Windows.Forms"/>
</imports>
<sources>
<include name="**/**/*.vb"/>
</sources>
</vbc>
or something like the following:
<vbc target="winexe" output="C:\Test.exe" main="My.MyApplication" verbose="true" rootnamespace="WindowAppNantTest">
<imports>
<import namespace="Microsoft.VisualBasic"/>
<import namespace="System.Windows.Forms"/>
</imports>
<sources>
<include name="**/**/*.vb"/>
</sources>
</vbc>
I'm not sure if you mean you don't want to use msbuild within NAnt or if you don't want to switch to msbuild scripting wholesale.
If it is the latter, then my reply on your other post on the same topic is valid here as well.
You can use NAnt contrib (http://nantcontrib.sourceforge.net/) and use msbuild within your NAnt script.
The reference on the msbuild task is:
http://nantcontrib.sourceforge.net/release/latest/help/tasks/msbuild.html
And the pertinent snippet:
<target name="build" depends="clean">
<msbuild project="ProjectName.vbproj" />
</target>
What you need to do is set the following into your VBC command:
<references>
<include name="System.Windows.Forms.dll"/>
<indlude name="Microsoft.VisualBasic.dll"/>
</references>
This should fix your problem. (I guessed on the second dll as I'm a CS guy) however the syntax for compiling is pretty much the same.
In all the projects I've worked on you always need to set the references to include any DLL's whether they are from .Net, 3rd party or your own (ie Project references) otherwise they won't link in properly.
Give that a go and see what happens.

Resources