I have a legacy .net application running 4.0 that I have been given to maintain and as part of the maintenance I am updating the CI/CD system.
In doing that I found a bunch of manual file content replaces used to manage environment specific variables.
One of these is the connection string, I was wondering what the recommended way of handling connection strings in app and web.config files is across environments.
I tried slowcheetah but it doesn't do the transform for the web.config when you are not using the publish option.
Thanks for any help.
Yes you can do this with a msbuild script:
<UsingTask TaskName="TransformXml" AssemblyFile="bin\Microsoft.Web.Publishing.Tasks.dll"/>
<Target Name="GenerateConfigs">
<MakeDir Directories="$(BuildOutput)" Condition="!Exists('$(BuildOutput)')"/>
<TransformXml Source="BTSNTSvc.exe.config"
Transform="BTSNTSvc.exe.$(Configuration).config"
Destination="$(BuildOutput)\BTSNTSvc.exe.config"/>
</Target>
Please review the following resources for performing a config transform without having to do a publish. This snippet was taken from the 2nd link.
http://matthewvukomanovic.blogspot.com/2012/10/webconfig-transform-without-publishing.html
http://geekswithblogs.net/EltonStoneman/archive/2010/08/20/using-msbuild-4.0-web.config-transformation-to-generate-any-config-file.aspx
Related
I have a project, lets call it 'ProjectX' which needs to use Excel DNA. Development is successful, however, I am unable to resolve one issue. The project uses config transforms to update values in app.config when building for different environments, for example DEV, UAT, and PROD.
Here is the issue: When I build for a certain environment, like DEV, the app.config transforms as expected. However, ProjectX-AddIn64-packed.xll.config does NOT transform, and this is the config that Excel DNA is using inside the XLL. I'm not sure what to do, any help would be appreciated.
Constraints: I can only deploy one file, the XLL.
Things I Have Tried/Researched:
Renaming app.config to ProjectX-AddIn64-packed.xll.config
Using Visual Studio Build Events
Editing ExcelDna.Build.props to try and override the default build/packaging process
App.config transforms are currently not supported in Excel-DNA, as of this writing. One workaround at the moment is to replace the .xll.config files with the contents of the .dll.config file at the end of the build, after the transformation has been applied.
There are many different ways you can choose to do this file replacement... For example, you can run a script in a post-build event, or alternatively add a new MSBuild target to your .csproj file that runs after the ExcelDnaBuild task which copies the file(s).
E.g.
<Target Name="CopyAppConfig" AfterTargets="ExcelDnaBuild">
<Copy SourceFiles="$(TargetPath).config" DestinationFiles="$(TargetDir)MyAddIn-AddIn.xll.config" />
<Copy SourceFiles="$(TargetPath).config" DestinationFiles="$(TargetDir)MyAddIn-AddIn-packed.xll.config" />
<Copy SourceFiles="$(TargetPath).config" DestinationFiles="$(TargetDir)MyAddIn-AddIn64.xll.config" />
<Copy SourceFiles="$(TargetPath).config" DestinationFiles="$(TargetDir)MyAddIn-AddIn64-packed.xll.config" />
</Target>
You can read more about this on our GitHub repo:
Hard coded references to App.config prevents use of config transforms in build. #282
AutoGenerateBindingRedirects doesn't work properly #241
So I have been thinking about how to make my life easier by automating the connection string name used to connect to my databases. I program on my laptop and on a PC at work. They utilize different databases. So I created the code below and it’s working. However, I don’t recall ever hearing about this method before and am wondering why? Is this a bad way to do this sort of thing. Understandably, I have to remove the code before releasing it but it sure seems like it’d make it easier to switch between computers since I don’t have to comment/uncomment lines in Web.config. Each connection string name is set to the computer name in Web.config.
static string connectionStringName = System.Environment.MachineName;
public ApplicationDbContext()
: base(connectionStringName, throwIfV1Schema: false)
{
}
A different approach to consider would be leveraging web.config transforms and create new environments (Dev-PC / Dev-Laptop) which contains has a different connection strings you need.
Then when you launch Visual Studio, you can simply select the build profile you need and when you launch the app the appropriate connection strings will be used. This will be helpful if you need to use file paths or other configurations that will differ between the two environments.
Your answer is to use a combination of web.config transforms, and your SetParameters.xml file. I know that they can seem daunting at first, but if you invest the time into learning, it pays off.
Using web.config transforms, or naming your connection strings based on machine names is a Bad Idea TM. These solutions will work, but they require you to rebuild your WebDeploy package every time you release to a new environment. You want a solution that lets you deploy any package to any environment.
Firstly you'll need to create two web.config transforms. One for Debug, and one for Release. Web.Debug.config will look something like this:
<connectionStrings>
<add name="MyDB" connectionString="Data Source=.\SQLExpress;Initial Catalog=MyDevDatabase;Integrated Security=True"
xdt:Transform="SetAttributes" xdt:Locator="Match(name)"/>
</connectionStrings>
Whereas your Web.Release.config will contain a placeholder string. These placeholders will be updated when you deploy your package into IIS in your target environment. It should look like this:
<connectionStrings>
<add name="MyDB" connectionString="__targetenvironmentconnstring__"
xdt:Transform="SetAttributes" xdt:Locator="Match(name)"/>
</connectionStrings>
To get your target environment connection string in place when you do your deploy, you need to update your publish profiles under Properties in your web project. You'll create one publish profile (using the Package option) for each target environment; so QA.pubxml, UAT.pubxml, Production.pubxml, etc. Visual Studio will walk you through a wizard to create these files when you use the Publish dialog. Now open each file. You'll want to edit the end of the file to incorporate MSDeployParameterValue elements like I've done here
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup> <!-- A bunch of info --> </PropertyGroup>
<ItemGroup>
<MSDeployParameterValue Include="$(DeployParameterPrefix)MyDB-Web.config Connection String">
<ParameterValue><!-- Your target environment connection string goes here! --></ParameterValue>
</MSDeployParameterValue>
</Project>
Now right-click and Publish on your web project, selecting the publish profile you want. In the output directory you've specified, you'll see a SetParameters.xml file which contains your target connection string. This is what will go into your web.config file when you deploy, but crucially, you can also just import the package manually using IIS, and assign your own values at deploy time like this:
Finally you can just refer to your connection string using ConfigurationManager.ConnectionStrings["MyDB"].ConnectionString and you've got the correct value, no matter what environment you're in!
To understand more, I highly recommend reading the following
https://www.asp.net/web-forms/overview/deployment/web-deployment-in-the-enterprise/configuring-parameters-for-web-package-deployment
https://www.asp.net/web-forms/overview/deployment/web-deployment-in-the-enterprise/deploying-web-packages
It takes a few goes to wrap your head around, but you'll never go back.
I have implemented a dll for consuming an API. It's working properlyn but now I have to allow user to consume the dev and production API's (we have separate environments for dev and production). I don´t know how to do it.
I can implement a switch and allow the user to set production or dev and just change the path, but I'm guessing this is bad practice.
Actually, I have a path stored in app.config. Any suggestions on how to implement this or is the only way to send the user two dll's, one for production and one for dev?
The correct way to do this would be:
to store the path in app.config
create environment specific transforms for the config file
transform the config file during deployments
Sample config:
<add key="apiUrl" value="http://localhost/myapi" />
And corresponding transform file
<add key="apiUrl" value="http://devhost/myapi" xdt:Transform="Replace" xdt:Locator="Match(key)" />
You can use plugins like SlowCheetah to do the transformation.
Alternatively, you can use MSBuild tasks to do the transformation as well:
<UsingTask TaskName="TransformXml"
AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.Tasks.dll"/>
<Target Name="TransformWebConfig">
<TransformXml Source="C:\myapp\app.config"
Transform="C:\myapp\app.dev.config"
Destination="C:\myapp\bin\<web.config or in case of exe, product.exe.config>"
StackTrace="true" />
</Target>
Note: DLL files in .Net cannot have config files. So I'm assuming you're referring to the exe or web.config of your product.
Conditional compilation could be an option for this.
It's not too hard to set up. A good article is:
http://www.codeproject.com/Articles/451734/Visual-Studio-Use-Conditional-Compilation-to-Contr
In order to handle settings for different deployment targets, I moved application settings from app.config to its own file and included that file in app.config via configSource. I also created a settings file for each target.Here is an illustration:
Project A
app.config (references settings.config)
settings.config
settings.Release.config
settings.Debug.config
During post-build, I copy the appropriate settings.{configuration}.config to the output directory. This is working fine so far and I can see settings.config file in the project output directory containing settings for the current build configuration: Release, Debug, etc.
However, I am having a problem with the setup project that I have for this project (Project A). Initially, it was not including settings.config file. So I set the build action for settings.config file as Content and I added content files from Project A to the setup project. This ensured that settings.config file was included in the setup. However, since the setup project appears to be picking settings.config file from the project directory instead of the output directory, settings.config file included in the setup is not what it should be. I want the one from the output directory to be included in the setup program since that one is the correct one for the current build configuration. I tried the following:
Added settings.config as a file to the setup project. However, it seems like I can only specify absolute path. So when I add it from the output directory of a particular build configuration (..bin\debug\settings.config), it does not work in other build configuration since (..bin\debug\settings.config) does exist in the directory specified. I looked into using relative paths or dynamic paths in the setup project where the build configuration could be specifed as part of the path but I could not find anything.
I considered using pre-build event to actually modify settings.config file in the project directory and then have it copied over the output directory by setting its 'Copy to Output Directory' to copy always or copy if newer. This should ensure that the appropriate settings.config is copied to the output directory just like the post-build based solution and should also ensure that the contents of settings.config file is updated before the setup project includes it. However, I don't like this solution because I would have to make sure settings.config file is writeable before I can make any changes since it is source controlled. If it is readonly, then I need to flip it to writeable, make changes, and then set it to readonly again. It is adding extra complexity.
I was wondering if anyone has a better idea or knows a setup project trick that allows me to include settings.config file appropriate for the current build configuration in the setup program.
Thanks
If I had to approach this problem, I'd start by asking the following question:
Why does settings.config have to be under source code control if settings.Debug.config or settings.Release.config provide the same information?
The answer, if I read your question correctly, is because you needed to force a settings.config file to appear as part of the build output. I'm guessing this is because your setup project is using the built in "Primary output" choice.
What you can do instead is add that file to your setup project as an explicit file reference. Right-click on the setup project and choose add / file, then select the file you want to include. As you'll notice (unless it's been fixed in VS2008 which sadly I'm not yet allowed to use at work), there is a very annoying limitation placed on manually added files - there is no way to make the path build configuration aware. You can work around that by copying the appropriate settings.config file to a common location (e.g. bin/Configuration) and picking it up from there. This does limit you to building Debug and Release versions sequentially, rather than in parallel, but for many this probably isn't a huge problem.
If you aren't required to use VS setup projects, I strongly encourage you to take a look at WiX (Windows Installer XML - see http://wix.sourceforge.net/ for more information). That will easily allow you to accomplish what is necessary, although if you are unfamiliar with the internal workings of Microsoft Installer the initial learning curve could be a little steep. Microsoft use WiX themselves for some pretty significant setup tasks (e.g. Office 2007, SQL Server, etc.). It had been hoped that WiX would become part of Visual Studio (for VS 2010), but sadly that is no longer the case.
I decided to go about achieving the same result (being able to have different configuration settings for different target environments) in a different way. So here is how I implemented it and it is working great. I read some of the posts here at SO about XmlMassUpdate task from MSBuild Community Tasks and decided to utilize it. Here is what I did:
1) For each project that needs to have different settings depending on the target environment, I added an xml file called app.config.substitutions.xml or web.config.substitutions.xml to the project. So, the project looked like
Project A
app.config
app.config.substitutions.xml
app.config.substitutions.xml file has the settings substitutions that XmlMassUpdate will process and apply to app.config file. Below is a sample substitution file that I use:
<configuration xmlns:xmu="urn:msbuildcommunitytasks-xmlmassupdate">
<substitutions>
<Development>
<appSettings>
<add xmu:key="key" key="SomeSetting" value="DevValue" />
</appSettings>
</Development>
<Test>
<appSettings>
<add xmu:key="key" key="SomeSetting" value="TestValue" />
</appSettings>
</Test>
<Release>
<appSettings>
<add xmu:key="key" key="SomeSetting" value="ReleaseValue" />
</appSettings>
</Release>
</substitutions>
</configuration>
For details on how to specify substitutions, take a look at the documentation for XmlMassUpdate or just do a search on it.
2) Now I need to run XmlMassUpdate as part of build automation (TeamBuild/MSBuild). So in BeforeCompile in TeamBuild build definition file (basically a proj file), I added the following to run XmlMassUpdate on config files that have a corresponding .substitution.xml file
<PropertyGroup>
<SubstitutionFileExtension>.substitutions.xml</SubstitutionFileExtension>
<TargetEnvironment>Test</TargetEnvironment>
</PropertyGroup>
<Target Name="BeforeCompile" Condition="'$(IsDesktopBuild)'!='true'">
<CreateItem Include="$(SolutionRoot)\**\app.config;$(SolutionRoot)\**\web.config">
<Output ItemName="ConfigurationFiles" TaskParameter="Include"/>
</CreateItem>
<CreateItem Include="#(ConfigurationFiles)" Condition="Exists('%(FullPath)$(SubstitutionFileExtension)')">
<Output ItemName="ConfigFilesWithSubstitutions" TaskParameter="Include"/>
</CreateItem>
<Message Text="Updating configuration files with deployment target specific settings..."/>
<XmlMassUpdate
ContentFile="%(ConfigFilesWithSubstitutions.FullPath)"
SubstitutionsFile="%(ConfigFilesWithSubstitutions.FullPath)$(SubstitutionFileExtension)"
ContentRoot="/configuration"
SubstitutionsRoot="/configuration/substitutions/$(TargetEnvironment)"/>
</Target>
Note that config files are read-only during the build, I make sure to set them writeable before running this task. I actually have another custom MSBuild task that runs before XmlMassUpdate that handles common settings throughout all of the config files such as connection strings. That task makes the config files writeable. I also don't check modified config files back to the source control. They're (appropriate config file for the deployment target) included in the installer.
I run an ASP.NET website solution with a few other projects in it. I've known that MSBuild projects are capable of this, but is it the best way? Are they easy to create? Is nAnt, CruiseControl.NET or any other solution better?
When I build the site (using Web Deployment Projects), can I automate part of the build so that it does not copy certain folders from the project into the Release folder? For instance, I have folders with local search indexes, images and other content part of the folder, but I never need or upload those when deploying the project.
I'm also looking toward this type of solution to automatically increment build and version numbers.
Here's an example of a Web Deployment Project scripting this sort of task in the .wdproj file:
<Target Name="AfterBuild">
<!-- ============================ Script Compression============================ -->
<MakeDir Directories="$(OutputPath)\compressed" />
<Exec Command="java -jar c:\yuicompressor-2.2.5\build\yuicompressor-2.2.5.jar --charset UTF-8 styles.css -o compressed/styles.css" WorkingDirectory="$(OutputPath)" />
<Exec Command="move /Y .\compressed\* .\" WorkingDirectory="$(OutputPath)" />
<RemoveDir Directories="$(OutputPath)\sql" />
<Exec Command="c:\7zip-4.4.2\7za.exe a $(ZipName).zip $(OutputPath)\*" />
</Target>
This would allow you to delete a folder.
(I suspect that if you wanted to not have the folder copy over at all, the solution file would be the place to specify that, though I haven't had to use that.)
MaseBase, you can use Web Deployment Projects to build and package Web Sites. We do that all the time for projects with a web application aspect. After you assign a WDP to a Web Site, you can open up the .wdproj file as plain-text XML file. At the end is a commented section of MSBuild targets that represent the sequence of events that fire during a build process.
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.WebDeployment.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="BeforeMerge">
</Target>
<Target Name="AfterMerge">
</Target>
<Target Name="AfterBuild">
</Target>
-->
You can uncomment the targets you want (e.g. "AfterBuild") and insert the necessary tasks there to carry out your repeated post-build activities.
You can set the Build Action/Copy to Output Directory property on individual files (select the file and hit F4 to open the properties window) to control what happens to them during build, but not for folders. This could probably be automated with a (pre) build task if you don't want to do it manually.
Alternatively, you can exclude these folders from the project (right click and 'exclude from project'); they'll still be there ("show all files" in solution explorer), but they won't be included when building the project.
CruiseControl.NET solves a different problem (continuous integration) ... however, I've had great success with NAnt for specifically what you're asking. There's a learning curve, but once you get proficient you'll wonder how you ever got along w/o it.
In addition to #Fredrik's tip about setting project items to "Copy to Output Directory", you can also specify a post-build action in the project's properties in the Build tab and include CMD commands like copy.exe and move.exe.
We use FinalBuilder to automate a bunch of post build / pre build tasks. There's also a web interface so you can kick off builds (or push websites) by logging in to the web site and clicking a button.
http://www.finalbuilder.com/
Can't you edit the Web Deployment project's MSBuild file for it to do what you want?