views:

729

answers:

2

back to the topic of why didn't .net provide a simple (i don't want to implement "ConfigurationSection" "ConfigurationElement" "ConfigurationProperty" for 2 values) way to write values back into application config files... (and i don't want to use 'user' app config)

i want to write to the app.config values, i tired the above method of key,value - for reading its fine but i can't write back into it (it says the collection is read only). even though the following method is supplied -

NameValueCollection.Set(string,string)

am i missing something here ?

this is the way i am trying to do it:

 NameValueCollection nvc = (NameValueCollection)ConfigurationManager.GetSection("options");
 nvc.Set("SelectTimeOut", sqlTimeoutSpinBox.Value.ToString());
+1  A: 

You need to load the config file in the right way. Rather than using the static properties of ConfigurationManager use the methods to load.

Also you need to ensure you are managing the difference between global, application, user roaming and user local configuration. Normally only the last two should be writeable.

Some test code for writing changes to the use config file:

Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoamingAndLocal);
TestConfigData data = (TestConfigData)config.GetSection("testConfigData");
++data.Data;
config.Save(ConfigurationSaveMode.Minimal);

Where TestConfigDate is a custom configuration type:

using System;
using System.Configuration;
using System.Text;

namespace CustomConfiguration {
  public class TestConfigData : ConfigurationSection {

    [ConfigurationProperty("Name", IsRequired=true)]
    public string Name {
      get {
        return (string)this["Name"];
      }
      set {
        this["Name"] = value;
      }
    }

    [ConfigurationProperty("Data", IsRequired=false),
     IntegerValidator(MinValue=0)]
    public int Data {
      get {
        return (int)this["Data"];
      }
      set {
        this["Data"] = value;
      }
    }
  }
}

And the configuration file contains, noting the allowExeDefinition attribute on the section element to define that a user configuration file and override the app.exe.config:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="testConfigData"
             type="CustomConfiguration.TestConfigData, CustomConfiguration"
             allowExeDefinition="MachineToLocalUser"/>
  </configSections>

  <testConfigData Name="Fubar" Data="0"/>
</configuration>
Richard
regarding "loading the configuration in the right way"....currently i do:NameValueCollection nvc = (NameValueCollection)ConfigurationManager.GetSection("options"); nvc.Set("SelectTimeOut", sqlTimeoutSpinBox.Value.ToString());is that wrong ? which way should i instantiate 'nvc' ?
thedrs
I'll need to come back to this later... need to lookup my code that does this so I give you the right place to start. In the mean time, look at the methods of ConfigurationManager that return a complete configuration.
Richard
thanks, but the main issue was not being able to write to the *application* app.config (the one sitting near the exe).Somehow MS decided that they will make it the hardest to do that. (you have to implement the ConfigSection/Element/... classes).writing to the *user* app config is very simple, you can use the Settings class built in to .net vs applications.
thedrs
at least microsoft made a way to do it...proof in the pudding - see example I posted.
JJS
A: 
  • No - NameValueSectionHandler does not assist the user in 'creating' the Xml that will be written to the app.config file.

Fire up reflector and take a look at the following method on System.Configuration.NameValueSectionHandler : internal static object CreateStatic(object parent, XmlNode section, string keyAttriuteName, string valueAttributeName).

Linda Liu from Microsoft Online Community Support gives some great information about NameValueSectionHandler, IConfigurationSectionHandler, and why a DefaultSection instance will be returned from Configuration.GetSection(string) in a discussion at the EggHeadCafe forums: Backwards compatibility of System.Configuration.Configuration.

It is technically possible to read, and create this information, but I recommend you not cause yourself more pain than you must by interacting at this extremely low level of the Configuration API. Please use the below code for Educational purposes only. I highly encourage you to use one of the methods like what Richard mentioned using the declarative style for creating a custom ConfigurationSection

app.config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <configSections>
        <section name="SingleTag" type="System.Configuration.SingleTagSectionHandler"/>
    </configSections>
    <SingleTag scheme="https" server="webmail.contoso.com" domain="CONTOSO" username="jdoe" password="iTz@s3Cr3t!"/>
</configuration>`

You could read this configuration section, but it's not good for your mental health.

Sample C# Code - Stick this inside your void Main(string[] args) and smoke it.

// Read configuration
Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
ConfigurationSection readableSection = config.GetSection("SingleTag");
string readableSectionRawXml = readableSection.SectionInformation.GetRawXml();
XmlDocument readableSectionRawXmlDocument = new XmlDocument();
readableSectionRawXmlDocument.Load(new StringReader(readableSectionRawXml));
SingleTagSectionHandler readableSectionHandler = new SingleTagSectionHandler();
Hashtable result = (Hashtable)readableSectionHandler.Create(null, null, readableSectionRawXmlDocument.DocumentElement);
foreach (string item in result.Keys)
{
    Console.WriteLine("{0}\t=\t{1}", item, result[item]);
}

// Create similar configuration section
Hashtable mySettings = new Hashtable();
mySettings.Add("key1", "value1:" + DateTime.Now);
mySettings.Add("key2", "value2:" + DateTime.Now);
mySettings.Add("key3", "value3:" + DateTime.Now);
mySettings.Add("keynull", null);
mySettings.Add("key4", "value4:" + DateTime.Now);
string rawData = string.Empty;
XmlDocument writableSectionXmlDocument = new XmlDocument();
XmlElement rootElement = writableSectionXmlDocument.CreateElement("CreateSingleTag");
foreach (var item in mySettings.Keys)
{
    if (mySettings[item] != null)
    {
        rootElement.SetAttribute(item.ToString(), mySettings[item].ToString());
    }
}
writableSectionXmlDocument.AppendChild(rootElement);

if (config.Sections.Get("CreateSingleTag") == null)
{
    ConfigurationSection writableSection = new DefaultSection();
    writableSection.SectionInformation.SetRawXml(writableSectionXmlDocument.OuterXml);
    config.Sections.Add("CreateSingleTag", writableSection);
}
else
{
config.Sections["CreateSingleTag"].SectionInformation.SetRawXml(writableSectionXmlDocument.OuterXml);
}

config.Save();

For completeness sake - you need the following usings

using System;
using System.Collections;
using System.Configuration;
using System.IO;
using System.Xml;

and a reference to at least the following assemblies

System
System.Configuration
System.Xml
JJS