visual studio

Multiple configuration files per environment


Hello,

Today I had the chance to perform a quick research about a classic problem. If have different settings for development, testing, staging and production. So, we usually merge or lost settings of the configuration files. Visual Studio 2008 have already resolved the problem (at least in web applications) by allowing the user to create different web.config files and applying XSLT to generate the final configuration file. Today I’ll propose the same solution, but for desktop Apps, and hopefully you could apply this solution to any XML file.

Just in case you’re one of those who want to download a sample, you coudl get it at https://github.com/hmadrigal/playground-dotnet/tree/master/MsDotNet.MultipleConfigurationFile

The Problem:

We have a configuration file ( .config or any xml document), and we want to have different settings per environment (development, testing, staging and production). Moreover we want to automate this process, so we don’t have to write the settings manually.

Remarks

  • It’s good to know basics of MSBuild and how to modify project.
  • It’s good to know about XML, XPath and XSLT

The solution

Create one configuration per environment 

Let’s start by creating configuration settings for each environment where you need custom settings. You could do it by click on “Configuration Manager…“. For example I’ve created the environments that we’re gonna use on this example.

Image

Now, add a configuration file (in our case app.config) and also add one config file per environment. Please note that the configuration file app.config contains most of the final content, and the other configuration files will hold only XSLT expressions to modify the original. As a convention usually the file names are: app.$(environment).config. The following image illustrates the configuration files:

Image

More in detail app.config, contains three simple settings:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <appSettings>
    <add key="EnvironmentName" value="Debug" />
    <add key="ApplicationDatabase" value="Data Source=DevelopmentServer\SQLEXPRESS;Initial Catalog=DevelopmentDatabase;Integrated Security=True;" />
    <add key="ServerSharedPath" value="\\127.0.0.1" />
  </appSettings>
</configuration>

Set up your project to use a XslTransform task once the compilation is done

Now you will have to edit the project file using a text editor. VS project files are XML, these normally are read by VS and see the project structure. However there are tasks that only can be customized by editing manually the XML file. You can modify the xml of a project within Visual Studio by using right (secondary) click on the project from the Solution Explorer. Then, click on “Unload project”, and once again right click and now select “Edit project …”. Now, you should be able to see the XML associated to the selected project.

You’ll be adding two targets (or if modifying if any of them is already in place). The following code:

<Target Name="ApplySpecificConfiguration" Condition="Exists('App.$(Configuration).config')">
    <XslTransformation XmlInputPaths="App.config" XslInputPath="App.$(Configuration).config" OutputPaths="$(OutputPath)$(RootNamespace).$(OutputType).config" />
    <Copy SourceFiles="$(OutputPath)$(RootNamespace).$(OutputType).config" DestinationFiles="$(OutputPath)$(RootNamespace).vshost.$(OutputType).config" />
  </Target>
  <Target Name="AfterBuild">
    <CallTarget Targets="ApplySpecificConfiguration" />
  </Target>

The previous XML portion Adds a custom task called and it uses MSBuild variables to determine the name of the configuration file, our example is using a Console App, thus we need to create a copy of the config file but with vshost.config extension, so we added a copy task.  The second thing we’ve added is a call to the task we’ve just created, this call is added into the AfterBuild Target. The AfterBuild is invoked by MSBuild automatically when the build process has finished.

Transform your app.config with XSLT

On this section, we’re gonna write the content for each configuration. This will let us custom XML porting into the final configuration file. As example, here is the XML for the production transformation:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
  <xsl:output method="xml" indent="yes"/>

  <!-- Nodes to be replaces -->
  <xsl:template match="/configuration/appSettings/add[@key='EnvironmentName']">
    <add key="EnvironmentName" value="Production" />
  </xsl:template>
  <xsl:template match="/configuration/appSettings/add[@key='ApplicationDatabase']">
    <add key="ApplicationDatabase" value="Data Source=PRODUCTION\SQLEXPRESS;Initial Catalog=ProductionDatabase;Integrated Security=True;" />
  </xsl:template>
  <xsl:template match="/configuration/appSettings/add[@key='ServerSharedPath']">
    <add key="ServerSharedPath" value="\\PRODUCTION" />
  </xsl:template>

  <!-- Copy all the remaining nodes -->
  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

I know XSLT might sound intimidating at first, but it’s kinda easy.BTW XSLT uses XPath, so if you want to know the basics of them, please check out w3c school for a quick reference. XPath at http://www.w3schools.com/xpath/xpath_examples.asp and XSLT at http://www.w3schools.com/xsl/xsl_transformation.asp

To put it in a nutshell, our XSLT is detecting specifically three attributes (by its name and location into the XML) and replacing them with a custom value. All the remaining nodes are kept into the final XML.

Let run our sample

