web.config transform from web.template.xml not working - c#

I'm trying to get web.config transformations working as described here. We've used this method on other projects and it works without issue, but not on this new project.
Here's what I've tried testing without success
Changing name of wpp.targets file in case I got the project name wrong. I know the current one I'm using works since it's the only one that causes web.config to be rebuilt from web.template.xml this transform works. Only the sub templates don't work.
Tried with xdt:Locator="Match(name)"
Tried .config extension vs .xml, our other projects where this works use .xml
Configuration manager is set to use the "Test" configuration for the project I'm working on.
web.template.Test.xml has xdt:Transform="Replace" for the section I want to replace
web.template.xml has the placeholder
Tried removing the "CopyWebTemplateConfig" section from wpp.targets as suggested on the stack question linked below. Our other projects have this and the "PropertyGroup" section commented out and I've tried both combinations.
I've read through the above link multiple times and this related stack question, but can't see what the problem is.
Note The publish transform does work in a way. It creates a web.template.xml file that contains the values from web.template.Test.xml, but does not create a web.config.xml as the wpp.targets instructs. So this is more of an issue with getting the build transform working it seems.
Anyone have an idea of what's missing?
wpp.targets
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- Make sure web.config will be there even for package/publish -->
<Target Name="CopyWebTemplateConfig" BeforeTargets="Build">
<Copy SourceFiles="web.template.xml"
DestinationFiles="web.config"/>
</Target>
<PropertyGroup>
<PrepareForRunDependsOn>
$(PrepareForRunDependsOn);
UpdateWebConfigBeforeRun;
</PrepareForRunDependsOn>
</PropertyGroup>
<!-- This target will run right before you run your app in Visual Studio -->
<Target Name="UpdateWebConfigBeforeRun">
<Message Text="Configuration: $(Configuration): Web.template.$(Configuration).xml"/>
<TransformXml Source="web.template.xml"
Transform="web.template.$(Configuration).xml"
Destination="web.config" />
</Target>
<!-- Exclude the config template files from the created package -->
<Target Name="ExcludeCustomConfigTransformFiles" BeforeTargets="ExcludeFilesFromPackage">
<ItemGroup>
<ExcludeFromPackageFiles Include="web.template.xml;web.template.*.xml"/>
</ItemGroup>
<Message Text="ExcludeFromPackageFiles: #(ExcludeFromPackageFiles)" Importance="high"/>
</Target>
</Project>
web.template.xml
<?xml version="1.0" encoding="utf-8"?>
<!--
For more information on how to configure your ASP.NET application, please visit
http://go.microsoft.com/fwlink/?LinkId=152368
-->
<configuration>
<configSections>
<sectionGroup name="TestSettings"></sectionGroup>
....
</configSections>
....
<TestSettings>
</TestSettings>
....
</configuration>
web.template.Test.xml
<?xml version="1.0"?>
<!-- For more information on using transformations
see the web.config examples at http://go.microsoft.com/fwlink/?LinkId=214134. -->
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<TestSettings xdt:Transform="Replace">
...
</TestSettings>
</configuration>
MSBuild output
Target "UpdateWebConfigBeforeRun: (TargetId:143)" in file "C:\...\Project.wpp.targets" from project "C:\...\Project.csproj" (target "PrepareForRun" depends on it):
Task "Message" (TaskId:93)
Configuration: Test: Web.template.Test.xml (TaskId:93)
Done executing task "Message". (TaskId:93)
Task "TransformXml" (TaskId:94)
Transforming Source File: Web.template.xml (TaskId:94)
Applying Transform File: Web.template.Test.xml (TaskId:94)
Executing Replace (transform line 5, 18) (TaskId:94)
on /configuration/TestSettings (TaskId:94)
Applying to 'TestSettings' element (source line 121, 4) (TaskId:94)
Replaced 'TestSettings' element (TaskId:94)
Done executing Replace (TaskId:94)
Output File: web.config (TaskId:94)
Transformation succeeded (TaskId:94)
Done executing task "TransformXml". (TaskId:94)
Done building target "UpdateWebConfigBeforeRun" in project "Project.csproj".: (TargetId:143)

