Rick Strahl's Web Log

Wind, waves, code and everything in between...
ASP.NET • C# • HTML5 • JavaScript • AngularJs
Contact   •   Articles   •   Products   •   Support   •   Search
Ad-free experience sponsored by:
ASPOSE - the market leader of .NET and Java APIs for file formats – natively work with DOCX, XLSX, PPT, PDF, images and more

Building a better .NET Application Configuration Class - revisited


Many years ago, when I was just starting out with .NET (in the 1.0 days - now nearly 10 years ago), I wrote an article about an Application Configuration component I created to improve application configuration using strongly typed classes. To me, application configuration is something that I've been adamant about since - well forever, even pre-dating .NET. An easy to access configuration store along with an easy application access mechanism to access and maintain configuration settings through code are crucial to building applications that are adaptable. I found that the default configuration features built into .NET are pretty good, but they still take a bit of effort to maintain and manage.

So I built my own long ago, with focus on a code-first approach. The configuration library I created is small, low-impact and simple to use - you create a class and add properties, instantiate and fire away at configuration values. The library has been updated and majorly refactored a few times over the years to adapt to changes in .NET and common usage patterns. In this post I'll describe the latest updated version of this library as I think many of you might find it useful.

The very latest version of this library is now available on its own as a smaller self contained component. You can find the actual files and basic usage info here:

But before I jump in and describe the library lets spend a few minutes reviewing what configuration options are available in the box in .NET and why I think it made sense to maintain a custom configuration solution over the years.

Configuration? How boring, but…

I consider configuration information a vital component of any application and I use it extensively for allowing customization of the application both at runtime and through external configuration settings typically using .config files and occasionally in shared environments with settings stored in a database. The trick to effective configuration in any application is to make creating new configuration values and using them in your application drop dead easy. To me the easiest way to do this is by simply creating a class that holds configuration values, along with a mechanism for easily serializing that configuration data. You shouldn't have to think about configuration - it should just work like just about any other class in your projects :-)

In my applications, I try to make as many options as possible user configurable and configure everything from user application settings, to administrative configuration details, to some top level business logic options all the way to developer options that allow me to do things like switch in and out of detailed debug modes, turn on logging or tracing and so on. Configuration information can be varied so it should also be easy to have multiple configuration stores and switch between them easily.

This is not exactly a sexy feature, but one that is quite vital to the usability and especially the configurability of an application. If I have to think about setting or using of configuration data too much, have to remember every setting in my head (ie. "magic strings"), or have to write a bunch of code to retrieve values, I'll end up not using them as much as I should, and consequently end up with an application that isn’t as configurable as it could be.

What does .NET provide for Configuration Management?

So what do you use for configuration? If you're like most like developers you probably rely on the AppSettings class which provides single level configuration values at the appSettings key. You know the kind that's stored in your web.config or application.config file:

<configuration>
  <appSettings>
    <add key="ApplicationTitle" value="Configuration Sample (default)" />
    <add key="ApplicationSubTitle" value="Making ASP.NET Easier to use" />
    <add key="DebugMode" value="Default" />
    <add key="MaxPageItems" value="0" />
appSettings> configuration>

All the configuration setting values are stored in string format in the appSettings section of an application's configuration file.

To access settings, the System.Configuration assembly in .NET provides a fairly easy way to access these configuration values via code from within applications:

string applicationTitle = ConfigurationSettings.AppSettings["ApplicationTitle"];

Easy enough, right? But it gets a little more complex if you need to grab a value that's not a string. For numeric or enum values you need to first ensure the value exists (is non-null) and then convert the string explicitly to whatever type, since configuration values are always strings. Here's what this looks like:

int maxPageItems = 0;
string tInt = ConfigurationManager.AppSettings["MaxPageItems"];
if (tInt != null)
    int.TryParse(tInt, out maxPageItems);

DebugModes mode = DebugModes.Default;
string tenum = ConfigurationManager.AppSettings["DebugMode"];
if (tenum != null)    
    Enum.TryParse(tenum,out mode);

which is a bit verbose if you have to go through this every time you want to use a configuration value. You also have to remember the values and use the ConfigurationManager.AppSettings class. Minor but 'noisy' in application level code.

AppSettings values are also limited to a single AppSettings section inside of an application's or Web config file. Luckily you can also create custom configuration sections use the same configuration format in custom sections in a config file as long as those custom sections get declared:

<configuration>
  <configSections>
    <section name="CustomConfiguration" requirePermission="false" 
type="System.Configuration.NameValueSectionHandler,System,Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> configSections> <CustomConfiguration> <add key="ApplicationName" value="Configuration Tests" /> <add key="DebugMode" value="Default" /> <add key="MaxDisplayListItems" value="15" /> <add key="SendAdminEmailConfirmations" value="False" /> <add key="MailServer" value="3v7daoNQzllLoX0yJE2weBlljCp0MgyY8/DVkRijRTI=" /> <add key="MailServerPassword" value="ud+2+RJyqPifhK4FXm3leg==" /> CustomConfiguration> configuration>

You can then access a custom section with:

var settings = ConfigurationManager.GetSection("CustomConfiguration") as NameValueCollection;            
Console.WriteLine(settings["ApplicationName"]);

