c# application and configuration settings - c#

I never used Settings class before but I found some articles on CodeProject which I'm currently reading (for example http://www.codeproject.com/KB/cs/PropertiesSettings.aspx) but so far I didn't see how to save string array for getting it after application is started next time.
For example my application has several FileSystemWatcher instances, with each instance several other directories are connected (for example one FSW instance is monitoring one directory for a change and when it happens it copies some file to several other directories), so I would have one string array with watched paths representing FSW instances, and string array for each of those paths, representing directories that are affected.
My question is, what should I use (Settings class or something else), and how should I use that for storing application configuration that is variable number of string arrays? Emphasize is on something I could use very soon as I don't have too much time to make custom class (but would have to if I cannot find solution) or dig into some obscure hacks.
Any tutorial link, code snippet would be very helpful.
Thanks.

Why not use a config file instead? I had a set of FileSystemWatchers like yours and just added a set of paths using custom config sections. Thought that requires you rolling a class to extending the Configuration classes, but I think that you can't beat that approach.
Though if you want a clear easy hack and don't want to get bothered with the Custom Config Sections/Elements/Collections. Just use a quick and easy AppSettings hack. XD
<appSettings>
<add key="ConnectionInfo0" value="server=(local);database=Northwind;Integrated Security=SSPI" />
<add key="ConnectionInfo1" value="server=(local);database=Northwind;Integrated Security=SSPI" />
<add key="ConnectionInfo2" value="server=(local);database=Northwind;Integrated Security=SSPI" />
</appSettings>
Getting it from code.
const string CONNECTIONSTRING="";
int i = 0;
while(ConfigurationSettings.AppSettings["ConnectionInfo"] + i != null)
{
// do stuff with connection info here
// use
ConfigurationSettings.AppSettings["ConnectionInfo" + i];
// to get data.
++i;
}
Tad ugly, I know. Custom Config works best.

Related

Make C# Console App recognize manually edited changes in .exe.config file

I successfully wrote a C# console application that collects .XML or .ZIP files from different locations and copies them to a single destination. Those locations are stored in the settings as User-scoped settings (for instance, "Folder01 C:\Data1\" and "Folder02:\Data2"). As you probably already know, building the project generates a [ProjectName].exe.config file in the /bin/Debug folder.
Now, the issue is that I cannot get the console app to recognize any changes that I made in the .exe.config file. Say, I want to add "Folder 03 C:\Data3\" to the settings or edit "Folder02" path to "C:\DataEdited\", the console app will still loop through the settings as initially set up in the code ("Folder01 C:\Data1\" and "Folder02 C:\Data2\").
I also noticed that the console app still runs even after deleting the .exe.config file, as if it does not rely on the file at all. I would like to make changes without having to open the project in Visual Studio and edit locally.
Is that possible?
EDIT:
In response to the request of the Settings that I created and code for getting folder paths, see image image below:
Here is the code:
string[] acceptedExtensions = new[] { ".xml", ".zip" };
string[] settingsToSkip = new[] { "RootFolder", "ArchiveFolder" };
// Collect data
var filteredSettings = Properties.Settings.Default.Properties
.Cast<SettingsProperty>()
.Where(p => !settingsToSkip.Contains(p.Name));
filteredSettings collects Folder01, Folder02, Folder03 and Folder04 and I loop through those to find files with acceptedExtensions.
I believe you expected this feature of c# ConfigurationManager. You might have deleted *.exe.config after your application is started. *.exe.config is not locked or needed after app starts unless you call configurationmanager.refreshsection() method.
Reloading configuration without restarting application using ConfigurationManager.RefreshSection
https://learn.microsoft.com/en-us/dotnet/api/system.configuration.configurationmanager.refreshsection?view=netframework-4.7.2
Thumbs up and mark it if it helped you!
How I have done it in my production code is that I have added to my App.config with my Visual Studios and made it have the format of:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="AConnection" value="127.0.0.1"/>
<add key="Folder01" value="L:\Path\To\A\Thing"/>
<add key="Folder02" value="L:\Path\To\ASecond\Thing"/>
<add key="Folder03" value="L:\Path\To\AThird\Thing"/>
<add key="Folder04" value="L:\Path\To\AFourth\Thing"/>
</appSettings>
</configuration>
Where the <add key="" value="">s are whatever you wish to name them and the values is the path to the correct file.
Assigning:
You can then assign these to variables:
string conStr = ConfiurationManager.AppSettings["AConnection"];
string strFolder1 = ConfigurationManager.AppSettings["Folder01"];
string strFolder2 = ConfigurationManager.AppSettings["Folder02"];
string strFolder3 = ConfigurationManager.AppSettings["Folder03"];
string strFolder4 = ConfigurationManager.AppSettings["Folder04"];