I had installed StyleCop and that was doing the overwrite for me.
So I uninstalled it and the issue was resolved.
Funny is that I re-installed the StyleCop and the transform was still working!
Also at some points I noticed that I should remove the CopyWebTemplateConfig target section as well.

I've got a solution to my problem, but not sure what the cause is, so not sure if this will solve it in other cases.
I reviewed the output of the MSBuild diagnostic and noticed that towards the end there was another section that copied web.template to web.config. Note that this is after the UpdateWebConfigBeforeRun target already ran and made its updates from the sub template transform file to web.config. It looked like this last step was overriding the web.config with the transform I wanted.
I wasn't sure where this last set of copy instruction was coming from, so I did a search for all files on my PC looking for other wpp.target files. I found another one in Slow Cheetah's extensions folder and saw some section up top that was setting a property "transformOnBuild" to false.
Thinking there was a conflict with SlowCheetah, I uninstalled it and the transformations started working as expected. This was still a bit odd since the other solutions worked with SlowCheetah enabled. On a whim I re-installed SlowCheetah and the transformation continued to work as expected.
So my solution ended up being a re-installation of SlowCheetah. I'm still confused about what the cause of this issue was, so if anyone else posts an answer to this I'll give them the bounty.

I've had a similar issue last week.
Turns out that whenever you add a project to a solution in VS 2010 the right project configuration doesn't get applied all the time.
So you think you have a configuration active but another is actually active on that project, thus the transformation you expect doesn't get applied.
Check the steps in last comment of this issue: Custom solution configuration not showing up in Visual Studio 2010

"I know the current one I'm using works since it's the only one that causes web.config to be rebuilt from web.template.xml this transform works. Only the sub templates don't work."
Does this mean, transformation works but TestSettings section alone does not get transformed?
Can you share build output with msbuild with verbosity set to diagnostic / detailed?

I wrote a blog post about this subject. I use it everyday in our web application. I wrote the blog post because the feature in slowcheetah isn't ready yet.
http://www.locktar.nl/general/use-config-transforms-when-debugging-your-web-application/

Related

Change nuget packages folder and use another NuGet.Config in Visual Studio

I want to change nuget package folder but it doesn't work.
I tried very much tricks but not work.
I restart many times VS but not work.
My config:
Visual Studio Community 2019
Windows 10 1909 x64
I have a VS solution folder
MyProjectSln\
HelloWorld\
bin
nuget_packages
NuGet.Config
I want VS studio nuget package manager console use the "NuGet.Config" file and put all packages downloaded in "nuget_packages"
So nuget packages must be in D:\MyProjetSln\HelloWorld\nuget_packages.
Content example of D:\MyProjetSln\HelloWorld\NuGet.Config :
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<config>
<add key="globalPackagesFolder" value=".\nuget_packages" />
<!-- I have also tried with repositoryPath but not work -->
</config>
</configuration>
The xml syntax is correct in my case (in my file).
Example, when i execute the command "install-package NUnit" from Package Manager Console, it put downloaded packages in D:\MyProjectSln\packages\ and I don't want that.
Thank you for helping !
Just as this document said, the new nuget.config file must be under the same level directory of the project folder rather than put the file inside the project folder.
In other words, it must be located in at least the solution directory or a higher-level directory.
Note: if you use this way, it will act on all the projects in the same level directory and all the projects in the sub directory.
So you should put the new nuget.config file on the D:\MyProjetSln.
Then modify its content as:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<config>
<add key="repositoryPath" value="HelloWorld\nuget_packages" />
</config>
</configuration>
Then, close VS and then reopen your project to enable that function.
In my side,
At the time of writing this message, what I want is not possible at the moment.
Maybe it will be a feature later.

NLog no log output from a referenced project

