Installing and Configuring Applications

Lesson 1: Configuration Settings

It is best to avoid hard coding variables. .NET provides ample tool set to avoid hard coding.

Prior to .NET 2.0 developers had 2 ways to handle configuration. First approach was to put everything in the appSettings of the configuration file. Simple, but had to deal with settings in non-object oriented way. Other way was to define custom configuration settings sections in configuration file and build corresponding classes to consume these sections. Complex, but object oriented.

.NET 2.0 provides tools to adopt the latter approach, but without having to write reams of code.

Configuration in .NET Framework 2.0

System.Configuration contains all classes needed to manage configuration.

Top of logical hierarchy are the Configuration and ConfigurationManager classes. Neither class has a constructor specified. Both classes have identical properties (AppSettings and ConnectionSettings). Each of the properties of the ConfigurationManager returns a Configuration object. To retrieve configuration settings follow these steps:

  1. Declare Configuration object
  2. Use the various methods in the ConfigurationManager with the Open prefix to open application or machine configuration file.

e.g.

configuration cs = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);

configuration cs = ConfigurationManager.OpenMachineConfiguration();

ExeConfigurationFileMap myMap = new ExeConfigurationFileMap();

myMap.ExeConfigFilename = @"Demo.exe.config";

Configuration cs = ConfigurationManager.OpenMappedExeConfiguration(myMap, ConfigurationUserLevel.None);

The ConfigurationUserLevel enumeration contains the following values:

  • None - Gets Configuration object that applies to all users
  • PerUserRoaming - Gets Configuration object that applies to the current user
  • PerUserRoamingAndLocal - Gets Configuration object that applies to the current user

The runtime will not stop the ExeConfigFilename being set to a non-existent file. Values returned in this situation will be null. To avoid verify the file exists before use.

Common Settings

Refers to areas that determine how application runs, e.g. Configuring to run under specific version of .NET framework. To specify that 1.1 should be used add following to file:

<configuration>
    <startup>
        <supportedRuntime version="v1.1.4322"/>
    </startup>
</configuration>

Rules for runtime versioning:

If version application is built with is present, that version will be used

If version application is built with is not present and nothing specified by supportedRuntime then the latest version of framework on machine is used.

To verify that shared assembly works with multiple assemblies it can be a pain installing / uninstalling the assembly from the GAC multiple times. Can alleviate by setting the environment variable DEVPTAH to point to the location of the assembly and setting the developmentMode to true:

<configuration>
    <runtime>
        <developmentMode developerInstallation="true"/>
    </runtime>
</configuration>

Can specify where to find specific version of assembly. Can use .NET Framework 2.0 configuration tool or by manually editing the machine configuration file. The codeBase element allows specification of version and location information, e.g.

<configuration>
    <runtime>
    <assemblyBinding ...>
        <dependentAssembly>
            <assemblyIdentity name="myprogram" ... />
            <codeBase version="x.0.0.0" href="http://www.example.com/myprogram.dll"/>
        </dependentAssembly>
    </assemblyBinding>
    </runtime>
</configuration>

Two primary sections, connectionStrings and appSettings, support common configuration requirements, e.g.

<configuration>
    <appSettings>
        <add key"Foo" value "bar"/>
    </appSettings>

    <connectionStrings>
        <add name="WorksString" providerName="System.Data.SqlClient" connectionString="Data Source=localhost" />
    </connectionStrings>
</configuration>

Previously accessed appSetting entries via ConfigurationSettings, but this has been obsoleted, e.g.

string barVar = ConfigurationSettings.AppSettings["Foo"];

Correct way to access is via ConfigurationManager, e.g.

string barVar = ConfigurationManager.AppSettings["Foo"];

Can enumerate AppSettings, as follows:

NameValueCollection coll = ConfigurationManager.AppSettings;

IEnumerator enum = coll.Keys.GetEnumerator();

Connection settings are slightly more involved, but still intuitive. Previously .NET made no real distinction between connection strings and others. Now strong typing is supported allowing provider type to be supported. Access almost identically to AppSettings, primary difference instead of using NameValueCollection is use of ConnectionStringSettingsCollection as exposed by ConnectionStrings property, e.g.

ConnectionStringSettings conn = ConfigurationManager.ConnectionStrings["WorksString"];

Web applications should use the WebConfigurationManager instead of the ConfigurationManager.

Application Settings