and essentially get the same behavior as you get with the AppSettings keys. The collection you get back is a read-only NameValueCollection that's easy to run through and read from.

.NET's configuration provider also supports strongly typed configuration sections via code, which involves creating classes based base on the ConfigurationSection class. This gives you a slightly different configuration format that's a little less verbose than the add/key/value structure of NameValue type configuration:

xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="MyCustomConfiguration" requirePermission="false"
             type="Westwind.Utilities.Configuration.MyCustomConfigurationSection,Westwind.Utilities.Configuration.Tests"/>
  configSections>
  <MyCustomConfiguration 
        ApplicationName="Configuration Tests"  
        MaxDisplayItems="25"
        DebugMode ="ApplicationErrorMessage"         
        />
configuration>

This is a little more involved in that you need to define a class and define each property along with some inherited logic to retrieve the configuration value.

class MyCustomConfigurationSection : ConfigurationSection
{        
    [ConfigurationProperty("ApplicationName")]
    public string ApplicationName
    {
        get { return (string) this["ApplicationName"]; }
        set { this["ApplicationName"] = value; }
    }

    [ConfigurationProperty("MaxDisplayItems",DefaultValue=15)]
    public int MaxDisplayItems
    {
        get { return (int) this["MaxDisplayItems"]; }
        set { this["MaxDisplayItems"] = value; }
    }

    [ConfigurationProperty("DebugMode")]
    public DebugModes DebugMode
    {
        get { return (DebugModes) this["DebugMode"]; }
        set { this["DebugMode"] = value; }
    }                
}

but the advantage is that you can reference the class as a strongly typed class in your application. With a bit of work you can even get Intellisense to work on your configuration settings inside of the configuration file. You can find out more from this detailed article from Rob Seeder. Strongly typed configuration classes are useful for static components that have lots of configuration settings, but for typical dynamic configuration settings that frequently change in applications the more dynamic section of key value pairs is more flexible and easier to work with dynamically.

For certain kinds of desktop applications, Visual Studio can also create a strongly typed Settings class. If you create a WinForms or WPF project for example it adds a Settings.settings file, which lets you visually assign properties in a designer. When saved the designer creates a class that accesses the AppSettings values indirectly.

SettingsDesigner

This is pretty nice in that it keeps all configuration information inside of a class that is managed for you as you add values. You also get default values and you can easily use the class in code:

var settings = new Settings();
var mode = Settings.DebugMode;
MessageBox.Show(mode.ToString());

The class is strongly typed and internally simply references a custom configuration section of values that are read from the config file. This is a nice feature, but it's limited to desktop Windows applications Console, WinForms, WPF and Windows 8 applications. It's also limited to a single configuration class.

Writing Values to the Config File

.NET also has support for writing configuration values back into configuration files via the configuration manager. You can load up a configuration, load a section and make changes to it, then write the entire configuration back out to disk assuming you have permissions to do this.

var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
var section = config.GetSection("appSettings") as AppSettingsSection;            
section.Settings.Add("NewKey", "Value");
config.Save();

This also works for Web Applications where you can use:

var config = WebConfigurationManager.OpenWebConfiguration("~");

to read the top level configuration.

Permissions are crucial here and often you will not be able to write configuration back using this approach. For Web applications Full Trust and read/write access to the web.config file are required. For desktop applications write file rights in the folder are required,  which is often not available - with User Account Control on you typically don't have rights to write into the Configuration folder.

Clearly there are a lot of choices available in .NET to handle configuration storage and retrieval. It's great that the ConfigurationManager is available to provide base features to create simple configuration storage quickly.

Posted in .NET  ASP.NET  

The Voices of Reason


 

Dan Nemec
December 28, 2012

# re: Building a better .NET Application Configuration Class - revisited

You mention that there's support for multiple configuration stores on the Github README, but it seems like all that means is "create a configuration store, then create another one". Is there support at all for layered configuration stores?

For example, suppose that there are machine-wide configuration settings but each user has his/her own configuration file that can selectively override some settings. The most common scenario I find myself needing is support for a "default" configuration file (separate from a hardcoded constructor) that can be source controlled and copied to each (development, staging, and production) server without modification. On top of that, each server has a custom configuration file that may (or may not) override certain settings like connection strings.

I'm surprised the .Net community doesn't have a solution for that since it's a common pattern when working with Python/Django projects (although Python is a scripting language so the "configuration files" are just normal code files). I ended up having to write my own since I couldn't find an existing implementation that worked the way I needed it to.

Rick Strahl
December 28, 2012

# re: Building a better .NET Application Configuration Class - revisited

@Dan - You're right this configuration manager basically expects you to create one or more self contained classes that map to a single store - there's no inheritance per se. But you can of course isolate configuration settings and store them separately - even in completely separate types of stores.

While I think that would be a useful feature, I'm not sure if this would be so common in an application/component level situation? I can see this being important for administrative type settings, but not so much for application/user level settings.

Dan Nemec
December 29, 2012

# re: Building a better .NET Application Configuration Class - revisited

The most obvious situation I can think of is loading general settings from a file (or some other provider) and then allowing command line parameters to selectively override certain settings at runtime *without* needing a lot of glue code eg.