I am using NLog 4.5.11 in one Visual Studio 2017 solution. It has the nlog.config in the start up project, but the actual logging required is from another project, where NLog is referenced (but no nlog.config exists). Running this solution works fine, the NLog logs are being produced where I expect.
The second VS solution uses (references) both the start up project and the one with the logging. One note on this project is that it is an Excel Add-on. When I run (debug from VS) this second solution, I do NOT get the NLog logging that should have been triggered via the referenced projects' code and NLog logging. I do not get any errors, exceptions, or error files etc.
I have tried also installing the NuGet packages for NLog to the second solution. I also tried adding a copy of the nlog.config to it. I have looked in the build directory and the NLog dlls and config file are being copied there. I have also tried enabling the throwExceptions="true" and internalLogLevel="Trace".
I have been looking on SO and elsewhere, but I cannot find a solution or even how to debug it. All that ever happens is simply no output, which is really frustrating.
NLog.config
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd"
autoReload="true"
throwExceptions="true"
internalLogLevel="Trace" internalLogFile="c:\temp\nlog-internal.log">
<variable name="appName" value="FSIS" />
<variable name="logDir" value="c:\temp\Rbnz.Fsis.Logging" />
<variable name="logDirArchive" value="c:\temp\Archive\Rbnz.Fsis.Logging" />
<targets async="true">
<target xsi:type="File"
name="default"
layout="${longdate} - ${level:uppercase=true}: ${message}${onexception:${newline}EXCEPTION\: ${exception:format=ToString}}"
fileName="${logDir}\${shortdate}.log"
keepFileOpen="false"
archiveFileName="${logDirArchive}\${shortdate}.{##}.log"
archiveNumbering="Sequence"
archiveEvery="Day"
maxArchiveFiles="30"
/>
</targets>
<rules>
<logger name="*" writeTo="default" minlevel="Debug" />
</rules>
</nlog>
Usage inside the code of the referenced project:
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
and later on, inside a method:
logger.Info("Start: CompileSeries()");
logger.Info("Done: CompileSeries()");
I expected that if NLog logging works for a project, then including that project as a reference in another project (in another solution), would trigger the same logging. Obviously I'm incorrect in this assumption.
Based on what you wrote, you have a First Solution that has two projects--a startup project that configures a logger object. That solution has a second project that references the startup project's logger object. Fine.
Now you have a Second Solution. In this solution, you simply reference the First Solution's startup project assembly and the First Solution's second project. Your Second Solution has its own project that's trying to access the logger exposed by the First Solution's referenced projects.
This issue is that your First Solution actually executes its startup project. Your Second Solution does not execute it--it simply references it. Therefore, your logger object isn't getting properly initialized by NLog.
The solution (generically) is to ensure that your logger object is initialized in your Second Solution just as it's initialized in your First Solution. If you want more specific guidance, show specifics as to how your First Solution's startup project initializes the logger object and I should be able to help you replicate that logic in the Second Solution.
Your conclusion that you cannot log from another solution is incorrect. It's all about proper object initialization.
I hope this helps.

How to make Web.Config Transform files work during both build & publish? [duplicate]

