Why is my Connection String only working in Dev? - c#

See DGibbs answer below.
I can't have the config file saved with the EXE, as this is present on each user's desktop, so it seems I am unable to store the password in the config file and will have to come up with some other solution.
I have an app that needs to run a CMD command as an administrator. To achieve this, I stored the password in a connection string in app.config:
<connectionStrings>
<add name="mypw" connectionString="PASSWORD" />
</connectionStrings>
I am then able to call this in my Cmd class as a SecureString:
private static SecureString pw()
{
string connectionString = ConfigurationManager.ConnectionStrings["mypw"].ConnectionString;
SecureString ss = new SecureString();
foreach (char c in connectionString)
{
ss.AppendChar(c);
}
return ss;
}
When I run the app from VS on my machine with debugging (F5), it works fine and the password is retrieved. However, when running it in a development environment I see the exception Object Reference not set to an instance of an object, and from my own debugging I can see that this is happening at this line:
string connectionString = ConfigurationManager.ConnectionStrings["mypw"].ConnectionString;
Can anyone please explain why the app is able to retrieve the connection string on my machine but not when deployed elsewhere? Does the app.config file change when publishing the app?

Few things, don't use <connectionStrings>, this is typically used to store credentials for a db connection, it doesn't make sense here. Try using AppSettings within the App.config file e.g
<appSettings>
<add key="mypw" value="password" />
</appSettings>
You can retrieve the value like this:
string pw = ConfigurationSettings.AppSettings["mypw"];
Finally, make sure you have the config file deployed, it should be [ApplicationName].exe.config and not App.Config. If it doesn't appear, check the Copy to output directory setting, make sure it's set to Copy Always.

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"];

Firebird ConnectionString Change during runtime