if(commandline.ConnectionString != null)
{ 
    settings.ConnectionString = commandline.ConnectionString
}
if(commandline.Timeout != null)
{ 
    settings.Timeout = commandline.Timeout
}
...etc.

Maybe it's not as common as I'm expecting it to be.

Rick Strahl
December 29, 2012

# re: Building a better .NET Application Configuration Class - revisited

@Dan - sure that works just fine for application code, but how would you make that generic to fit as part of a component? Or how would you even want to specify this? Hand coding the above sort of thing is easy but how to make that generic and extensible.

Dan Nemec
December 29, 2012

# re: Building a better .NET Application Configuration Class - revisited

I'm not entirely sure what you mean "as part of a component" or if I'm misunderstanding what you're asking, but Reflection would enable it to scale to any number of configuration properties. So long as each Provider uses the same Type, we can just iterate over all properties and copy over any non-default values in order to merge the two objects. To detect whether a value is non-default, we can apply a `new()` constraint to our Configuration object and use the property values of a new'ed up object as the reference.

Given this configuration class:

public class Config
{
    public int Age { get; set; }
 
    public string Name { get; set; }
}


public static void MergeWith<T>(this T primary, T secondary, T defaultObject)
{
    foreach (var pi in typeof(T).GetProperties())
    {
        var secValue = pi.GetValue(secondary, null);
        var defaultValue = pi.GetValue(defaultObject, null);
 
        if (!Equals(secValue, defaultValue))
        {
            pi.SetValue(primary, secValue, null);
        }
    }
}


Imagine configA was populated from an XML file and configB was populated from a SQL database provider:

var defaultConfig = new Config();  // Age == 0 and Name == null
 
var configA = new Config
{
    Name = "Nate",
    Age = 10
};
 
var configB = new Config
{
    Name = "Fred",
    Age = 0  // This is the default value, it shouldn't be merged
};
 
configA.MergeWith(configB, defaultConfig);
 
// // Only the non-default properties were copied over:
// assert(configA.Name == "Fred")
// assert(configA.Age == 10)


Now we can pass configA on to whatever application logic needs the configuration object. As I mentioned earlier it allows us to keep a configuration file of sane defaults source controlled and use a separate, non source controlled file of "local overrides" if any values need to be changed per-host.

Most of the time we don't really care *where* the settings come from, just that each provider has an appropriate priority (eg. any values in the SQL provider take precedence over the XML provider) and that we shouldn't need to check multiple configuration objects every time we need a single config value.

Toni
December 30, 2012

# re: Building a better .NET Application Configuration Class - revisited

Interesting approach to this problem. I built my AppSettings (https://github.com/tparvi/appsettings) library about one year ago. I wanted something simple so I didn't implement support for anything else than basic settings (no sections).

Matt
January 04, 2013

# re: Building a better .NET Application Configuration Class - revisited

.settings files are not limited to Windows/WPF apps, you can use them just fine in web apps too.

The item template is missing from Visual Studio, but if you go to Add New Item > Text Document, and then change the file extension from ".txt" to ".settings", you'll get a fully functional settings designer.

A couple of caveats - you need to make sure all the settings are machine scoped, and you need to change the access modifier to public if you want to access the settings from a Razor view.

GJL
January 04, 2013

# re: Building a better .NET Application Configuration Class - revisited

Nice work Rick

I used your previous version in an app a while back (storing in app.config), and was pleased.

I dont think there's any one solution for all the issues out there - I usually end up using a hybrid of this and say a data-table in SQLite or such... for my most recent app Im writing, I'm testing JSON as a configuration 'language'/representation rather than say XML - I like the ability to have arrays in JSON which map to lists of objects in my Config Object class - one negative about using JSON, I like to comment my config files, here XML wins :-)

One thing Im working on for either case is the ability to have a key and such a value :-

${app-path}/data/test-file.ext

And when the config value is loaded, ${app-path} is translated/interpolated. I know there are some frameworks out there for this sort of thing, String Template (Terence Parr?) comes to mind, but 'too big' for this as far as I see ..

Keep up the work and the thought provoking dialog
cheers
GJL

Rick Strahl
January 04, 2013

# re: Building a better .NET Application Configuration Class - revisited

@GJL - I had thought about adding a JSON Configuration Provider but didn't for a couple of reasons. The main one is that would add a dependency on JSON.NET (or some other 3rd party library) in order to provide nicely formatted JSON support. The other is that XML Serialization (which is supported via the XmlFile Provider) really provides complex structure support so if you want to have nested objects or arrays/lists/collections of objects you can use that with XML.

As popular as JSON is, I think it's even MORE geeky than XML for a user editable format. I think even non-tech people probably can figure out XML formatting. JSON however, has requirements for quotes, escape characters and various other things. If user editing doesn't matter than using XML Serialization in binary mode is more efficient yet :-)

I'm going to look into the JSON Provider nevertheless though. several people have commented/asked for that in the past. Maybe using a dynamically loaded reference to JSON.NET using Reflection instantiation and dynamic would work to keep a hard coded reference out of the project.

Jeffl
January 16, 2013

# re: Building a better .NET Application Configuration Class - revisited

Rick,

I'm having a hard time with your class because the Initialize method is protected.

Rick Strahl
January 16, 2013

# re: Building a better .NET Application Configuration Class - revisited