How can I merge and make use of Web.debug.config in visual studio 2010 built-in debugger?
This is a known bug. That feature can be used right now only as part of the deploy process.
https://connect.microsoft.com/VisualStudio/feedback/details/523221/have-web-debug-config-apply-during-development
Please upvote it, if you encounter this too, so this will be fixed ASAP.
This is actually quite simple to do and, believe it or not, it seems this is the way VS is designed to work.
Add the following lines verbatim right before the closing "Project" tag of the .csproj file of the project that contains web.config.
<UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.Tasks.dll" />
<Target Name="Transform">
<MakeDir Directories="obj\$(Configuration)" Condition="!Exists('obj\$(Configuration)')" />
<TransformXml Source="Web.Config" Transform="Web.$(Configuration).config" Destination="obj\$(Configuration)\Web.config" StackTrace="true" />
</Target>
Put the following lines verbatim to the post-build event in the project properties of the project that contains the web.config file. Do this for each build configuration you want the transformations to run for.
"$(MSBUILDBINPATH)\msbuild" "$(ProjectPath)" /t:Transform /p:Configuration=$(ConfigurationName);Platform=AnyCPU
xcopy "$(ProjectDir)obj\$(ConfigurationName)\Web.Config" "$(ProjectDir)". /F /R /Y
I had solved this in a simpler way, by adding this at the end of the .csproj file, right before the tag. This is similar to keitn's answer, with the difference that it doesn't use a post build event.
<Target Name="BeforeBuild">
<TransformXml Source="Web.config" Transform="Web.$(Configuration).config" Destination="Web.config" />
</Target>
I didn't want to update the web.config in my project just the one that ends up in the bin folder so here is how I did it.
Add the following to the end of .csproj (just before the final closing project tag)
<Target Name="Transform">
<MakeDir Directories="bin" Condition="!Exists('bin')" />
<TransformXml Source="Web.Config" Transform="Web.$(Configuration).config" Destination="bin\$(TargetFileName).config" StackTrace="true" />
</Target>
Then add the following post build step
"$(MSBUILDBINPATH)\msbuild" "$(ProjectPath)" /t:Transform /p:Configuration=$(ConfigurationName);Platform=AnyCPU
This means that when you build a transform takes place from the debug/release config to WebsiteName.Config file in the output bin directory thus not interfering with the main web.config in the project.
After reading many similar posts and having problems with files not being able to be overwritten or web.config not being accessible because it is read only this is what I got working for me:
<Target Name="BeforeBuild" Condition="$(Configuration) == 'MyAltDebugConfiguration'">
<ItemGroup>
<OriginalWebConfig Include="$(ProjectDir)Web.config"/>
<TempWebConfig Include="$(ProjectDir)TempWeb.config"/>
</ItemGroup>
<Exec Command=""$(DevEnvDir)tf.exe" checkout "$(ProjectDir)Web.config"" />
<Copy SourceFiles="#(OriginalWebConfig)" DestinationFiles="#(TempWebConfig)" />
<TransformXml Source="$(ProjectDir)TempWeb.config"
Transform="Web.$(Configuration).config"
Destination="Web.config" />
</Target>
Notes:
This runs as the BeforeBuild target.
I only want it to run under a certain configuration (an alternative debug environment) and so that is why I have the Condition. When deploying via web deploy the publishing target kicks in and I don't need this target to run.
I don't want to have to remember to check out web.config (only to undo it when I am done) so I check web.config out before beginning the transform. If you aren't using TFS you can remove this line.
Because VS (2010) \ msbuild doesn't want to let go of the Source web.config I use a temp file (thanks to this article for the info: http://www.diaryofaninja.com/blog/2011/09/14/using-custom-webconfig-transformations-in-msbuild)
I tried adding a command to delete the TempWeb.config but VS \ msbuild doesn't want to let go of it. I can live with it as it doesn't get added to TFS.
I know this is old, but I'm facing the same problem. We have Test, Staging, Live configs that replace endpoints, connection strings etc. from the default Web.config
However I would do the following:
Right click on the desired transform config (e.g. Web.Live.config)
Click on "Preview Transform"
Copy everything from right (it's how the Web.config looks with the transformation)
CTRL+A + CTRL+C
Open Web.config file (default one)
Select everything (CTRL+A) and paste it in (CTRL+V)
Run
It's not that many steps and is done pretty quickly when you get a hang of it. Hope this helps. :)
#ologesa:
Your solution needs write access to the original Web.config (you must check-out in TFS).
The better solution is to directly generate the Web.config in the bin folder like keitn does this. When we combine keitn's and your solution we get this one:
<Target Name="BeforeBuild">
<Message Text="Transforming Web.config from Web.$(Configuration).config" Importance="high" />
<MakeDir Directories="bin" Condition="!Exists('bin')" />
<TransformXml Source="Web.Config" Transform="Web.$(Configuration).config" Destination="bin\$(TargetFileName).config" StackTrace="true" />
</Target>

Multiples configurations with Defines in C#

I have a solution with 25 C# projects aprox.
For every compilation of the solution, I tweak some project configuration usings precompilers directives.
When I need to add a directive to a configuration. I have to go to every project and add that directive. When I add a new configuration it is the same an worse.
In a C++ project I would had a .h file that would be included in all projects, to have something like a global configuration file.
But, how can I do something like that using Visual Studio and C#?
A good way to do this would be to reference external config files from each projects configuration.
You can create just one config file with the information/directories that are frequently changing and reference it from each projects config files.
For example:
directives.confg:
<appSettings>
<add key="ThisDirectory" value="This\Directory\Path"/>
<add key="ThatDirectory" value="That\Directory\Path"/>
</appSettings>
And in your web/app config you would have:
<configuration>
<appSettings file="C:\PathToYourExternalConfigFile\directives.config">
<add key="OtherKeyNotInExternalConfig" value="SomeValue" />
</appSettings>
</configuration>
Now only the directives.config file will need to be updated and all other projects will automatically have the latest config changes.
If you build your project using msbuild then all you need to do is set an environment variable with the same name as your define.
#if OPTION_ONE
// option one code here
#else
// option one not set
#endif
Then from the developer command prompt you can build like so
set OPTION_ONE=true
msbuild YourSolution.sln
If that doesn't work you could add the following to your project file
<DefineConstants Condition="'%(OPTION_ONE)' != ''">OPTION_ONE</DefineConstants>
Edit:
Create a common c# file for definitions the just like you would in c++ with a header.
Create a user file (i.e. MyProject.csproj.user) to include the common cs file. The user file doesn't need to be included in the project. Visual studio will automatically use it if it exists:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" >
<Compile Include="$(SolutionDir)CommonFile.cs"/>
</Project>
Copy that file to each project directory, changing the file name to match the project.
Or you can just add the common file as a link by adding existing item and click the option arrow next to Add and select Add As Link

Couldn't find solution file (CCNet & NAnt)

I am trying to configure CCNet to build my project.
I get the error:
Couldn't find solution file 'C:\CRUISECONTROL\BuildEngine\BuildEngine.sln'
I haven't specified that specific path so I assume it is using part of the build file path.
When I specify:
<buildArgs>-buildfile:C:\CRUISECONTROL\BuildEngine\BuildEngine.build -D:sln=C:\CRUISECONTROL\BuildEngine.sln</buildArgs>
Where it actually is, I get an error about file format which is expected.
How on earth do I specify the path to the file, at the moment I can only specify the file name.
Folder Layout:
C:\CRUISECONTROL\ **SOLUTION IS HERE**
C:\CRUISECONTROL\BuildEngine\ ** BUILD FILE IS HERE **
CCNet Config:
<tasks>
<nant>
<executable>C:\Program Files (x86)\NAnt\bin\NAnt.exe</executable>
<baseDirectory>C:\CRUISECONTROL</baseDirectory>
<buildArgs>-buildfile:C:\CRUISECONTROL\BuildEngine\BuildEngine.build -D:sln=BuildEngine.sln</buildArgs>
<nologo>false</nologo>
<targetList>
<target>build</target>
</targetList>
<buildTimeoutSeconds>1200</buildTimeoutSeconds>
</nant>
</tasks>
NAnt build file:
<?xml version="1.0"?>
<project name="BuildEngine" default="build" basedir=".">
<description>Build Engine Build File</description>
<property name="sln" value="sln.file.empty" overwrite="false" />
<target name="clean">
</target>
<target name="build" depends="clean">
<solution configuration="debug" solutionfile="${sln}" />
</target>
</project>
Thanks.
The build file is set in its own element within the nant element. See below.
<nant>
<executable>c:\nantdir\nant.exe</executable>
<buildArgs>-D:blah_prop=foobar</buildArgs>
<nologo>false</nologo>
<buildFile>default.build</buildFile>
<targetList>
<target>the-nant-target</target>
</targetList>
<buildTimeoutSeconds>9000</buildTimeoutSeconds>
</nant>
buildArgs are just properties to send to Nant. They have nothing to do with the nant file you're trying to use.
NAnt Documentation:
<Solution>
Right now, only Microsoft Visual Studio .NET 2002 and 2003 solutions and projects are supported.
Two hours wasted.

Categories