Monday, August 30, 2010

Enterprise Library 5.0 – Configuration Services Part - III

The previous two posts on Configuration Services showed how we can use an external configuration file and how we can use a specific section from an external config source.

In the final post of the Configuration Sources series, we will look at what I personally think is one of the most useful features of Configuration Services. We will see how we can have the application configuration file inherit from another configuration file and override or add only sections that are application specific. I will give you a scenario where this can be very useful.

In one of my recent projects we were developing a number of websites. Except for some application specific key value pairs a lot of the other environmental configurations were the same across most of these websites. With this feature, it will be very easy to isolate the environmental specific values in a separate configuration file, inherit the same and add key value pairs to store application specific values.

To show how this works, I have a shared config file that looks like the below.

   1: <configuration>
   2:     <configSections>
   3:         <section name="loggingConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.LoggingSettings, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="true" />
   4:     </configSections>
   5:   <connectionStrings configSource="">
   6:     
   7:   </connectionStrings>
   8:     <loggingConfiguration name="" tracingEnabled="true" defaultCategory="General">
   9:         <listeners>
  10:             <add name="EntLIbTestListener" type="Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.FormattedEventLogTraceListener, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
  11:                 listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.FormattedEventLogTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
  12:                 source="Enterprise Library Logging" formatter="Text Formatter"
  13:                 log="" machineName="." traceOutputOptions="None" />
  14:         </listeners>
  15:         <formatters>
  16:             <add type="Microsoft.Practices.EnterpriseLibrary.Logging.Formatters.TextFormatter, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
  17:                 template="Timestamp: {timestamp}{newline}&#xA;Message: {message}{newline}&#xA;Category: {category}{newline}&#xA;Priority: {priority}{newline}&#xA;EventId: {eventid}{newline}&#xA;Severity: {severity}{newline}&#xA;Title:{title}{newline}&#xA;Machine: {localMachine}{newline}&#xA;App Domain: {localAppDomain}{newline}&#xA;ProcessId: {localProcessId}{newline}&#xA;Process Name: {localProcessName}{newline}&#xA;Thread Name: {threadName}{newline}&#xA;Win32 ThreadId:{win32ThreadId}{newline}&#xA;Extended Properties: {dictionary({key} - {value}{newline})}"
  18:                 name="Text Formatter" />
  19:         </formatters>
  20:         <categorySources>
  21:             <add switchValue="All" name="General">
  22:                 <listeners>
  23:                     <add name="EntLIbTestListener" />
  24:                 </listeners>
  25:             </add>
  26:         </categorySources>
  27:         <specialSources>
  28:             <allEvents switchValue="All" name="All Events" />
  29:             <notProcessed switchValue="All" name="Unprocessed Category" />
  30:             <errors switchValue="All" name="Logging Errors &amp; Warnings">
  31:                 <listeners>
  32:                     <add name="EntLIbTestListener" />
  33:                 </listeners>
  34:             </errors>
  35:         </specialSources>
  36:     </loggingConfiguration>
  37: </configuration>

This is the same configuration as I have used in Part I and II with the logging settings. To use this as my parent configuration, I have to set a couple of properties. I have to set the Parent Source of my config file to the common config file I have created above. The settings are shown in the screenshot below. I have also changed the configuration source to be System Configuration which means my local app.config is being used.

Parentconfig