@Jeff - you need to use the new westwind.utilities.configuration assembly - the old Westwind.Utilities version doesn't have the 2.0 version of this new functionality. Unfortunately there's no good way right now to mix the two short of copying the new code into the old project.

I'm in the process of reworking Westwind.Utilities for a new release that will have the new features - this will be a 2.0 version that includes some refactorings and also some removal of stuff the framework provides. Kind of a housecleaning to bring it up to date. Unfortunately I've not been able to push this out to Github just yet as there are still a bunch of housekeeping issues and documentation to take care of.

Jason Finch
January 30, 2013

# re: Building a better .NET Application Configuration Class - revisited

I found this Json Config library pretty handing dealing with ever changing configurations.

https://github.com/Dynalon/JsonConfig

Supports merging of configs.
Nested objects etc.

@Rick, you mention JSON is even more Geeky than XML.. Just wait until you have to use CDATA's or escape ampersands and angle brackets, XML can look geeky too!

Also for end users this helps to edit JSON. http://www.jsoneditoronline.org/

Rick Strahl
January 31, 2013

# re: Building a better .NET Application Configuration Class - revisited

@Jason - well XML is not optimal for human editing either, but IMHO still way better than JSON editing with all of its encoding requirements. In the config class the XML generated is plain (either .config files or XML Serialization). As I mentioned it'd be pretty trivial to add JSON configuration format to this class, but it does add a dependency for a JSON serializer library (JSON.NET or System.Web.Extensions).

Fred Peters
April 01, 2013

# re: Building a better .NET Application Configuration Class - revisited

I had used your utility years ago and tried to use the new one from NuGet. But like Jeffl, no matter how I set things up I get the protected error on the Initialize method.

Rick Strahl
April 02, 2013

# re: Building a better .NET Application Configuration Class - revisited

@Fred - not sure why that would be. Where are you getting the NuGet package? It should be:

install-package Westwind.Utilities.Configuration


from the package manager console. I just tried this to confirm that indeed the right code is in there and that it works. Here's a small console example to demonstrate (New Console Project, add the NuGet is all that's needed):

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(App.Configuration.ApplicationName);
        Console.WriteLine(App.Configuration.MaxDisplayItems);
        Console.Read();
    }
}
 
public class ApplicationConfiguration : AppConfiguration
{
    
    public string ApplicationName { get; set; }
    public int MaxDisplayItems { get; set; }
 
    public ApplicationConfiguration()
    {
        ApplicationName = "Test Application";
        MaxDisplayItems = 15;
    }
}
 
public class App
{
    public static ApplicationConfiguration Configuration { get; set; }
 
    static App()
    {
        Configuration = new ApplicationConfiguration();
        Configuration.Initialize();
    }
}


If you are using the existing Westwind.Utilities assembly from the West Wind Toolkit, that version is the old configuration object which requires constructor configuration.

There are new versions of these that are in beta now and can be found on GitHub:

https://github.com/RickStrahl/WestwindToolkit

The new version is not 100% backwards compatible. Almost all functionality is still there, but a number of things have been refactored into more logical classes etc. Currently none of these are on NuGet, but you can definitely pick up the new binaries out of the GitHub project.

Update:
I've also updated the Westwind.Utilities assembly to version 2.0 which is now available on NuGet and which includes the new configuration components supporting above syntax.


Fred Peters
April 22, 2013

# re: Building a better .NET Application Configuration Class - revisited

Thanks Rick, it does work for the console application. I'll have to see what's different in my Winforms app.

Rick Strahl
April 22, 2013

# re: Building a better .NET Application Configuration Class - revisited

@Fred - Last week I also updated the NuGet package for Westwind.Utilities 2.0 which includes the new Application Configuration class. Just be aware that the new version is not 100% backwards compatible. The ApplicationConfiguration is the most prominent change - others are refactored classes and namespaces for more logic groupings.

PM > install-package Westwind.Utilities

If you use this package remove the Westwind.Utilities.Configuration assembly.

Andrew
June 06, 2013

# re: Building a better .NET Application Configuration Class - revisited

Just want to clarify one thing regarding licensing. Your page says:

Usage for open source or personal projects is free. Any other usage commercial or otherwise requires registration for use of product on a per developer basis. This includes application in commerical, non-profit and government organizations. Once registered (either paid or personal/open source) you are allowed an Unlimited Runtime Distribution of compiled components for any number of applications.

If I'm going to use your compiled library in a commercial project or in a project for internal use of some company, and it is not a reusing of your code since our project is not a configuration library itself, do I need to obtain license? Or I'm free to use your binaries like that?

Please advise.
Thanks.

Trevor Moyakhe
June 11, 2013

# re: Building a better .NET Application Configuration Class - revisited

Used the XML serializer until I had to serialize arbitrary types contained in config classes. These unknown type would be created by plugin writers, and the main application just needed to know that there will be settings for a particular plugin. The object type is never relevant to the main app, since it never directly uses the plugin settings. I did, however, centralize the reading and writing of settings.

