RadicalSoftwareSolutions
asked on
How to change connectionstrings that are readonly in app.config
OK Here goes.
I have developped a VB.NET 2005 application which includes the fancy new "drag 'n drop" DataSet tool. When you first create a new dataset VB writes the connection string into the app.config file (visible through myproject.settings) as an "application scope" value. Application scope values are read only at runtime which is all well and good while I'm developping and my development server is available but completely useless when it comes to releasing the application to clients.
How can you change these connection strings at run time??
I've been searching now for ages to a proper answer to this question and a lot of time the answer is "you shouldn't do that....microsoft intended these to be fixed etc,etc" which is avoiding the issue. These values need to be changed!!!
One client has a need to set these values on a "per user" basis as the databases are replicted out to branch offices and the connectionstring needs to point at the replicated database.
The answer ould seem to be to change the values in the <programname>.exe.config file that is created in the user's directory and apply these in the settings.SettingsLoaded function but how exactly can this be done?
Simply assigning these values to a User Scope won't work as VB will continue to look at the application scope value for any updates effected on the dataset
I have seen Q_20954883 but it doesn't appear to address changing the connectionstring values (I accept that the app will need to be restarted the first time these are changed) as they don't appear to follow the normal XML structure rules.
Here's part of the app.config file...(values have been changed to protect the innocent)
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<sectionGroup name="userSettings" type="System.Configuration .UserSetti ngsGroup, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=123456789" >
<section name="System2.My.MySetting s" type="System.Configuration .ClientSet tingsSecti on, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=123456789" allowExeDefinition="Machin eToLocalUs er" requirePermission="false" />
</sectionGroup>
</configSections>
<connectionStrings>
<add name="Systemx.My.MySetting s.Connecti onString"
connectionString="Data Source=SQLSERVER;Initial Catalog=SystemXDB;Persist Security Info=True;User ID=xy;Password=abc"
providerName="System.Data. SqlClient" />
<add name="Systemx.My.MySetting s.Connecti onString_O LE"
connectionString="Provider =SQLOLEDB; Data Source=SQLSERVER;Initial Catalog=SystemXDB;Persist Security Info=True;User ID=xy;Password=abc"
providerName="System.Data. OleDb" />
</connectionStrings>
<userSettings>
<Systemx.My.MySettings>
<setting name="Database" serializeAs="String">
<value>SystemxDB</value>
</setting>
<setting name="RunBefore" serializeAs="String">
<value>False</value>
</setting>
</System2.My.MySettings>
</userSettings>
</configuration>
I'm assigning maximum points to this as there are a lot of other developers asking the same question on lots of other such sites but please do not say "you shouldn't be doing this!!!" - unless of course there is another way of getting VB to connect the dataset to a runtime connectionstring
Many thanks
Ian
I have developped a VB.NET 2005 application which includes the fancy new "drag 'n drop" DataSet tool. When you first create a new dataset VB writes the connection string into the app.config file (visible through myproject.settings) as an "application scope" value. Application scope values are read only at runtime which is all well and good while I'm developping and my development server is available but completely useless when it comes to releasing the application to clients.
How can you change these connection strings at run time??
I've been searching now for ages to a proper answer to this question and a lot of time the answer is "you shouldn't do that....microsoft intended these to be fixed etc,etc" which is avoiding the issue. These values need to be changed!!!
One client has a need to set these values on a "per user" basis as the databases are replicted out to branch offices and the connectionstring needs to point at the replicated database.
The answer ould seem to be to change the values in the <programname>.exe.config file that is created in the user's directory and apply these in the settings.SettingsLoaded function but how exactly can this be done?
Simply assigning these values to a User Scope won't work as VB will continue to look at the application scope value for any updates effected on the dataset
I have seen Q_20954883 but it doesn't appear to address changing the connectionstring values (I accept that the app will need to be restarted the first time these are changed) as they don't appear to follow the normal XML structure rules.
Here's part of the app.config file...(values have been changed to protect the innocent)
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<sectionGroup name="userSettings" type="System.Configuration
<section name="System2.My.MySetting
</sectionGroup>
</configSections>
<connectionStrings>
<add name="Systemx.My.MySetting
connectionString="Data Source=SQLSERVER;Initial Catalog=SystemXDB;Persist Security Info=True;User ID=xy;Password=abc"
providerName="System.Data.
<add name="Systemx.My.MySetting
connectionString="Provider
providerName="System.Data.
</connectionStrings>
<userSettings>
<Systemx.My.MySettings>
<setting name="Database" serializeAs="String">
<value>SystemxDB</value>
</setting>
<setting name="RunBefore" serializeAs="String">
<value>False</value>
</setting>
</System2.My.MySettings>
</userSettings>
</configuration>
I'm assigning maximum points to this as there are a lot of other developers asking the same question on lots of other such sites but please do not say "you shouldn't be doing this!!!" - unless of course there is another way of getting VB to connect the dataset to a runtime connectionstring
Many thanks
Ian
ASKER
Thanks Roger
I have been using this approach up til now but I recently wasted time looking at why an app worked fine in development and not in live envionment only to find I'd forgotten to include the redirect on a newly added table.
I do not understand why Microsoft allows us to change so many parameters and system attributes in VB.NET but decided we're not to be trusted changing connnectionstrings!!
Ian
I have been using this approach up til now but I recently wasted time looking at why an app worked fine in development and not in live envionment only to find I'd forgotten to include the redirect on a newly added table.
I do not understand why Microsoft allows us to change so many parameters and system attributes in VB.NET but decided we're not to be trusted changing connnectionstrings!!
Ian
ASKER
Hi All
I have found a workaround to this but I'm still looking for a better solution (i.e changing the app.config files)
The solution I have used involves changing the settings.desogner.vb code which is created by the dataset design tool. The only reason my.settings.<connection string> are read only is that the designer only creates a "Get" property for them, its a 2 line change to add the "Set" property (since all we're doing is storing a string value there!). I then set the value to what I want in the settings.vb.settingsloaded function.
Ok it's not perfect as the settings.designer.vb gets re-written every time the designer is changed but you'll know if that's happened as the connectionstring will become readonly again and your code won't compile.
Obviously you'll have to do this for each connectionstring if you have more than one and don't forget to remove the "readonly" value from the property command
Ian
I have found a workaround to this but I'm still looking for a better solution (i.e changing the app.config files)
The solution I have used involves changing the settings.desogner.vb code which is created by the dataset design tool. The only reason my.settings.<connection string> are read only is that the designer only creates a "Get" property for them, its a 2 line change to add the "Set" property (since all we're doing is storing a string value there!). I then set the value to what I want in the settings.vb.settingsloaded
Ok it's not perfect as the settings.designer.vb gets re-written every time the designer is changed but you'll know if that's happened as the connectionstring will become readonly again and your code won't compile.
Obviously you'll have to do this for each connectionstring if you have more than one and don't forget to remove the "readonly" value from the property command
Ian
i know this is in C# but this is what i use to save changes to the app.config during runtime:
what you can do is enable your application to read from a static variable which contains your connection string and allow you application to modify this variable during runtime and before your application exits save the value of the connection string back into the app.config for use next time round.
using System;
using System.Xml;
using System.Configuration;
using System.Collections;
using System.Reflection;
using System.Diagnostics ;
namespace BDS.Core.Config
{
public enum ConfigFileType
{
WebConfig ,
AppConfig
}
public class AppConfig : System.Configuration.AppSe ttingsRead er
{
public string docName = String.Empty;
private XmlNode node=null;
private int _configType;
public int ConfigType
{
get
{
return _configType;
}
set
{
_configType=value;
}
}
public bool SetValue(string key, string value)
{
XmlDocument cfgDoc = new XmlDocument();
LoadConfigDoc(cfgDoc);
// retrieve the appSettings node
node = cfgDoc.SelectSingleNode("/ /appSettin gs");
if( node == null )
{
throw new System.InvalidOperationExc eption( "appSettings section not found");
}
try
{
// XPath select setting "add" element that contains this key
XmlElement addElem= (XmlElement)node.SelectSin gleNode("/ /add[@key= '" +key +"']") ;
if (addElem!=null)
{
addElem.SetAttribute("valu e",value);
}
// not found, so we need to add the element, key and value
else
{
XmlElement entry = cfgDoc.CreateElement("add" );
entry.SetAttribute("key",k ey);
entry.SetAttribute("value" ,value);
node.AppendChild(entry);
}
//save it
SaveConfigDoc(cfgDoc,docNa me);
return true;
}
catch
{
return false;
}
}
private void SaveConfigDoc(XmlDocument cfgDoc,string cfgDocPath)
{
try
{
XmlTextWriter writer = new XmlTextWriter( cfgDocPath , null );
writer.Formatting = Formatting.Indented;
cfgDoc.WriteTo( writer );
writer.Flush();
writer.Close();
return;
}
catch
{
throw;
}
}
public bool RemoveElement ( string elementKey)
{
try
{
XmlDocument cfgDoc = new XmlDocument();
LoadConfigDoc(cfgDoc);
// retrieve the appSettings node
node = cfgDoc.SelectSingleNode("/ /appSettin gs");
if( node == null )
{
throw new System.InvalidOperationExc eption( "appSettings section not found");
}
// XPath select setting "add" element that contains this key to remove
node.RemoveChild( node.SelectSingleNode("//a dd[@key='" +elementKey +"']") );
SaveConfigDoc(cfgDoc,docNa me);
return true;
}
catch
{
return false;
}
}
private XmlDocument LoadConfigDoc( XmlDocument cfgDoc )
{
// load the config file
if( Convert.ToInt32(ConfigType )==Convert .ToInt32(C onfigFileT ype.AppCon fig))
{
docName= ((Assembly.GetEntryAssembl y()).GetNa me()).Name ;
docName += ".exe.config";
}
else
{
docName=System.Web.HttpCon text.Curre nt.Server. MapPath("w eb.config" );
}
cfgDoc.Load( docName );
return cfgDoc;
}
}
}
// this is how you use it
AppConfig ac = new AppConfig();
ac.ConfigType = (int)ConfigFileType.AppCon fig;
appConfigDataSet = ac.GetAppSettings();
// set the value of the ConnString key
ac.SetValue("ConnString"," this is my connection string");
REgards,
Ready1
what you can do is enable your application to read from a static variable which contains your connection string and allow you application to modify this variable during runtime and before your application exits save the value of the connection string back into the app.config for use next time round.
using System;
using System.Xml;
using System.Configuration;
using System.Collections;
using System.Reflection;
using System.Diagnostics ;
namespace BDS.Core.Config
{
public enum ConfigFileType
{
WebConfig ,
AppConfig
}
public class AppConfig : System.Configuration.AppSe
{
public string docName = String.Empty;
private XmlNode node=null;
private int _configType;
public int ConfigType
{
get
{
return _configType;
}
set
{
_configType=value;
}
}
public bool SetValue(string key, string value)
{
XmlDocument cfgDoc = new XmlDocument();
LoadConfigDoc(cfgDoc);
// retrieve the appSettings node
node = cfgDoc.SelectSingleNode("/
if( node == null )
{
throw new System.InvalidOperationExc
}
try
{
// XPath select setting "add" element that contains this key
XmlElement addElem= (XmlElement)node.SelectSin
if (addElem!=null)
{
addElem.SetAttribute("valu
}
// not found, so we need to add the element, key and value
else
{
XmlElement entry = cfgDoc.CreateElement("add"
entry.SetAttribute("key",k
entry.SetAttribute("value"
node.AppendChild(entry);
}
//save it
SaveConfigDoc(cfgDoc,docNa
return true;
}
catch
{
return false;
}
}
private void SaveConfigDoc(XmlDocument cfgDoc,string cfgDocPath)
{
try
{
XmlTextWriter writer = new XmlTextWriter( cfgDocPath , null );
writer.Formatting = Formatting.Indented;
cfgDoc.WriteTo( writer );
writer.Flush();
writer.Close();
return;
}
catch
{
throw;
}
}
public bool RemoveElement ( string elementKey)
{
try
{
XmlDocument cfgDoc = new XmlDocument();
LoadConfigDoc(cfgDoc);
// retrieve the appSettings node
node = cfgDoc.SelectSingleNode("/
if( node == null )
{
throw new System.InvalidOperationExc
}
// XPath select setting "add" element that contains this key to remove
node.RemoveChild( node.SelectSingleNode("//a
SaveConfigDoc(cfgDoc,docNa
return true;
}
catch
{
return false;
}
}
private XmlDocument LoadConfigDoc( XmlDocument cfgDoc )
{
// load the config file
if( Convert.ToInt32(ConfigType
{
docName= ((Assembly.GetEntryAssembl
docName += ".exe.config";
}
else
{
docName=System.Web.HttpCon
}
cfgDoc.Load( docName );
return cfgDoc;
}
}
}
// this is how you use it
AppConfig ac = new AppConfig();
ac.ConfigType = (int)ConfigFileType.AppCon
appConfigDataSet = ac.GetAppSettings();
// set the value of the ConnString key
ac.SetValue("ConnString","
REgards,
Ready1
ASKER
Hi
Thank you for post. I've run it through a c# to vb.net convertor but it appears to be missing the .getsettings function which is described in your "how to use" instructions.
Ian
Thank you for post. I've run it through a c# to vb.net convertor but it appears to be missing the .getsettings function which is described in your "how to use" instructions.
Ian
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Many thanks - that works a treat. Hopefully it'll help out all the other web searchers looking for the answer to this problem
Ready1
My thanks too.
Roger
My thanks too.
Roger
no probs mate glad to be of some assistance
Regards,
Ready1
Regards,
Ready1
The only way I've found of doing this is via the .Connection.ConnectionStri
The Wizard embeds the ConnectionString as an Application Setting. Although it can then be altered, I have only been able to do this at design time. I have not been able to find any way of getting the Wizard to save it as a User Setting, or to retain amendments I made to the designer code to try to change it to a User Setting. It's all done in the background - with Designer code and Private properties and methods - and fiddling with those either causes crashes or doesn't persist through a Build.
But there is a "loophole". Each TableAdapter's .Connection property is declared, in the DataSet.Designer code file (in the DataSetTableAdapters Namespace), as Friend, and with a Set as well as a Get. So a sub on these lines for each TableAdapter (or one sub with a number of lines covering each of them)
Private Sub changeConnection()
myTableAdapter.Connection.
End Sub
called before any use is made of them allows the default, Application Setting value/s to be overwritten. As the only access I can find is via the TableAdapter it means that the change (probably the same change) has to be made for each. But I think this sort of "workaround" is essentially the line that you'll have to take. Where and how you store and retrieve the value/s that you use for <myUserConnectionString> is up to you.
I'll be interested to hear if anyone else has found a more "pukka" approach.
Roger