Can I change the default config file? - c#

I'm using Jeff Atwood's Last Configuration Section Handler You'll Ever Need, but it only seems to work for the default app.config file. If I wanted to separate certain settings into another file, the deserializing doesn't work, since ConfigurationManager.GetSection only reads from the application's default app.config file. Is it possible to either change path of the default config file or point ConfigurationManager to a second config file?

yes, just replace the section in the default config file with an xml element of the same name that has a configSource="" attribute that points to another file...
... In yr App.config or web.config...
<configSections>
<section name="Connections"
type="BPA.AMP.Configuration.XmlConfigurator, BPA.AMP.Data.Config.DAL"/>
<section name="AutoProcessConfig"
type="BPA.AMP.Configuration.XmlConfigurator, BPA.AMP.Data.Config.DAL"/>
</configSections>
<Connections configSource="Config\Connections.config" />
<AutoProcessConfig configSource="Config\AutoProcess.config" />
And then the common xml;Configurator class
public class XmlConfigurator : IConfigurationSectionHandler
{
public object Create(object parent,
object configContext, XmlNode section)
{
XPathNavigator xPN;
if (section == null || (xPN = section.CreateNavigator()) == null )
return null;
// ---------------------------------------------------------
Type sectionType = Type.GetType((string)xPN.Evaluate
("string(#configType)"));
XmlSerializer xs = new XmlSerializer(sectionType);
return xs.Deserialize(new XmlNodeReader(section));
}
}

You can do this manually, by opening the document as an XDocument, finding the appropriate section and passing that to your configuration section handler.
XDocument configDoc = XDocument.Load( alternateConfigFile );
var section = configDoc.Descendants( "sectionName" ).First();
var obj = sectionHandler.Create( null, null, section );

Related

How can I change the value of a custom configSections variable?

I have a custom section in the configSections of my App.config file, how can I change the value of one of the variables of this section in code?
The section I would like to change is "serverConfiguration", and I want to change the value of "serverUrl":
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="serverConfiguration" type="someType" />
</configSections>
<serverConfiguration serverUrl="http://development/server/" />
</configuration>
I found this piece of code below from this previous question, App.Config change value.
It looks close to what I need, but I am not sure how to change it myself to use it for a custom section rather than the AppSettings. Will the code below work for what I am trying to do? How do I change the code below to allow me to pass this new String as serverUrl "http://staging/server/"? Thanks!
class Program
{
static void Main(string[] args)
{
UpdateSetting("lang", "Russian");
}
private static void UpdateSetting(string key, string value)
{
Configuration configuration = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
configuration.AppSettings.Settings[key].Value = value;
configuration.Save();
ConfigurationManager.RefreshSection("appSettings");
}
}
You have an option to load the config into XML, edit the node value and save it back. Give a try with this
var xmlDoc = new XmlDocument();
xmlDoc.Load(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile);
xmlDoc.SelectSingleNode("//serverConfiguration").Attributes["serverUrl"].Value = "http://staging/server/";
xmlDoc.Save(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile);
Probably, it is a good idea to refresh the Config sections after file is saved.
ConfigurationManager.RefreshSection

c# embed config file within exe