I also felt cheated, having Googled my way into this page, getting hold of the binaries and using them, before I read up on the licensing :-(

So, the XML serializer will not serialize unknown types and I need to pay if I want to wire these binaries to a solution I make money from. Don't think so.

Came up with the following two classes. You can use them without paying anyone, including me.

1. Serializer.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
using System.Runtime.Serialization;
 
namespace Ios.Serialization
{
    public class Serializer
    {
        public static void WriteObject(string fileName, object obj)
        {
            if (obj != null && !string.IsNullOrWhiteSpace(fileName))
            {
                FileStream stream = new FileStream(fileName, FileMode.Create);
                XmlDictionaryWriter writer = XmlDictionaryWriter.CreateTextWriter(stream);
                NetDataContractSerializer serializer = new NetDataContractSerializer();
 
                serializer.WriteObject(writer, obj);
                writer.Close();
            }
        }
 
        public static object ReadObject(string fileName)
        {
            object obj = null;
 
            if (!string.IsNullOrWhiteSpace(fileName) && File.Exists(fileName))
            {
                FileStream stream = new FileStream(fileName, FileMode.Open);
                XmlDictionaryReader reader = XmlDictionaryReader.CreateTextReader(stream, new XmlDictionaryReaderQuotas());
                NetDataContractSerializer serializer = new NetDataContractSerializer();
                
                obj = serializer.ReadObject(reader, true);
                
                reader.Close();
            }
 
            return obj;
        }
    }
}

2. ConfigProvider.cs
using System.IO;
 
namespace Ios.Serialization
{
    public class ConfigurationProvider<T> where T : new()
    {
        private string _FileName;
        private T _Configuration;
 
        public ConfigurationProvider(string fileName)
        {
            _FileName = fileName;
            
            ReadConfig();
        }
 
        public T Configuration
        {
            get { return _Configuration; }
            set { _Configuration = value; }
        }
 
        public void ReadConfig()
        {
            if (!string.IsNullOrWhiteSpace(_FileName) && File.Exists(_FileName))
            {
                object obj = Ios.Serialization.Serializer.ReadObject(_FileName);
 
                if (obj != null && obj is T)
                    _Configuration = (T)obj;
                else
                    _Configuration = new T();
            }
            else
                _Configuration = new T();
        }
 
        public void WriteConfig()
        {
            if (!string.IsNullOrWhiteSpace(_FileName) && _Configuration != null)
            {
                Ios.Serialization.Serializer.WriteObject(_FileName, _Configuration);
            }
        }
    }
}

Usage:
You will need to ensure that the class you need to serialize provides a public, parameterless constructor. (See the constraint in ConfigurationProvider class)

Class to store settings...
public class SomeConfigClass
{
    public SomeConfigClass()
    {
        //do something here
    }
 
    public string StringProp { get; set; }
 
    public int IntProp { get; set; }
 
    public object ObjectProp { get; set; }
}


Using the classes
ConfigurationProvider<SomeConfigClass> configProvider = new ConfigurationProvider<SomeConfigClass>("DaConfigFile.xml");


At this point, configProvider.Configuration should provide an instance of SomeConfigClass. This class will either be deserialized from the xml file provided, or a new instance will be created.

You can then call configProvider.ReadConfig() if you need to reload the settings from file.
To persist the settings to file, use configProvider.WriteConfig().

Credits:
The author of this article, for showing that it's possible.
Microsoft documentation, for the serialization code.

Live Long and prosper.
Trevor Moyakhe

Zx
August 16, 2013

# re: Building a better .NET Application Configuration Class - revisited

Any idea why this doesn't work in Debug mode?

Rick Strahl
August 18, 2013

# re: Building a better .NET Application Configuration Class - revisited

@Zx - works for me in Debug mode. What 'doesn't work' exactly?

Mat Polutta
August 23, 2013

# re: Building a better .NET Application Configuration Class - revisited

I added your project to one of the government projects I work on, before seeing the License in the github help. Unfortunately, paying the licensing fee is a nightmare. So, I have to replace it. We have something similar that I created using Castle IoC to apply database supplied configuration settings to a Windows Service that schedules various tasks at runtime. No problem - back to work to integrate it into this project.

In this project (and others), our government client wishes to change configurations from a SQL Server data table, so that a service may be reconfigured without going through the Kafka-ish Change Configuration Management (yes - changing a config file in production takes 200 man-hours and coordinating who-knows-how-many meetings, phone calls, emails, shared-screen sessions in 3 different environments). Our client prefers to change the settings using an Everything Database Application in which he edits each key-value pair in its own table row.

Let me know if you are interested - didn't want to copy the whole class, but here is most of the code, which is based on your SqlServerConfigurationProvider:

    public class EdbSqlServerConfigurationProvider<TAppConfiguration> : ConfigurationProviderBase<TAppConfiguration>
        where TAppConfiguration : AppConfiguration, new()
    {
 
        private static string CreateTemplate =
@"CREATE TABLE [{0}](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [SectionKey] [int] NOT NULL,
    [Name] [varchar](200) NOT NULL,
    [Value] [varchar](8000) NOT NULL,
    [Description] [varchar](1000) NULL,
    [created_by] [nvarchar](50) NULL,
    [created_datetime] [smalldatetime] NULL,
    [updated_by] [nvarchar](50) NULL,
    [updated_datetime] [smalldatetime] NULL,
 CONSTRAINT [PK_{0}] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]";
...
 
        public override T Read<T>()
        {
            using (SqlDataAccess data = new SqlDataAccess(ConnectionString, ProviderName))
            {
                string sql = "select [ID], [Name], [Value] from [" + Tablename + "] where SectionKey=" + Key.ToString();
...
 
                List<string> items = new List<string>();
                string itemTemplate = "<{0}>{1}</{0}>";
                while (reader.Read())
                {
                    items.Add(string.Format(itemTemplate, (string)reader["Name"], (string)reader["Value"]));
                    /// TODO: Validate - remove if element is not allowed, since deserialization will fail
                }
                /// TODO: Add missing Properties and set to default
 
                reader.Close();
                data.CloseConnection();
 
                //Convert Dictionary to XML
                string xmlTemplate = 
                    "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
                    "<{0} xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">{1}</{0}>";
                string itemContent = string.Join("", items.ToArray());
 
                T instance = null;
                //Empty Content List indicates nothing to serialize to XML, so treat as empty.
                if (string.IsNullOrEmpty(itemContent))
                {
                    IsEmpty = true;
                    instance = new T();
                    instance.Provider = this;
                }
                else // Has content, so serialize to XML.
                {
                    string xmlConfig = null;
                    xmlConfig = string.Format(xmlTemplate, typeof(T).Name, itemContent);
                    instance = Read<T>(xmlConfig);
                }
                return instance;
            }
        }
 
        public override bool Read(AppConfiguration config)
        {
            TAppConfiguration newConfig = Read<TAppConfiguration>();
            if (newConfig == null)
                return false;
 
            ///TODO: Rick had this backwards - config is the source, newConfig is the target
            //DataUtils.CopyObjectData(newConfig, config,"Provider,ErrorMessage");
            DataUtils.CopyObjectData(config, newConfig, "Provider,ErrorMessage");
 
            if (CreatedTable || IsEmpty)
            {
                CreatedTable = false;
                IsEmpty = false;
                //Seed the Section with values from the config data
                Write(newConfig);
            }
            return true;
        }
 
...
 
        public override bool Write(AppConfiguration config)
        {
            Dictionary<string, string> nameValuePairs = GetNameValuePairsDictionary(config);
 
            // Set a flag for missing fields
            // If we have any we'll need to write them out into .config
            //bool missingFields = false;
 
            string fieldsToEncrypt = "," + PropertiesToEncrypt.ToLower() + ",";
 
            SqlDataAccess data = new SqlDataAccess(ConnectionString, ProviderName);
 
            string sqlTemplate =
@"declare @pairCount int
select @pairCount=count(ID) from [Configuration] where SectionKey=@SectionKey AND [Name]=@Name
if (@pairCount = 0)
begin
    Insert [{0}] (SectionKey,[Name],[Value],[created_by],[created_datetime]) values (@SectionKey,@Name,@ConfigData,@UpdatedBy,@UpdatedDatetime)
end
else
begin
    Update [{0}] set [Value]=@ConfigData, [updated_by]=@UpdatedBy, [updated_datetime]=@UpdatedDatetime where SectionKey=@SectionKey AND [Name]=@Name AND ([Value] is null OR [Value]<>@ConfigData)
end";
 
            foreach (var item in nameValuePairs)
            {
                string sql = string.Format(sqlTemplate, Tablename);
                //, Key, item.Key, "code", DateTime.Now.ToString("s"));
                var parameters = new[] { 
                    new SqlParameter() { ParameterName="@SectionKey", SqlDbType = System.Data.SqlDbType.Int, Value=Key },
                    new SqlParameter() { ParameterName="@Name", SqlDbType = System.Data.SqlDbType.VarChar, Size=200, Value=item.Key },
                    new SqlParameter() { ParameterName="@UpdatedBy", SqlDbType = System.Data.SqlDbType.VarChar, Size=50, Value="code" },
                    new SqlParameter() { ParameterName="@UpdatedDatetime", SqlDbType = System.Data.SqlDbType.DateTime, Value=DateTime.Now },
                    new SqlParameter() { ParameterName="@ConfigData", SqlDbType = System.Data.SqlDbType.VarChar, Size=8000, Value=item.Value }
                };
 
                int result = 0;
                try
                {
                    //Execute sqlTemplate
                    result = data.ExecuteNonQuery(sql, parameters);
 
...
 
        private Dictionary<string, string> GetNameValuePairsDictionary(AppConfiguration config)
        {
            Dictionary<string, string> pairs = new Dictionary<string, string>();
            string fieldsToEncrypt = "," + PropertiesToEncrypt.ToLower() + ",";
            Type typeWebConfig = config.GetType();
            MemberInfo[] Fields = typeWebConfig.GetMembers(BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty | BindingFlags.GetField);
 
            // Loop through all fields and properties                 
            foreach (MemberInfo Member in Fields)
            {
                string typeName = null;
 
                FieldInfo field = null;
                PropertyInfo property = null;
                Type fieldType = null;
                string value = null;
                object rawValue = null;
 
                if (Member.MemberType == MemberTypes.Field)
                {
                    field = (FieldInfo)Member;
                    fieldType = field.FieldType;
                    typeName = fieldType.Name.ToLower();
                    rawValue = field.GetValue(config);
                    if (rawValue != null)
                        value = rawValue.ToString();
                }
                else if (Member.MemberType == MemberTypes.Property)
                {
                    property = (PropertyInfo)Member;
                    fieldType = property.PropertyType;
                    typeName = fieldType.Name.ToLower();
                    rawValue = property.GetValue(config, null);
                    if (rawValue != null)
                        value = rawValue.ToString();
                }
                else
                    continue;
 
                string fieldName = Member.Name;
                    //.ToLower();
 
                // Error Message is an internal public property
                string fieldNameLowered = fieldName.ToLower();
                if (fieldNameLowered == "errormessage" || fieldNameLowered == "provider")
                    continue;
 
                if (value == null)
                {
                    value = "";
                }
                else //Validate and Coerce the Values to conform to XML Specification
                {
                    if (typeName == "boolean")
                        value = value.ToLower();
                }
 
                // If we're encrypting decrypt any field that are encyrpted
                if (value != string.Empty && fieldsToEncrypt.IndexOf("," + fieldName + ",") > -1)
                    value = Encryption.EncryptString(value, EncryptionKey);
 
                pairs.Add(fieldName, value);
            }
 
            return pairs;
        }
    }

Rick Strahl
August 24, 2013

# re: Building a better .NET Application Configuration Class - revisited

@Mat - I recently changed the licensing to be plain MIT License with an optional availability of a commercial license. The commercial license is not required though and is only for those that need official support and or are required to have licensed software.

Jes Singh
January 30, 2014

# re: Building a better .NET Application Configuration Class - revisited

Rick,

I use the assembly in a .net 4 VB.NET project and it always encrypts the keys by default.
Your code is as-is downloaded and compiled into a assembly for which I added the assembly reference.

This is the inherited call in VB.NET
---------------------------------
Public Class MyConfiguration : Inherits Westwind.Utilities.Configuration.AppConfiguration

Private _FirstName As String
Private _LastName As String

Public Property FirstName As String
Get
Return _FirstName
End Get
Set(value As String)
_FirstName = value
End Set
End Property

Public Property LastName As String
Get
Return _LastName
End Get
Set(value As String)
_LastName = value
End Set
End Property

Private Shared _this As MyConfiguration

Public Sub New()
FirstName = "John"
LastName = "Doe"
End Sub


Public Shared Function _Instance() As MyConfiguration
If _this Is Nothing Then
_this = New MyConfiguration
_this.Initialize(Nothing, "NewSection", Nothing)
End If
Return _this
End Function
End Class



And then in my main form of the application I call

MyConfiguration._Instance()

And the result I see in the existing app.config file is
.....
.....
....
<x77dcf153982bff74>
<add key="x7c540434d0d79540" value="John" />
<add key="x34580293846923b7" value="Doe" />
</x77dcf153982bff74>
</configuration>

Note the encrypted keys and section name

Rick Strahl
January 31, 2014

# re: Building a better .NET Application Configuration Class - revisited

@Jes - that's not coming from the AppConfiguration class - it doesn't encode the keys nor elements only the values and your values aren't encoded.

Maybe you have .NET level encryption enabled on your config file?

More info here:
http://msdn.microsoft.com/library/dtkwfdky.aspx

Jes Singh
January 31, 2014

# re: Building a better .NET Application Configuration Class - revisited

@Rick, no the other keys in the .config are not affected. Only the ones that are written by your code. Any suggestion what am I doing wrong here!

Rick Strahl
January 31, 2014

# re: Building a better .NET Application Configuration Class - revisited

@Jes - like I said we're not encrypting any of the keys or elements, so this has to be something else. All the config class does is read the XML file (the .config) from disk, add or update the keys, then writes it back.

I've never seen or heard anything like this before. You have the source code - you can step through to see and see what values are written.

Jes Singh
January 31, 2014

# re: Building a better .NET Application Configuration Class - revisited

@Rick found the issue.
We obfuscate the .NET exe for deployment after we build it. SO that was the one which was renaming the Properties. And I guess because your code picks up the properties during runtime, so it was writing the obfuscated names.

We had to instruct the obfuscat'or to not obfuscate the MyConfigurationManager class.

Thank you!

Rick Strahl
January 31, 2014

# re: Building a better .NET Application Configuration Class - revisited

@Jes - Ah that's a good one! Yikes that would cause problems with just about any code trying to reflect over properties...

Brian
June 23, 2014

# re: Building a better .NET Application Configuration Class - revisited

I know it's bad form, but if I need access to configuration settings in a class library project, referenced by my main web application project, what would be the best way to implement this technique?

Would I just implement it in the class library project? Implement a base type in the class lib project with the settings used in there and derive in the web app project with the rest?

Rick Strahl
June 23, 2014

# re: Building a better .NET Application Configuration Class - revisited

@Brian - I don't think that's bad form at all - in fact that's how I set up most of my applications. The business layer holds the configuration settings and then exposes the App class with the static Configuration member to the top level application (Web, Win, Service - whatever). The main application can then reference the public static App.Configuration property (or whatever) whenever it needs configuration settings.

Implementation in a class library is no different than in the main project. I'd argue this is the correct way to do this - config settings belong in the component that uses those settings and typically that's not the front end application but the 'business' layer.

This setup works even with multiple configurations in separate class libs,  because each config can get its own section or file so even if you had multiple class libraries they can all store config settings without stepping on each other. I use this configuration setup for all of my business object assemblies and a bunch of my custom components. It works for all of it.

Implementation the same regardless where you implement. Config settings (if you're using .config files anyway) always go to the main application's configuration file regardless which assembly it came from. So yes - create your config class subclassed from AppConfiguration and simply expose a static to it somewhere so it's accessible to your component and/or publicly to the main application. For business apps a public class can be used. For a component that hides its implementation, internal scope can be used.

Hope this helps.

Will
May 13, 2015

# re: Building a better .NET Application Configuration Class - revisited

I like this. I've got some pretty complex types I need to represent in my application settings, and I think this is the solution. I have two questions (which may their answers in this article):

1. How would I go about implementing the Settings.Upgrade() method? Is that handled by this class?

2. Is it possible to bind values to the properties in this class, and have the listening controls change to reflect changes to the class values? This is pretty important.

Of course; I am certain that in the worst case scenario both of these can be implemented. I just do not want to have to do so if they have been taken int account already.

Rick Strahl
May 15, 2015

# re: Building a better .NET Application Configuration Class - revisited

@Will - not sure what upgrade() would do. As it stands there Write() method that will write whatever values are on the object. It'll just write out everything that's on the object now, which is what you'd almost always want. Read the values that are in the config file, make any changes, write them back.

You implement the class and you can add any interfaces you want. If you need to implement IPropertyChanged to get change notification you have to implement that yourself.

+++ Rick ---

Ben
August 28, 2015

# re: Building a better .NET Application Configuration Class - revisited

Hi Rick

Thanks for your blog post

I am using a console application to test your code. But with the console application, there are no dlls so what should i put after type="" ??

any pointers would be appreciated, because I am young to .net and it would take me a long time to work it out otherwise.

regards
Ben


<configSections>
    <section name="MyCustomConfiguration" requirePermission="false"
             type="Westwind.Utilities.Configuration.MyCustomConfigurationSection,Westwind.Utilities.Configuration.Tests"/>
  configSections>
.

Andrejka
August 31, 2016

# re: Building a better .NET Application Configuration Class - revisited

Hi,

settings encryption is buggy if the configuration class was extended (e.g. in a new version of the application) - tries to decrypt non-encrypted default values.

Regards,
Andrejka

Rick Strahl
September 01, 2016

# re: Building a better .NET Application Configuration Class - revisited

Not sure what you mean. If you change the behavior of an encrypted field then yes you'll have a problem because the behavior change is likely to result in invalid encryption keys. Once encrypted you can't make changes to the structure, encryption key or default field behavior otherwise existing values won't work and non-encrypted data can get encrypted. The class doesn't internally know whether the data is encrypted or not - just assumes when read the data is encrypted and when writing the data is not encrypted (yet).

Yehezkel
June 12, 2017

# re: Building a better .NET Application Configuration Class - revisited

Thanks for this project.

I am encountering a problem. I try to use the XML provider (XmlFileConfigurationProvider), but I got an error "Invalid XML configuration file format in file.xml", on the Initialize() call.

The C# code is:

msmxml = new MySettingsManagment();
            var Providerxml = new XmlFileConfigurationProvider<MySettingsManagment>()
            {
                ConfigurationSection = "MySettingsManagment",
                XmlConfigurationFile = "config\\testerFlowSettingsxml.xml"

            };
            try
            {
                msmxml.Initialize(Providerxml);
            }
            catch (Exception ex)
            {}

I wrote the XML file such:

<configuration> 
  <MySettingsManagment>
    <NameApp123>applause</NameApp123>
  </MySettingsManagment>
</configuration>

and such:

<configuration>
  <MySettingsManagment>
    <add key="NameApp123" value="app" />
  </MySettingsManagment>
</configuration>

But in both I get this error.

What should be the right file format?

P.S. What do you think about feature of limiting the allowed values for specific setting? (E.g. numUnits must be between 0 to 10). Didn't find anything like this for .NET


Becca
July 10, 2017

# re: Building a better .NET Application Configuration Class - revisited

I have a remote REST API that contains the configuration settings.

Would it be possible to override the binding in the custom configuration settings class to a remote model returned by a REST client?


Rick Strahl
July 10, 2017

# re: Building a better .NET Application Configuration Class - revisited

@Becca - possibly, but to be honest if you have an API you're calling why not just create a custom class that handles this rather than configuration? Create a class that has the config structure you need and then have Load() and Save() methods that populate the data and then call Load() in ConfigureServices and add to DI. I see little benefit to hooking into the existing startup mechanism just to get essentially a class, especially if you are going to use very specific customized logic to retireve the data. I think the only time this makes sense if you want this to fit into the existing pipeline and needs to work with your REST API, Environment variables etc.


Sri
August 08, 2017

# re: Building a better .NET Application Configuration Class - revisited

I have a windows service with a default app.config. This config file has appSettings and bunch of custom sections. Every client can have their own override config file. I am using the default .net Configuration object to override the sections and app settings which updates the actual app.config file itself. With your code, would it be possible to load the entire app config in memory and then override the settings in memory and use it without actually updating the app config file?

 

West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2017