How can i use a value from my appSettings Configuration file, and use it in a constructor method as a decimal value?

So I am in the process of working through a project. Everything was working fine until i put some more control into my form, now it seems as though my tax rates I had set in my configuration file, won't be accepted by my constructor method at run time.
compiles fine, then when i open the form up from the MDi frame, I get this ArguementNullException Handled error and a troubleshoot window pops up.
After doing some researching i am thinking that when i pull my keys from the config file, i am not properly parsing them so that my constructor will take them at run-time.
here's the code that i wrote, im not posting my entire solution, that would be insane at this point.
// sales tax inititalization
decimal gstTax = Decimal.Parse(ConfigurationManager.AppSettings.Get("GoodsAndServicesTaxRate."));
decimal pstTax = Decimal.Parse(ConfigurationManager.AppSettings.Get("ProvincialSalesTaxRate"));
decimal salesTaxRate = gstTax + pstTax;
SalesQuote quote = new SalesQuote(Decimal.Parse(txtSalePrice.Text),
(Decimal.Parse(txtTradeIn.Text)),
salesTaxRate,
(Accessories)optA,
(ExteriorFinish)optB);
And here is the XML file with the key values.
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="ApplicationName:" value="RRC Automotive Group"/>
<add key="GoodsAndServicesTaxRate" value=".05"/>
<add key="ProvincialSalesTaxRate" value=".08"/>
</appSettings>
</configuration>
And yes there is an assembly reference.
the signature for SalesQuote(decimal,decimal,decimal,Enum,Enum) if that wasn't already obvious.
i want to thank you fellow nerds for any advice... to the best of my knowledge this was all working fine so im not sure what is trumping me up here.
any help helps!
thanks
You have a stray . here
ConfigurationManager.AppSettings.Get("GoodsAndServicesTaxRat‌​e.")
which does not match up with what is in the config file
<add key="GoodsAndServicesTaxRate" value="0.05"/>
This would result in the ConfigurationManager.AppSettings.Get method return null which would cause the parsing Decimal.Parse to fail.

Is there a difference between loading a value from the app.config to loading it from an object?

This is my app.config:
<appSettings>
<add key="PreRootFolder" value="D:\" />
<add key="RootFolder" value="webSite" />
<add key="Folder" value="folder_a" />
</appSettings>
Since I prefer to build the path in the application rather than have many keys for each part of the path... (hard to maintain) so I build the path this way:
string prePath = ConfigurationManager.AppSettings["PreRootFolder"];
string rootFolder = ConfigurationManager.AppSettings["RootFolder"];
string folder= ConfigurationManager.AppSettings["Folder"];
// global param (actually accessed by ((MainFormName)mainParent)).g_fullOriginalRoot
string g_fullOriginalRoot = prePath + "\\" + rootFolder + "\\" + fodler;
I do this in the application MDI parent form (so it never die)
I did this because I found out that I call those keys many times and now I can get the data from the parent.
I wonder, if my solution is correct?
Are app.config parameters loaded as globals?
It's reasonable to use a globally accessible (static or singleton) object that memoises the settings in this case.
Many of the disadvantages of globals are less serious when communication is one-way (read-only settings or write-only logging being classic cases). Also, since the settings are inherently global, it matches what is being modelled.
I'd prefer to have this done in its own class than in the MDI form though. The form should model things that have to do with the form, other objects should model other concerns.
Another thing to consider is if it is valid for this value to change while the app is running. Do you want to allow the user to change the value and have the application pick up the new value, or is it sufficient for the application to load the value at startup and then assume the value never changes during the running of the application.
This consideration is, of course, more important for a long-running application than for short-sessioned applications.