I have a console program 'A' that at a given point will run program 'B' and program 'C'. However I'm having an issue with the app.config associate with each of the program. Basically program A is just a wrapper class that calls different console application, It should not have any app.config but it should use the current running program's app config. So in theory there should be only 2 app.config one for Program B and another for program C.
So if we run Program A and program B gets executed, it should use program B's app.config to get the information and after when program C gets executed it should use Program C's app.config.
Is there a way to do this? Currently i'm doing this:
var value = ConfigurationManager.OpenExeConfiguration(Assembly.GetExecutingAssembly().Location).AppSettings.Settings["ProgramBKey"].Value;
It does not seem to work. I checked the debug on Assembly.GetExecutingAssembly().Location it's variable is the \bin\Debug\ProgramB.exe and 'ConfigurationManager.OpenExeConfiguration(Assembly.GetExecutingAssembly().Location).AppSettings' has setting with Count=0 when there are key values as seen below.
sample code Program A:
static void Main(string[] args)
{
if(caseB)
B.Program.Main(args)
else if(caseC)
C.Program.Main(args)
}
sample app.config for Program B:
<?xml version="1.0"?>
<configuration>
<appSettings>
<add key="ProgramBKey" value="Works" />
</appSettings>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0" />
</startup>
</configuration>
Edit: the following answer pertains to this question from the original post, "Is it possible to compile the app.config for B and C within the exe of the program."
You can use the "Embedded Resource" feature. Here's a small example of using an XML file that's been included as an embedded resource:
public static class Config
{
static Config()
{
var doc = new XmlDocument();
using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Fully.Qualified.Name.Config.xml"))
{
if (stream == null)
{
throw new EndOfStreamException("Failed to read Fully.Qualified.Name.Config.xml from the assembly's embedded resources.");
}
using (var reader = new StreamReader(stream))
{
doc.LoadXml(reader.ReadToEnd());
}
}
XmlElement aValue = null;
XmlElement anotherValue = null;
var config = doc["config"];
if (config != null)
{
aValue = config["a-value"];
anotherValue = config["another-value"];
}
if (aValue == null || anotheValue == null)
{
throw new XmlException("Failed to parse Config.xml XmlDocument.");
}
AValueProperty = aValue.InnerText;
AnotherValueProperty = anotherValue.InnerText;
}
}
You can have multiple application using the same config file. That way when you switch applications, they can both find their own parts of the config file.
The way I usually do it is... first let each application "do its own thing", then copy the relevant sections of config file A into config file B.
It will look like this:
<configSections>
<sectionGroup>
<sectionGroup name="applicationSettings"...A>
<sectionGroup name="userSettings"...A>
<sectionGroup name="applicationSettings"...B>
<sectionGroup name="userSettings"...B>
<applicationSettings>
<A.Properties.Settings>
<B.Properties.Settings>
<userSettings>
<A.Properties.Settings>
<B.Properties.Settings>
For me, the whole thing sounds like a "design issue". Why should you want to open Programm B with the config of Programm A?
Are you the author of all those Programms? You might want to use a dll-file instead. This will save you the trouble as all code runs with the config of the Programm running.
Here how you can do it:
Make App.config as "Embedded Resource" in the properties/build action
Copy to output Directory : Do not copy
Add this code to Proram.cs Main
if (!File.Exists(Application.ExecutablePath + ".config"))
{
File.WriteAllBytes(Application.ExecutablePath + ".config", ResourceReadAllBytes("App.config"));
Process.Start(Application.ExecutablePath);
return;
}
Here are the needed functions:
public static Stream GetResourceStream(string resName)
{
var currentAssembly = Assembly.GetExecutingAssembly();
return currentAssembly.GetManifestResourceStream(currentAssembly.GetName().Name + "." + resName);
}
public static byte[] ResourceReadAllBytes(string resourceName)
{
var file = GetResourceStream(resourceName);
byte[] all;
using (var reader = new BinaryReader(file))
{
all = reader.ReadBytes((int)file.Length);
}
file.Dispose();
return all;
}

How to read AppSettings from app.config in WinForms

I usually use a text file as a config. But this time I would like to utilize app.config to associate a file name (key) with a name (value) and make the names available in combo box
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="Scenario1.doc" value="Hybrid1"/>
<add key="Scenario2.doc" value="Hybrid2"/>
<add key="Scenario3.doc" value="Hybrid3"/>
</appSettings>
</configuration>
will this work? how to retrieve the data ?
Straight from the docs:
using System.Configuration;
// Get the AppSettings section.
// This function uses the AppSettings property
// to read the appSettings configuration
// section.
public static void ReadAppSettings()
{
try
{
// Get the AppSettings section.
NameValueCollection appSettings = ConfigurationManager.AppSettings;
// Get the AppSettings section elements.
Console.WriteLine();
Console.WriteLine("Using AppSettings property.");
Console.WriteLine("Application settings:");
if (appSettings.Count == 0)
{
Console.WriteLine("[ReadAppSettings: {0}]",
"AppSettings is empty Use GetSection command first.");
}
for (int i = 0; i < appSettings.Count; i++)
{
Console.WriteLine("#{0} Key: {1} Value: {2}",i, appSettings.GetKey(i), appSettings[i]);
}
}
catch (ConfigurationErrorsException e)
{
Console.WriteLine("[ReadAppSettings: {0}]", e.ToString());
}
}
So, if you want to access the setting Scenario1.doc, you would do this:
var value = ConfigurationManager.AppSettings["Scenario1.doc"];
Edit:
As Gabriel GM said in the comments, you will have to add a reference to System.Configuration.
app settings in app.config are to store application/environment specific settings not to store data which binds to UI.
If you cant avoid storing in config because of weird business requests I would rather stick to one single setting
<add key="FileDropDown" value="File1-Value|File2-Value" />
and write C# code to get this setting ConfigurationManager.AppSettings["FileDropDown"] and do some string Splits ('|') and ('-') to create kvp collection and bind it to UI.

