Is it possible to modify configuration ConnectionStrings at runtime? - c#

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.

Related

Remove domain name from machine name

Is there a safe way to remove the domain name from a machine name in C#?
Example:
MYMACHINE.mydomain.com
should be resolved to
MYMACHINE
Naive String Approach:
string fullName = "MYMACHINE.mydomain.com";
string resolvedMachineName = fullName.Split('.')[0];
If you want the 'basic' understanding of machine name, then use :
Environment.MachineName
It provides the machine name without the domain (traditionally the COMPUTERNAME variable at a command prompt). See: http://msdn.microsoft.com/en-us/library/system.environment.machinename.aspx
Edit #1
#Antineutrino - as I re-read my answer I realized we don't have enough information to give you a correct answer. When you say 'machine name', however, it is not clear if you want to know how to parse the string you have, or the proper way to retrieve what you want. If you simply want to know how to parse a string, the other answers are correct. On the other hand...
Do you want the NetBios name or Cluster Name? Then Environment.MachineName
Do you want the underlying name of a the machine running a cluster? You'll need to dig into the WMI classes.
Do you want the most specific name from all DNS strings that resolve to the current machine? i.e. web1 from web1.mysite.com? You'll need to resolve all the IP's bound to the machine.
If you go back and edit/revise your question with more details -- you'll get better/more-specific answers.
Edit #2
If I understand correctly, your scenario is as follows: You have similar code that runs on both client and server machines in a desktop environment. In all machines registries you've saved a DNS name for the server (server.myapp.com). Now in the code you want to determine if a machine is the server or not.
What You Asked For
string serverName = Registry.GetValue(REG_KEY, REG_VALUE, "key missing");
string currentMachine = Environment.MachineName;
if (currentMachine.ToLower() == serverName.ToLower().Split('.')[0] ) {
//do something fancy
};
Another Idea
Instead of shrinking the server name, you can grow the machine name.
string serverName = Registry.GetValue(REG_KEY, REG_VALUE, "key missing");
string currentMachine = Environment.MachineName +
Environment.GetEnvironmentVariable("USERDNSDOMAIN");
if (currentMachine.ToLower() == serverName.ToLower() ) {
//do something fancy
};
What you may want
Distributing registry keys seems a bit cumbersome. If it were me I would probably do one/some/many of the following:
Create different build configurations and use pre-processor directives to change the code. Something very roughly like: #if(RELEASE_SERVER) { /*do server code*/} #if(RELEASE_CLIENT) { /*do server code*/}
Use app.config ---that's what it's there to store configuration data. No need to pollute the registry. app.config can be transformed on build/deployment, or simply modified manually on the one server.
Store the majority of the configuration data in a central location. This way the app only need to know were to retrieve it's configuration from (even if that's itself).

How to correctly iterate application settings in C# using reflection