Is it possible to modify configuration ConnectionStrings at runtime?

Is it possible to modify the connectionstrings defined in the app.config/web.config at runtime? I want to use different configfiles depending on the machine the app/site is run on (only for debugging purposes, of course. We'll use the regular config files when deployed).
I can write AppSettings, but not ConnectionStrings (AFAIK). Or can I?
Yes it's possible, but AFAIK only via Reflection. The following code should do what you need (read below for usage):
public static string SetConnectionString(Type assemblyMember,
Type settingsClass,
string newConnectionString,
string connectionStringKey)
{
Type typSettings = Type.GetType(Assembly.CreateQualifiedName(assemblyMember.Assembly.FullName, settingsClass.FullName));
if (typSettings == null)
{
return null;
}
PropertyInfo prpDefault = typSettings.GetProperty("Default", BindingFlags.Static | BindingFlags.Public);
if (prpDefault == null)
{
return null;
}
object objSettings = prpDefault.GetValue(null, null);
if (objSettings == null)
{
return null;
}
// the default property, this[], is actually named Item
PropertyInfo prpItem = objSettings.GetType().GetProperty("Item", BindingFlags.Instance | BindingFlags.Public);
if (prpItem == null)
{
return null;
}
object[] indexerName = { connectionStringKey };
string oldConnectionString = (string)prpItem.GetValue(objSettings, indexerName);
prpItem.SetValue(objSettings, newConnectionString, indexerName);
return oldConnectionString;
}
assemblyMember is the calling type
settingsClass is the type of your settings class
newConnectionString is the full string to set
connectionStringKey is the name of the connection string that you defined in your app's settings
You should call this method as soon as possible after your app has started, preferably in the Main() method.
I tried this once in my project for debugging purpose but could not do it, the problem (I guess, correct me if I am wrong) is on app start up the app.config gets loaded into the memory, any changes to app.config while the app is running do not get reflected.
To overcome this here's what I did, define all the connectionstrings in the app.config and then call the ones that you want when your program is running like this.
for example lets assume you defined your connectionstrings in the app.config as follows.
<connectionStrings>
<add name="YourNameSpace.Properties.Settings.ConnectionString_1"
connectionString="Provider=Microsoft.Jet.OLEDB.4.0;Data
Source=|DataDirectory|\file.mdb"
providerName="System.Data.OleDb"/>
<add name="YourNameSpace.Properties.Settings.ConnectionString_2"
connectionString="Provider=Microsoft.Jet.OLEDB.4.0;Data
Source=|DataDirectory|\file.mdb"
providerName="System.Data.OleDb"/>
</connectionStrings>
define as many as you want (you are debugging right :-) )
then to call those connection settings in your code do something like this:
YourNameSpace.Properties.Settings foo = new YourNameSapce.Properties.Settings();
foo.ConnectionString_1;
HTH
Best Regards
#nand
P.S: This reply is specific to C#.
You can't really edit the config file of the running process.
One option (with pros and cons) is to use config data in the machine.config or the master web.config (for "site", you use the "location" nodes) - not an option to rush into, though.
A better way to handle this is to swap the config file as part of your build/deploy process, ideally automated. That way, everything is self-contained, and you can "robocopy" to a vanilla server and have it work.
Re your "per developer" point; I found that the easiest way to do this was to standardise the config, and tweak the machines. For example, we run a local web-server on a VM; rather than code against each machine, we standardise on "localserver" (to mirror "localhost"), and add a local DNS record to each machine that the developer can control. Note that this requires fixed IP addresses (or maybe a DHCP reservation) to prevent it changing over time!
Ditto databases; local servers can use "."; remote servers can be aliased on the machine, so "devserver" points to whatever the user wants.
Just a thought...
You could run xmlpoke in a NAnt script when installing the website.
E.g.
Deploy the NAnt with the website.
Create a go.bat that calls NAnt which installs the site and does xmlpoke to modify the web.config with settings based on the server name or some other parameter.