I'm working on an app that has a live network database and a contingency local database, and it detects whether the live network db is accessible, and, if not, it times out after three seconds, changing the connectionstring to the local contingency database.
Following tips here on SO, I managed to alter the connectionstring on app.config during run time and reload the settings.
This is the method the app calls when a change on the connection string is needed:
public static void ChangeConnectionString(string connectionstring)
{
var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
var connectionStringsSection = (ConnectionStringsSection)config.GetSection("connectionStrings");
connectionStringsSection.ConnectionStrings[0].ConnectionString = connectionstring;
var connectionStrings = config.ConnectionStrings;
foreach (ConnectionStringSettings connectionString in connectionStrings.ConnectionStrings)
{
connectionString.ConnectionString = connectionstring;
}
config.Save();
ConfigurationManager.RefreshSection("connectionStrings");
PDV_WPF.Properties.Settings.Default.Save();
PDV_WPF.Properties.Settings.Default.Reload();
//Ensures the configuration is saved and reloaded.
FbConnection.ClearAllPools();
//Closes all currently open connections which might be using the old connection string.
Debug.WriteLine("==========Ran ChangeConnectionString");
Debug.WriteLine("==========FDBConnString is:");
Debug.WriteLine("==========" + PDV_WPF.Properties.Settings.Default.FDBConnString);
After I disconnect my computer form the network, whenever I check the current FDBConnString, it correctly points to the local contingency database. However, on the very next line, when it tries to run a query, I get the following exception:
Inner Exception 1:
IscException: Unable to complete network request to host "dbserver".
Inner Exception 2:
SocketException: Este host não é conhecido //(This host is unknown)
Full exception details: https://pastebin.com/3syLvsQf
It seems that, even after I successfully change the connection string, and successfully reload the application config file, it still tries to open a connection using the old connection string. Even if I call Debug to print the current Properties.Settings.Default.FDBConnString right on the line above the call for FbConnection.Open(), it shows the new string rather than the incorrect, old one.
Any insights on what might be going on?
I found what was the issue.
I am instancing a inherited table adapter from the generated xsd file. When I declare a table adapter on my class it also inherits the connection string stored on app.config at the time of declaration. So it doesn't matter if I change app.config, as the declared table adapter is already stuck with the previous connection string.
So, the solution was, rather than changing the connection string stored on app.config, I just had to change the connection string on the declared table adapter:
tB_STOCKTableAdapter1.Connection.ConnectionString = Properties.Settings.Default.ContingencyDB;
tB_STK_PRODUCTTableAdapter1.Connection.ConnectionString = Properties.Settings.Default.ContingencyDB;
or
tB_STOCKTableAdapter1.Connection.ConnectionString = Properties.Settings.Default.NetworkDB;
tB_STK_PRODUCTTableAdapter1.Connection.ConnectionString = Properties.Settings.Default.NetworkDB;
Both ContingencyDB and NetworkDB are strings stored on app.config as a user-scoped string, which can be changed via a given "Settings" window presented to the user.

Changing Connection String in C# programmatically

I need to make a connection setting in my system (changing the connection string in run time). I mean the user can set up and connect to any server they want. My question is, how can I retrieve the last connection string the user made in the connection setting and use it when the user re run the program?
So far this what I've made :
connect = "Data Source=" + Class1.DS.ToString() + ";Initial Catalog=" + Class1.IC.ToString() + ";Integrated Security= True;pooling=false;Connection Timeout=0;";
MessageBox.Show("Connection Made!");
this.Close();`(this is for the settings form)
frmSettings settings = new frmSettings();
connectString = frmSettings.connect.ToString();
dbconnection = new SqlConnection(connectString);
dbconnection.Open(); //<--(and this is where I call the connection string after the set-up)
What will I do to retrieve the last connection string the user made? Any suggestions please help me..
I assume you are using WinForm. There are many options about saving settings such as registry, custom ini...etc. The easiest one I always try before going elsewhere is config file.
Add "System.Configuration" into your project reference. Right click your project and "Add New Item", search for "config", you will see "Application Configuration File", add it. Now you have a App.Config.
You can add items into the file like:
<?xml version="1.0"?>
<configuration>
<appSettings>
<add key="Config1" value="Foo" />
<add key="Config2" value="Bar" />
</appSettings>
</configuration>
And to use it, you can do:
Configuration configManager = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
var conf = configManager.AppSettings.Settings;
string val1 = conf["Config1"].Value;
You can play with the OpenExeConfiguration call to look for different locations depending on your YourApp.exe.config file, but you get the idea...

DataDirectory Set up project

I am doing a SETUP project for a C# winforms, sqlite application.
Connection string seems to a bit of a problem. The user will put the Database he wants to work with at a location(which v will tell him). In this example it is "C:\\Program Files(x86)\\DefaultCompany\\RSetup"; The user can work his own copy of the DB.
So I am setting the data directory to this path in the Program.cs Main
This is the only way I can think of. If there is a better way thats grt!!.
App.config
<add name="ConnString" connectionString="|DataDirectory|\MySQlite.db;Compress=True;Version=3"
providerName="System.Data.Sqlite" />
Program.cs
Setting the datadirectory to the path of the executable. Currently hard coded the path of the executable
static void Main()
{
AppDomain.CurrentDomain.SetData("DataDirectory","C:\\Program Files(x86)\\DefaultCompany\\RSetup");
This doesn't seem to be working. It doesn't give any error except the data is not blank. Doesn't seem to be working in both set up and the regular project
Thank you
JC
You could ask the user where the database is located, store that path somewhere (such as User Settings) and then you can retrieve it at any time. This would give the user more flexibility of where to put it and multiple users on the same machine could have their own database if desired.
Here is some pseudocode...
string dbLocation = Properties.Settings.Default.DatabaseLocation;
if (string.IsNullOrWhiteSpace(dbLocation)
{
dbLocation = AskUserForLocation();
Properties.Settings.Default.DatabaseLocation = dbLocation;
Properties.Settings.Default.Save();
}
AppDomain.CurrentDomain.SetData("DataDirectory",dbLocation);
Using this approach you could also add a menu option to allow the user to change the location if desired.
It also gives you the ability to retrieve the value anywhere, including where you create a connection, you can append the path to the location between where you read the connection string and you create a new connection.
SQLiteConnection myConnection = new SQLiteConnection;();
myConnection.ConnectionString = Path.Combine(Properties.Settings.Default.DatabaseLocation, myConnectionString);
myConnection.Open();

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.

Categories