Just to make it work I’ve coded two classes, one that defines the constant name of the settings and a main class which print values from the configuration file.

using System;
using System.Configuration;

namespace MultipleConfigurationFilesSample
{
    public static class ApplicationConstants
    {
        public class AppSettings
        {
            public const string ServerSharedPath = @"ServerSharedPath";
            public const string EnvironmentName = @"EnvironmentName";
            public const string ApplicationDatabase = @"ApplicationDatabase";
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            var environment = ConfigurationManager.AppSettings[ApplicationConstants.AppSettings.EnvironmentName];
            var serverSharedPath = ConfigurationManager.AppSettings[ApplicationConstants.AppSettings.ServerSharedPath];
            Console.WriteLine(@"Environment: {0} ServerSharedPath:{1}", environment, serverSharedPath);
            Console.ReadKey();
        }
    }
}

Now I have run the application twice, but using different configurations. The first was using development and I got the following output:

Image

The second was using Production configuration, and it produces the following output:

Image

As you can see, the selected environment is determining the settings into the resultant configuration file.

As usual the source code can be fond at https://github.com/hmadrigal/playground-dotnet/tree/master/MsDotNet.MultipleConfigurationFile

References:
References: http://litemedia.info/transforming-an-app-config-file

Best regards,
Herber

Advertisements

Visual Studio re install individual packages


Hi,

I remember that sometime ago my Visual Studio installation didn’t install the package correctly. And if you’ve installed visual studio you might know that the standard installation might take 1 hour at least. If you decide to repair the visual studio then the process can take 2 hours. However by reinstalling the visual studio you still might have errors.

I resolved my problems by reinstalling only the broken package. So, I’ll explain how you can do this. I’ll take the images from my friend Anthony (arbot) which has the same issue, and he could solve it by installing only what he needed (see details at this post). So my example is based on Arbot’s problem with the “Microsoft SQL Server 2008 Management Objects

Step 1: Prepare your bat-belt
Easy dude, you will need the Visual Studio Installation files. You are more likely to need admin rights to perform installation tasks.

Step 2: Identify your broken component
In the Arbot, the broken package was “Microsoft SQL Server 2008 Management Objects”. Unfortunately these kind of errors does not have a friendly message. Usually they might look like:

So don’t be afraid if you see:
Could not load file or assembly
‘Microsoft.SqlServer.Management.SqlParser, Version=10.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91’ or one of its dependencies. The system cannot find the file specified

The thing here is that you should be able to recognize which part of your Visual Studio is failing. If you aren’t sure about the component, then check the list of packages /components that Visual Studio installs at
http://msdn.microsoft.com/en-us/library/ee225240.aspx

Step 3: Remove what it’s not working
Once you know what component is missed, go to the control panel and uninstall the specific component. In the case or Arbot he uninstalled all the products from the “Microsoft SQL Server 2008 Management Objects”, which are under the folder WCU\SMO see http://msdn.microsoft.com/en-us/library/ee225240.aspx to know which components are installed under the same folder.

Step 4: Make sure you’re ready to reinstall
Ok, It seems obvious, but before installing the Visual studio components you will have to make sure that your current installation has all the components you might need. So, take your time and check http://msdn.microsoft.com/en-us/library/ee225240.aspx and the prerequisites 😉

Step 5: Install in order each package
open a command prompt in admin mode. (I’d recommend Visual Studio Command Prompt), and make sure MSIExec is in your path (http://technet.microsoft.com/library/cc759262(WS.10).aspx). So, we can run the installer from the command prompt.
Now In the command prompt, go to the installer folders of your broken package. In the case of Arbot, look for “WCU\SMO” folder. Review which platform you’re using because X86 platforms uses different commands than the X64 platforms. Additionally there are two kind of X64 platforms.
One you have identifies the packages that you have to install run the commands, in the case of Arbot he ran these commands:


C:\Windows\system32>MSIExec SQLSysClrTypes_x86_enu.msi /log "%TEMP%\dd_SQLSysClr
Types_x86_msi.txt"
C:\Windows\system32>MSIExec SharedManagementObjects_x86_enu.msi /log:"%TEMP%\dd_SharedManagementObjects_x86_MSI.txt"

Depending of the settings you’ve specified for MSIExec you might have to attend the installation and restart your computer.

Step 6: Try you reinstalled component on visual studio
If the installation worked properly it shouldn’t report any issue, and you always have the option to check the log file generated by the MSIExec.

Now you should be able to run Visual Studio and try once again to use the feature on Visual studio, and hopefully it should be working properly. My friend Anthony made a post about his problem 😉 if you need to see more pics 😀

http://abakerp.blogspot.com/2011/02/cannot-load-file-or-assembly.html

Best regards,
Herber