Using App.config and System.Configuration

Suppose that you have a interface in your project

1: namespace Configuration.Logger 2: { 3: public interface ILogger 4: { 5: void Write; 6: } 7: }

and a few his implementations.

1: public class FileLogger: ILogger 2: { 3: public void Write 4: { 5: Console.WriteLine("Writing to file"); 6: } 7: } 8: 9: public class DatabaseLogger: ILogger 10: { 11: public void Write 12: { 13: Console.WriteLine("Writing to the database"); 14: } 15: }

Depending on the situation, you want use first of them, another time the second. But how (and where) change it? The simplest possibility is to change it in the source code, but this is associated with recompilation of the entire project. So what else we can do?

The.NET Framework comes to us with a System.Configuration namespace which is responsible for working with the configuration files (Machine.config, Web.config, App.config). Now I think, the answer is simple – create and work with own config file. Thanks to him we can change the concrete implementation of the interface without recompilation the entire project.

So let’s create one.

1: 2: 3: 4: <section name="loggerMappingsConfiguration" 5: type="Configuration.Configuration.LoggerSettings, 6: Configuration,Version=1.0.0.0,Culture=neutral,PublicKeyToken=null"/> 7: 8: 9: 10: <loggerMapping interfaceShortTypeName="ILogger" 11: concreteFullTypeName="Configuration.Logger.DatabaseLogger, 12: Configuration,Version=1.0.0.0,Culture=neutral,PublicKeyToken=null"/> 13: 14: 15:

This is a simple App.config with one section only. As I mentioned earlier, System.Configuration provides classes to work with it. What we have to do is inherit from them and create a new for this example. We need to create a three classes derived from:

The forth class this will be a helper class contains a string values for configuration element. Let’s start from her.

1: internal static class LoggerMappingConstants 2: { 3: public const string ConfigurationPropertyName = "loggerMappings"; 4: public const string ConfigurationElementName = "loggerMapping"; 5: public const string InterfaceShortTypeNameAttributeName = "interfaceShortTypeName"; 6: public const string ConcreteFullTypeNameAttributeName = "concreteFullTypeName"; 7: public const string LoggerMappingsConfigurationSectionName = "loggerMappingsConfiguration"; 8: }

These values are used in other classes several times, so create a static class with them is a good idea. Class derived from ConfigurationElement is show below

1: public sealed class LoggerMappingElement: ConfigurationElement 2: { 3: [ConfigurationProperty(LoggerMappingConstants.InterfaceShortTypeNameAttributeName, 4: IsKey = true, IsRequired = true)] 5: public string InterfaceShortTypeName 6: { 7: get 8: { 9: return (string)this[ 10: LoggerMappingConstants.InterfaceShortTypeNameAttributeName]; 11: } 12: set 13: { 14: this[LoggerMappingConstants.InterfaceShortTypeNameAttributeName] = value; 15: } 16: } 17: 18: [ConfigurationProperty(LoggerMappingConstants.ConcreteFullTypeNameAttributeName, 19: IsRequired = true)] 20: public string ConcreteFullTypeName 21: { 22: get 23: { 24: return (string)this[LoggerMappingConstants.ConcreteFullTypeNameAttributeName]; 25: } 26: set 27: { 28: this[LoggerMappingConstants.ConcreteFullTypeNameAttributeName] = value; 29: } 30: } 31: }

Contain a two methods mapping to the appropriate properties from config file. Class derived from ConfigurationElementCollection

1: public sealed class LoggerMappingCollection: ConfigurationElementCollection 2: { 3: 4: protected override ConfigurationElement CreateNewElement 5: { 6: return new LoggerMappingElement; 7: } 8: 9: protected override object GetElementKey(ConfigurationElement element) 10: { 11: return ((LoggerMappingElement)element).InterfaceShortTypeName; 12: } 13: 14: public override ConfigurationElementCollectionType CollectionType 15: { 16: get 17: { 18: return ConfigurationElementCollectionType.BasicMap; 19: } 20: } 21: 22: protected override string ElementName 23: { 24: get 25: { 26: return LoggerMappingConstants.ConfigurationElementName; 27: } 28: } 29: 30: public new LoggerMappingElement this[string interfaceShortTypeName] 31: { 32: get 33: { 34: return 35: (LoggerMappingElement)this.BaseGet(interfaceShortTypeName); 36: } 37: } 38: } 39:

And the last class:

1: public class LoggerSettings: ConfigurationSection 2: { 3: [ConfigurationProperty(LoggerMappingConstants.ConfigurationPropertyName, 4: IsDefaultCollection = true)] 5: public LoggerMappingCollection LoggerMappings 6: { 7: get 8: { 9: return 10: (LoggerMappingCollection)base[LoggerMappingConstants.ConfigurationPropertyName]; 11: } 12: } 13: }

To test these classes, we can build a factory that will create objects depending on the settings in the configuration file

1: public static class LoggerFactory 2: { 3: public static ILogger CreateLogger 4: { 5: ILogger logger; 6: string interfaceShortName = typeof(ILogger).Name; 7: 8: LoggerSettings settings = (LoggerSettings)ConfigurationManager.GetSection( 9: LoggerMappingConstants.LoggerMappingsConfigurationSectionName); 10: 11: logger = Activator.CreateInstance( 12: Type.GetType(settings.LoggerMappings[interfaceShortName] 13:.ConcreteFullTypeName)) as ILogger; 14: 15: return logger; 16: } 17: }

And finally check the result

1: class Program 2: { 3: static void Main(string[] args) 4: { 5: ILogger logger = new DatabaseLogger; 6: ILogger loggerFromFactory = LoggerFactory.CreateLogger; 7: 8: Debug.Assert(loggerFromFactory.GetType == logger.GetType); 9: } 10: }

Source code is available (click the blue button called "Pobierz plik").

This article is part of the GWB Archives. Original Author: Łukasz Kuryło

New on Geeks with Blogs

  • We Won The One Award I Actually Care About

    Full Scale made the Inc. 5000 for the fifth year straight, the 12th listing across my three companies. Here is why the one award you cannot buy is worth stopping for.

  • Your Customers Build the Features Now

    I let a tool I liked sit dead for a year rather than build the features I wanted. An MCP server meant I never had to, and your customers can do the same to your product.

  • Get the Size of a Directory in Linux the Easy Way

    du -sh for the quick answer, ncdu for the cleanup, df for the disk itself: every command for checking directory size in Linux, plus why du and df never agree.

  • Vim Search and Replace: The Ultimate Guide

    One :%s command replaces every match in a file before a find dialog would even open. The Vim substitute patterns worth the muscle memory: flags, ranges, capture groups, and multi-file edits.