How to get the values of a ConfigurationSection of type NameValueSectionHandler

I'm working with C#, Framework 3.5 (VS 2008).
I'm using the ConfigurationManager to load a config (not the default app.config file) into a Configuration object.
Using the Configuration class, I was able to get a ConfigurationSection, but I could not find a way to get the values of that section.
In the config, the ConfigurationSection is of type System.Configuration.NameValueSectionHandler.
For what it worth, when I used the method GetSection of the ConfigurationManager (works only when it was on my default app.config file), I received an object type, that I could cast into collection of pairs of key-value, and I just received the value like a Dictionary. I could not do such cast when I received ConfigurationSection class from the Configuration class however.
EDIT:
Example of the config file:
<configuration>
<configSections>
<section name="MyParams"
type="System.Configuration.NameValueSectionHandler" />
</configSections>
<MyParams>
<add key="FirstParam" value="One"/>
<add key="SecondParam" value="Two"/>
</MyParams>
</configuration>
Example of the way i was able to use it when it was on app.config (the "GetSection" method is for the default app.config only):
NameValueCollection myParamsCollection =
(NameValueCollection)ConfigurationManager.GetSection("MyParams");
Console.WriteLine(myParamsCollection["FirstParam"]);
Console.WriteLine(myParamsCollection["SecondParam"]);
Suffered from exact issue. Problem was because of NameValueSectionHandler in .config file. You should use AppSettingsSection instead:
<configuration>
<configSections>
<section name="DEV" type="System.Configuration.AppSettingsSection" />
<section name="TEST" type="System.Configuration.AppSettingsSection" />
</configSections>
<TEST>
<add key="key" value="value1" />
</TEST>
<DEV>
<add key="key" value="value2" />
</DEV>
</configuration>
then in C# code:
AppSettingsSection section = (AppSettingsSection)ConfigurationManager.GetSection("TEST");
btw NameValueSectionHandler is not supported any more in 2.0.
Here's a good post that shows how to do it.
If you want to read the values from a file other than the app.config, you need to load it into the ConfigurationManager.
Try this method: ConfigurationManager.OpenMappedExeConfiguration()
There's an example of how to use it in the MSDN article.
Try using an AppSettingsSection instead of a NameValueCollection. Something like this:
var section = (AppSettingsSection)config.GetSection(sectionName);
string results = section.Settings[key].Value;
Source:
http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/d5079420-40cb-4255-9b3b-f9a41a1f7ad2/
The only way I can get this to work is to manually instantiate the section handler type, pass the raw XML to it, and cast the resulting object.
Seems pretty inefficient, but there you go.
I wrote an extension method to encapsulate this:
public static class ConfigurationSectionExtensions
{
public static T GetAs<T>(this ConfigurationSection section)
{
var sectionInformation = section.SectionInformation;
var sectionHandlerType = Type.GetType(sectionInformation.Type);
if (sectionHandlerType == null)
{
throw new InvalidOperationException(string.Format("Unable to find section handler type '{0}'.", sectionInformation.Type));
}
IConfigurationSectionHandler sectionHandler;
try
{
sectionHandler = (IConfigurationSectionHandler)Activator.CreateInstance(sectionHandlerType);
}
catch (InvalidCastException ex)
{
throw new InvalidOperationException(string.Format("Section handler type '{0}' does not implement IConfigurationSectionHandler.", sectionInformation.Type), ex);
}
var rawXml = sectionInformation.GetRawXml();
if (rawXml == null)
{
return default(T);
}
var xmlDocument = new XmlDocument();
xmlDocument.LoadXml(rawXml);
return (T)sectionHandler.Create(null, null, xmlDocument.DocumentElement);
}
}
The way you would call it in your example is:
var map = new ExeConfigurationFileMap
{
ExeConfigFilename = #"c:\\foo.config"
};
var configuration = ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationUserLevel.None);
var myParamsSection = configuration.GetSection("MyParams");
var myParamsCollection = myParamsSection.GetAs<NameValueCollection>();
This is an old question, but I use the following class to do the job. It's based on Scott Dorman's blog:
public class NameValueCollectionConfigurationSection : ConfigurationSection
{
private const string COLLECTION_PROP_NAME = "";
public IEnumerable<KeyValuePair<string, string>> GetNameValueItems()
{
foreach ( string key in this.ConfigurationCollection.AllKeys )
{
NameValueConfigurationElement confElement = this.ConfigurationCollection[key];
yield return new KeyValuePair<string, string>
(confElement.Name, confElement.Value);
}
}
[ConfigurationProperty(COLLECTION_PROP_NAME, IsDefaultCollection = true)]
protected NameValueConfigurationCollection ConfCollection
{
get
{
return (NameValueConfigurationCollection) base[COLLECTION_PROP_NAME];
}
}
The usage is straightforward:
Configuration configuration = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
NameValueCollectionConfigurationSection config =
(NameValueCollectionConfigurationSection) configuration.GetSection("MyParams");
NameValueCollection myParamsCollection = new NameValueCollection();
config.GetNameValueItems().ToList().ForEach(kvp => myParamsCollection.Add(kvp));
Here are some examples from this blog mentioned earlier:
<configuration>
<Database>
<add key="ConnectionString" value="data source=.;initial catalog=NorthWind;integrated security=SSPI"/>
</Database>
</configuration>
get values:
NameValueCollection db = (NameValueCollection)ConfigurationSettings.GetConfig("Database");
labelConnection2.Text = db["ConnectionString"];
-
Another example:
<Locations
ImportDirectory="C:\Import\Inbox"
ProcessedDirectory ="C:\Import\Processed"
RejectedDirectory ="C:\Import\Rejected"
/>
get value:
Hashtable loc = (Hashtable)ConfigurationSettings.GetConfig("Locations");
labelImport2.Text = loc["ImportDirectory"].ToString();
labelProcessed2.Text = loc["ProcessedDirectory"].ToString();
Try this;
Credit: https://www.limilabs.com/blog/read-system-net-mailsettings-smtp-settings-web-config
SmtpSection section = (SmtpSection)ConfigurationManager.GetSection("system.net/mailSettings/smtp");
string from = section.From;
string host = section.Network.Host;
int port = section.Network.Port;
bool enableSsl = section.Network.EnableSsl;
string user = section.Network.UserName;
string password = section.Network.Password;
This works like a charm
dynamic configSection = ConfigurationManager.GetSection("MyParams");
var theValue = configSection["FirstParam"];

How to read nested configuration element from the web.config?

I need to read configuration elements from the web.config.
Let this be my web.config.
<family>
<parents>
<child name="Hello"/>
<child name="World"/>
</parents>
<parents>
<child name="Hello1"/>
<child name="World2"/>
</parents>
</family>
So I have something like this, I need to read this into a collection.
How can i do this????
In general, you can store simple application settings and connection string in web.config (or app.config), but anything more complex, like an object graph or XML (as in your case) and you should consider a different method.
These may be helpful:
How do I store an XML value in my .NET App.Config file
(it suggests encoding the XML in an app setting)
However it would be better to have a separate XML data file and convert it to an object graph using Linq-To-XML (see reference) or XPath and the XmlDocument and related classes.
Edit: see the other answer, which does allow XML in the config file. That's a more direct answer to your exact questions but I will leave this here for reference. On the whole it looks like your data is not configuration data (more like runtime / user data) and does not belong in a .config file: so I would recommend storing it in a separate XML file, and having a config file entry pointing to the filename of the separate XML file.
Hope that helps!
You need to define your own custom configuration section, which will allow you to read the nested configuration element properly. BTW, this is the same method that all the others use, for instance the Enterprise Library components, NHibernate, etc.
The steps you need to take are very straightforward, and a tutorial is provided here:
http://msdn.microsoft.com/en-us/library/2tw134k3.aspx
You need to use the ConfigurationElementCollection Class.
See this sample on the MSDN
public struct Child
{
public string name;
public Child(string name)
{
this.name = name;
}
}
public class Parent
{
public List<Child> childs = new List<Child>();
public static List<Parent> ReadParentsFromXml(string fileName)
{
List<Parent> parents = new List<Parent>();
System.Xml.XmlTextReader doc = new System.Xml.XmlTextReader(fileName);
Parent element = new Parent();
while (doc.Read())
{
switch (doc.Name)
{
case "parents":
if (doc.NodeType == System.Xml.XmlNodeType.EndElement)
{
parents.Add(element);
element = new Parent();
}
break;
case "child":
if(doc.NodeType != System.Xml.XmlNodeType.EndElement)
element.childs.Add(new Child(doc.GetAttribute(0)));
break;
}
}
return parents;
}
}

Categories

Resources