Apply to application as a whole rather than a specific user, typical candidates are database connection strings, web service urls, remoting settings, etc.

Can construct by hand, but discouraged because configuration files demand extreme precision. Other options is to use Settings class and the designer. To use designer add new project item of type Settings File. The IDE presents designer allowing creation and management of settings. Generates appropriate class to provide access to the settings, e.g.

SampleSettings mySettings = new SampleSettings();

Debug.WriteLine(mySettings.WebServiceUrl);

Last mechanism is ApplicationSettingsBase - as used by Visual Studio. To use create class that derives from ApplicationSettingsBase and decorate each property with either UserScopedSettings or ApplicationScopedSettings attribute, e.g.

class AppSettingsHelper : ApplicationSettingsBase
{
    [UserScopedSetting()]
    public String Key
    {
        get { return (this["Key"] as String); }
        set { this["Key"] = value; }
    }
}

Remoting Settings

Provides ability to add assemblies or change their location without requiring recompilation of application. For example...

3-tier application with all layers running on same machine. By using remoting can spread out load between machines. If components have been registered in code then recompilation will be required to point to the new assembly locations. By using configuration files the assemblies can be deployed to new machines and used when the configuration file is updated - with no need to restart the application.

To register a component as a server need to specify fully qualified object name and assembly, the type (Singleton or Single Call) and Uniform Resource Identifier. If being used on IIS then the URI must have a .rem extension:

<system.runtime.remoting>
   <application name="MyApplication">
       <service>
           <wellknown type="FullyQualifiedName,AssemblyName" mode="Singleton" objectUri="MyClass.rem"/>
       </service>
   </application>
</system.runtime.remoting>

For a client to consume the assembly similar entries are made, except the mode is not required and the URI points to the location where the object is being made available, e.g.

<system.runtime.remoting>
   <application name="MyClientApplication">
       <service>
           <wellknown type="FullyQualifiedName,AssemblyName" objectUri="http://localhost:5000/MyClass.rem"/>
       </service>
   </application>
</system.runtime.remoting>

Lesson 2: Creating an Installer

In some circumstances it is necessary to add custom functionality to the features provided by the Microsoft Windows Installer. These custom installers should derive from the Installer class.

Main reason for using installers:

  • professional look and feel
  • simplify what user has to do in order to use application
  • specify settings application needs to execute
  • provide mechanism to remove application without unwanted remnants

To create and use an Installer derived class:

  1. Derive class from Installer
  2. Override Install, Commit, Rollback and Uninstall methods
  3. Add RunInstallerAttribute to derived class and set runInstaller parameter to true.
  4. Put derived class in assembly with your application to install
  5. Invoke the installer, e.g. Use the InstallerTool or AssemblyInstaller or ComponentInstaller class programmatically. The Installer class has an Installers property which returns an instance of the InstallerCollection

To start installation call the Install method. If no errors are found then Commit is called at the end of the installation.

To launch an installer progammatically use either the AssemblyInstaller or ComponentInstaller.

IDictionary actions = new Hashtable();

AssemblyInstaller customAssemblyInstaller = new AssemblyInstaller("CustomInstaller.dll", args);

customAssemblyInstaller.UseNewContext = true;

customAssemblyInstaller.Install(actions);

customAssemblyInstaller.Commit(actions);

To uninstall:

IDictionary actions = new Hashtable();

AssemblyInstaller customAssemblyInstaller = new AssemblyInstaller("CustomInstaller.dll", args);

customAssemblyInstaller.UseNewContext = true;

customAssemblyInstaller.Uninstall(actions);

customAssemblyInstaller.Commit(actions);

Lesson 3: Configuring .Net Framework 2.0

Access tool via Administrative Tools or the SDK command prompt. Available options

  • Manage assembly cache

    • View list of assemblies in GAC
    • Add assembly to GAC
  • Manage configured assemblies

    • View list of configured assemblies
    • Configure assembly
      • Dialogue provides two tabs. One controls binding policy, typically used to configure binding redirections that are used when switching from one release to another. Second tab allows the specification of code bases and reversion to earlier code bases.
  • Configure code access security policy

    • Provides access to multiple nodes, specifically Enterprise, Machine and User. Each node has associated Code Groups, Permission Sets and Policy Assemblies. For these it is possible to manipulate membership conditions and permissions sets, e.g. the zone can be changed from Local Internet to My Computer.
  • Adjust remoting services
  • Manage individual applications

Resetting a configuration