I am trying to iterate through application properties in C# using reflection (.NET 3.5 using VS 2010). The code "works" in that it successfully gets properties. However, it always gets the property values that were defined at design time and does not see the current values in myapp.exe.config. Properties that I access directly by name do reflect what is in the .config file. Here is the reflection-based code which only sees design-time properties:
List<StringDictionary> dictList = new List<StringDictionary>();
StringCollection bogus = new StringCollection();
foreach (PropertyInfo info in Properties.Settings.Default.GetType().GetProperties())
{
if (!("logLevel".Equals(info.Name) || "eventURL".Equals(info.Name)))
{
if (bogus.GetType().IsAssignableFrom(info.PropertyType))
{
StringCollection rawConfig = (StringCollection)info.GetValue(Properties.Settings.Default, null);
// do something
}
}
}
This code does pick up the current values in myapp.exe.config.
String logLevelStr = Properties.Settings.Default.logLevel
What am I doing wrong in my reflection code that causes me to pull only the properties defined at design time and not what is currently in myapp.exe.config?
To get the current value you need to use something like this which looks at Default.PropertyValues instead of Default.Properties
foreach (SettingsPropertyValue property in Properties.Settings.Default.PropertyValues)
{
Debug.WriteLine(string.Format("Property {0}'s value is {1}",property.Name,property.PropertyValue));
}
// note: the above may not work in some multi-form app, even if the applicaton prefix is prepended in front of Properties esp for visual studio 2010 compiled app with .net frame work 4
I think that there is a fundamental misunderstanding here. Settings can be one of two types- Application settings and User settings.
Application settings are intended to be written only at design time. As Henk points out it is possible to edit them after deployment if you are admin, but that isn't really the intent. Also, it should be noted that while Application settings are stored in the .config file, they are only read once and then cached in memory. That's why you don't see the new values when you edit the file.
User settings can be overwritten at run time by application code and saved, but they are saved at a user scope, so a different user running the same application can have different values. The intention there was things like user preferences. There is a drop down in the settings designer grid to switch between Application and User scope for each Setting.
Either way, you shouldn't be accessing them via reflection.
There must be some kind of misunderstanding here.
If you want to read the configurations from myapp.exe.config you should use ConfigurationManager. This class allows you to access AppSettings and ConnectionString directly through static properties or read custom sections by the GetSection method.
Beside, application configurations are meant to be design-time only. You shouldn't alter myapp.exe.config at runtime. Never. This file must be the same for each execution of your application.
Beside, what is Properties.Settings.Default.logLevel ???
Consider:
foreach (SettingsProperty sp in Settings.Default.Properties)
{
Console.WriteLine(sp.Name + "=" + Settings.Default.Properties.Default[sp.Name].ToString());
}

c# application and configuration settings

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.

C# AppSettingsReader: "reread" values into the AppSettingsReader (runtime)?

In my application, I need to change some value ("Environment") in appSetting of app.config at runtime.
I use AppSettingsReader
private static AppSettingsReader _settingReader;
public static AppSettingsReader SettingReader
{
get
{
if (_settingReader == null)
{
_settingReader = new AppSettingsReader();
}
return _settingReader;
}
}
Then at some stage I do this
config.AppSettings.Settings[AppSettingString.Environment.ToString()].Value = newEnvironment.ToString();
config.Save(ConfigurationSaveMode.Modified);
ConfigurationManager.RefreshSection("appSettings");
However, next time I try to read "Environment" like this
string environment = (string)SettingReader.GetValue(AppSettingString.Environment.ToString(), typeof(System.String));
I end up with the old value of Environment.
I noticed that I can fix this by doing
_settingReader = new AppSettingsReader();
before I read "Environment".
But I think creating a new instance is not the proper approach.
Maybe there is a way to let my SettingReader know, that the values have changed to use the same instance of it, but with refreshed values?
(Not a project-breaking question obviously, more of an educational one)
AppSettingsReader doesn't seem to have any method to reload from disk. It just derives from Object. Creating a new instance seems to be the only thing that would work... I may be wrong, but AppSettings are supposed to be read-only values for your app. More like configuration parameters for your application that can be tweaked before startup.
For read-write application settings, I think the Settings mechanism with IDE support (System.Configuration.ApplicationSettingsBase) would be the preferred approach. This has Save and Reload methods. The designer-gen class makes the code far more readable too..
Double click on the Properties Node under your Project in Solution Explorer. Find the Settings tab.
Instead of
sEnvironment = (string)SettingReader.GetValue(AppSettingString.Environment.ToString(), typeof(System.String));
you could have typed properties like
sEnvironment = Properties.Settings.Default.Environment;
The designer generated class exposes a synchronized singleton instance via the Default property.. which should mean you don't need to reload.. you'd always get the latest value within the application.
Upgrade is the function you are looking for
System.Configuration.ApplicationSettingsBase.Upgrade
System.Configuration.ApplicationSettingsBase.Reload

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