Notice how this configuration file does not have any logging settings. After these changes, my config file now looks like



   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <configuration>
   3:     <configSections>
   4:         <section name="enterpriseLibrary.ConfigurationSource" type="Microsoft.Practices.EnterpriseLibrary.Common.Configuration.ConfigurationSourceSection, Microsoft.Practices.EnterpriseLibrary.Common, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="true" />
   5:     </configSections>
   6:     <enterpriseLibrary.ConfigurationSource selectedSource="System Configuration Source"
   7:         parentSource="Shared Config">
   8:         <sources>
   9:             <add name="System Configuration Source" type="Microsoft.Practices.EnterpriseLibrary.Common.Configuration.SystemConfigurationSource, Microsoft.Practices.EnterpriseLibrary.Common, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
  10:             <add name="Shared Config" type="Microsoft.Practices.EnterpriseLibrary.Common.Configuration.FileConfigurationSource, Microsoft.Practices.EnterpriseLibrary.Common, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
  11:                 filePath="C:\Users\r.vaideeswaran\Documents\Visual Studio 2010\Projects\EntLibTest\EntLibTest\CommonConfig.config" />
  12:         </sources>
  13:     </enterpriseLibrary.ConfigurationSource>
  14: </configuration>


Notice how there are now two sources added; one is the system configuration source and the other points to the shared configuration file.

As a first step, let me now test the logging code I wrote in the previous two parts. For re-cap, here is the code



   1: namespace EntLibTest1
   2: {
   3:     class Program
   4:     {
   5:         static void Main(string[] args)
   6:         {
   7:             LogWriter textWriter = EnterpriseLibraryContainer.Current.GetInstance<LogWriter>();
   8:             LogEntry testEntry = new LogEntry();
   9:             testEntry.Message = "Logging the loading of EntLibTest1";
  10:             testEntry.Priority = 2;
  11:             testEntry.Categories.Add("General");
  12:             textWriter.Write(testEntry);
  13:  
  14:             Console.WriteLine("Message Logged");
  15:             Console.ReadLine();
  16:         }
  17:     }
  18: }

This does indeed produce a “Message Logged” output on the console though there are no logging sections directly in my config. This tells me that my parent source is working. The next step is now to add a specific section and see if that works.

I add a new key value pair in the appSettings section through enterprise library. The screenshot for the same is as below.

image


This is our good old appSettings key value pair. Unsurprisingly, the config is now changed to reflect the above settings.


   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <configuration>
   3:     <configSections>
   4:         <section name="enterpriseLibrary.ConfigurationSource" type="Microsoft.Practices.EnterpriseLibrary.Common.Configuration.ConfigurationSourceSection, Microsoft.Practices.EnterpriseLibrary.Common, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="true" />
   5:     </configSections>
   6:     <appSettings>
   7:         <add key="ConnectionString" value="TestConnectionString" />
   8:     </appSettings>
   9:     <enterpriseLibrary.ConfigurationSource selectedSource="System Configuration Source"
  10:         parentSource="Shared Config">
  11:         <sources>
  12:             <add name="System Configuration Source" type="Microsoft.Practices.EnterpriseLibrary.Common.Configuration.SystemConfigurationSource, Microsoft.Practices.EnterpriseLibrary.Common, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
  13:             <add name="Shared Config" type="Microsoft.Practices.EnterpriseLibrary.Common.Configuration.FileConfigurationSource, Microsoft.Practices.EnterpriseLibrary.Common, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
  14:                 filePath="C:\Users\r.vaideeswaran\Documents\Visual Studio 2010\Projects\EntLibTest\EntLibTest\CommonConfig.config" />
  15:         </sources>
  16:     </enterpriseLibrary.ConfigurationSource>
  17: </configuration>


To make sure my app can retrieve the appSettings value, I add the following three lines of codev(Do not forget the usings at the top)


  1: AppSettingsReader connReader = new AppSettingsReader();
  2: var connString = connReader.GetValue("ConnectionString", typeof(string));
  3: Console.WriteLine(connString.ToString());



Nothing fancy. Now when I run the application, you will see that the application picks up both the logging configuration and the appSettings key. This happens because at run time the Enterprise library merges the two configuration files so that they act as one. The above example did not specifically override a section but if I were to override, the value in the local configuration will take precedence over the shared configuration.

That concludes my three part starter posts on Configuration Services. The Configuration Services is no where near perfect yet but as with anything else, I am sure this will evolve over multiple releases. Happy Coding!!!