Multiple things that can be reset in a configuration, most common being security settings. Easiest way to do this is select the "Runtime Security Policy" node and choose "Reset All Policy Levels".

Lesson 4: Configuration Management

Getting and Storing Settings

ConfigurationManager = primary vehicle through which configuration settings retrieved. Using the appSettings section of the ConfigurationManager provides access to every value in the appSettings section of the configuration file.

Can use the ConnectionStrings property (as discussed earlier).

Another possibility is through the GetSection method. In the configSections portion of the configuration file specify the name of the section, e.g.

<configSections>
    <sectionGroup name="MyFirstSectionGroup">
        <section name="MyFirstSection" ... />
    </sectionGroup>
</configSections>

Then specify the group and section, e.g.

<MyFirstSectionGroup>
    <MyFirstSection>
        <Value>
            <Identifier>11</Identifier>
            <SettingValue>System.Data.SqlClient</SettingValue>
        </Value>
    </MyFirstSection>
</MyFirstSectionGroup>

Can have as many section groups and sections as want. To retrieve...

ValuesHandler vals = ConfigurationManager.GetSection("MyFirstSectionGroup/MyFirstSection") as ValuesHandler;

To save call the Save or SaveAs method of the Configuration object.

Implementing Configuration Interfaces

System.Configuration class has multiple interfaces that can be implemented to achieve precise functionality. Some, like IConfigurationSectionHandler are for general purpose use, others like ISettingsProviderService are more specific (e.g. providing design time support components).

The IConfigurationSectionHandler provides a high degree of granularity when accessing configuration information. It has been depreciated in .Net 2.0 and use should now be made of the ConfigurationSection class.

To create a ConfigurationSection named Chapter9Section add the following declaration:

<configSections>
    <section name="Chapter9Section" type="Chapter9.Configuration.ConfigHandler, App_Code" />
</configSections>

The first portion of the type section takes a fully qualified object name (namespace.objectname) a comma and then the Assembly name (in this example the assembly name is App_Code).

In this example the ConfigurationSection will be implemented with two elements, note only one ConfigurationSection will be used although multiple ones can be. When a ConfigurationSection is used it is asses to the ConfigurationSectionCollection and can be retrieved by name or index. A ConfigurationSection can be as complex as required

<Chapter9Section LastName="Ryan" FirstName="William" />

Can use ConfigurationSection as a base for class to access these values:

public class ConfigHandler : ConfigurationSection
{
    [ConfigurationProperty("LastName", IsRequired=false, DefaultValue = "NotGiven")]
    public string LastName
    {
        get { return (string) base["LastName"];}
        set { base["LastName"] = value;}
    }

    [ConfigurationProperty("FirstName", IsRequired=false, DefaultValue = "NotGiven")]
    public string FirstName
    {
        get { return (string) base["FirstName"];}
        set { base["FirstName"] = value;} 
    }
}

To use

Configuration Chapter9Config = WebConfiguration.OpenWebConfiguration(Request.ApplicationPath);

Chapter9.Configuration.ConfigHandler Chapter9Section = (Chapter9.Configuration.ConfigHandler) Chapter9Config.GetSection("Chapter9Section");

Response.Write(Chapter9Section.FirstName);

// Clear config sections from config object
Chapter9Config.Sections.Clear(); 

// Add new section
Chapter9Config.Sections.Add("Chapter9Section", Chapter9Section); 

Chapter9Config.Save();

The IApplicationSettingsProvider is useful, supporting 3 methods:

  • GetPreviousVersion - value of specified settings property for previous version of same application
  • Reset - returns application settings to their default values
  • Upgrade - indicates to provider that the application has been upgraded, offering the provider the opportunity to upgrade its stored settings.

Provides support for:

  • side-by-side execution of different versions of same application
  • retaining application settings when upgrading
  • resetting application settings to default value for currently used version

This functionality is implemented by ApplicationSettingsBase and so is easily available for use (if required). In addition this class provides:

  • each property can be scoped at either application or user level
  • values scoped at user level stored differently to those at application level
  • application scoped values are stored in file .config that is stored in a Windows Special Folder accessed via the Application class through Application.LocalUserAppDataPath
  • access provided through LocalFileSettingsProvider

IConfigurationSectionHandler objects provide the ability to strongly type data. The value can be checked by the object before passing on to the client. This is not just restricted to type checking but can extend to value validation (e.g. the value is a valid US telephone number).

Download