Changing values in Web.config with a Batch file or in .NET code

I have a web.config file on my computer.
There are alot of things i need to change and add in the file.
(I am actually working with my SharePoint web.config file)
Can i do this with a Batch file, if so how would i do it.
Or how would i do it using VB.NET or C# code?
Any ideas guys?
Edit: i need to create a program to alter a web.config of lets say i web.config laying on my deskop and not the actual web.config of my project
Regards
Etienne
You can modify it from C# code, for example:
Configuration configuration = WebConfigurationManager.OpenWebConfiguration("~");
AppSettingsSection appSettingsSection = (AppSettingsSection)configuration.GetSection("appSettings");
if (appSettingsSection != null)
{
appSettingsSection.Settings["foo"].Value = "bar";
config.Save();
}
where foo is the key and bar the value of the key to set, obviously. To remove a value, use Settings.Remove(key);
See the msdn documentation for more information about the OpenWebConfiguration method and more.
The context in which you want to change the file really affects how you should do it. If you're looking at performing changes relatively frequently, but in an administrative domain, then some sort of command-line tool makes sense, and in this case I'd agree with JaredPar that PowerShell would be a valuable tool.
If, on the other hand, you find yourself in a situation where you need to modify the web.config in a more programmatic environment (e.g., as part of a setup program), then using programmatic technologies might make more sense. I recently had to do such a thing and Linq to Xml proved very convenient.
For example, to open a document "C:\foo\bar.xml" you could do something like (untested, no convenient build environment at the moment):
XDocument config = XDocument.Load(#"C:\foo\bar.xml");
You could then carry on in the usual fashion with the API. Note that this may be overkill if you're doing an administrative task as opposed to a programmatic task-- there are big, long-term advantages to learning a tool like PowerShell.
Finally, if you're modifying the web.config from within the program that the web.config is being used for, and you aren't doing anything too fancy or dynamic, then using the built-in Settings or ConfigurationManager may be the way to go.
Your best bet might to change it using a MSBuild Script and the MsBuild Community Tasks XML Mass update task
I would personally recommend using PowerShell. This is the next gen command line from Microsoft and it's sits right on top of .Net. It was built to do items like batch edits across large sets of files.
For a change in web.config in a SharePoint environment you have classes specially developed for this task. You only have to search for SPWebConfigModification class.
To load an arbitrary .NET config file
string configLocation = #"C:\myconfigFile.Config";
ExeConfigurationFileMap configFileMap = new ExeConfigurationFileMap();
configFileName = configLocation;
configFileMap.ExeConfigFilename = configFileName;
Configuration configuration= ConfigurationManager.OpenMappedExeConfiguration(configFileMap, ConfigurationUserLevel.None);
Then use Razzie's code to alter the actual config setting
AppSettingsSection appSettingsSection = (AppSettingsSection)configuration.GetSection("appSettings");
if (appSettingsSection != null)
{
appSettingsSection.Settings["foo"].Value = "bar";
configuration.Save();
}
This is what i needed to do.......thanks for all the help!!!
// Read in Xml-file
XmlDocument doc = new XmlDocument();
doc.Load("C:/Web.config");
//SaveControl tag..........................................................
XmlNode n = doc.SelectSingleNode("/configuration/SharePoint/SafeControls");
XmlElement elemWeb = doc.CreateElement("SafeControl");
elemWeb.SetAttribute("Assembly", "SamrasWebOption4");
elemWeb.SetAttribute("Namespace", "SamrasWebOption4");
elemWeb.SetAttribute("TypeName", "*");
elemWeb.SetAttribute("Safe", "True");
XmlElement elemSmartPart = doc.CreateElement("SafeControl");
elemSmartPart.SetAttribute("Assembly", "Machine_Totals");
elemSmartPart.SetAttribute("Namespace", "Machine_Totals");
elemSmartPart.SetAttribute("TypeName", "*");
elemSmartPart.SetAttribute("Safe", "True");
//Appending the Nodes......................................................
n.AppendChild(elemWeb);
n.AppendChild(elemSmartPart);
//Saving the document......................................................
doc.Save("C:/Web.config");

Categories