how to manipulate connectionstring in app.config file? - c#

I am trying to set connectionstring at runtime. What I need is set datasource at runtime and restart application. I found some approaches in google but the problem is app.config backs to default value after Application.Restart();
I tried to save connectionstring in settings.cs and app.config both but it doesn't work for me.
this is my code:
public void setConnectionString()
{
try
{
string str = string.Empty;
str = string.Format(#"Data Source={0}; Initial Catalog=CRM01_DB; UID= {1}; PWD={2}", Default.DataSource, Default.UID, Default.UPass);
this["CRM01_DBConnectionString"] = str;
Default.Save();
Thread.Sleep(100);
var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
var connectionStringsSection = (ConnectionStringsSection)config.GetSection("connectionStrings");
connectionStringsSection.ConnectionStrings["CRM01_DBConnectionString"].ConnectionString = str;
config.Save();
ConfigurationManager.RefreshSection("connectionStrings");
Thread.Sleep(100);
}
catch (System.Exception ex)
{
System.Windows.Forms.MessageBox.Show(ex.Message);
}
I didn't get any errors but when application starts the connectionstrig has default value

If you testing this code from VS it might not work (and probably won't).
When running from VS the actually program that is executed is <your_program>.vshost.exe and the corresponding .config file is named <your_program>.vshost.exe.config and you should check your changes there.
But..
VS keep the original .config and .vshost.exe.config synchronized so you don't see your changes.
To test if the code works try it from outside VS or uncheck Enabled the Visual Studio hosting process (Properties -> Debug) (keep in mind that it has implications)

Related

How can I allow the user to modify connection strings stored in app.config if they lack write permissions?

First, some background on our app: When deployed, our application will be installed on a clients machine in the Program Files directory. We use connection strings which are stored in the App.config file that comes with the program. Under most circumstances, these connection strings will never change. However, at some point, it may be possible that the connection string info will go out of date. To that end, we have included a feature in our program that allows the user to enter new database information if the database can't be reached. The information is parsed into a connection string, tested for validity, and written back into the config file if valid. (Yes, this change is intended to affect all users on the computer, since they will be unable to run the application without a valid connection string anyway - if one user knows the DB info, then other users on the same computer will benefit from that knowledge. Writing to App.config instead of a per-user settings file is preferred.)
The problem I'm running into is that the end user will not have admin permissions, and thus will not be able to run our app at a level that allows it to make any changes to its own config file (since it is located in the C:\Program Files directory). I'm looking at a couple of different options, but I'm having problems implementing each of them:
Move config file to a different location. Not possible in this case because we are deploying an executable, and from what I understand, the App.config file must reside in the same directory.
Separate the connection string info into an external config file. I know about the configSource property that can be added to the connection string section of App.config. However, I'm having trouble specifying a concrete target. I can't simply put the file alongside the executable (otherwise I'd get the same permissions issues as the regular App.config). However, it appears environment variables (such as %AppData%) are not supported as valid config sources. I have tried to implement the solution from this question, but the program crashes before I can change the config source to a valid string because the ConfigurationManager apparently attempts to read the config source folder immediately and thus crashes with a FileNotFoundException.
Create our own config file implementation. I could always just create a custom class that is dedicated to reading a config file that is located in a specific location, but I would prefer using the built-in ConfigurationManager if possible.
My main question is this: How can I allow the end user (who only has read permissions in the application folder) to modify config settings dynamically when the config file must stay in the application folder?
Since windows xp, the OS prevents programs with out admin privileges from writing to the Program Files folder. The "Program Data" folder was created to allow programs to store persistent information. I always use a custom config file to store program data that an end User needs to config to connect to a database. I also encrypt at least the password of the connection string, but that is a another discussion. I provided a sample class and usage that stores a config file in Program Data Folder.
Config Class
using System;
using System.IO;
using System.Xml.Serialization;
namespace MyNamespace
{
public class Config
{
public string SomeStringProperty { get; set; }
public int SomeIntProperty { get; set; }
private static string _filePath = null;
static Config()
{
_filePath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.CommonApplicationData) + #"\My Program Name";
}
public static Config LoadConfig()
{
if (File.Exists(_filePath))
{
try
{
XmlSerializer reader = new XmlSerializer(typeof(Config));
StreamReader file = new StreamReader(_filePath);
Config config = (reader.Deserialize(file) as Config);
return config;
}
catch (Exception ex)
{
//Deal With no file file read error here.
}
}
// IF we get here no valid config file was loaded so make a new one.
return new Config();
}
public Exception SaveConfig()
{
try
{
XmlSerializer writer = new XmlSerializer(typeof(Config));
StreamWriter file = new StreamWriter(_filePath);
writer.Serialize(file, this);
file.Close();
return null;
}
catch (Exception ex)
{
return ex;
}
}
}
}
Basic Usage
Config config = Config.LoadConfig();
Debug.WriteLine("SomeIntProperty=" + config.SomeIntProperty.ToString());
Debug.WriteLine("SomeStringProperty=" + config.SomeStringProperty);
config.SomeIntProperty = 10;
config.SomeStringProperty = "Hello";
config.SaveConfig();
var configuration = System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration("~");
var section = (ConnectionStringsSection)configuration.GetSection("connectionStrings");
section.ConnectionStrings["YourNameConnectionString"].ConnectionString = "yourstring";

Why is StreamReader.ReadLine() and File.ReadAllText() returning null or empty

I did find a few similar questions, but they weren't able to point me in the right direction... This may be something entirely stupid, but if anyone could tell me why I can't get a string populated I'd appreciate it. Here's my method that's failing:
private static string passwordTrace { get; set; }
// ... lots of other code
private static void RefreshPassword()
{
try
{
string filePath = "\\\\[server ip]\\share\\folder\\file.abcd";
string npDecrypted;
DateTime lastRefreshDate = Properties.Settings.Default.lastRefresh;
if (DateTime.Now >= lastRefreshDate.AddDays(30))
{
using (StreamReader sr = new StreamReader(filePath))
{
string npEncrypted = sr.ReadLine();
if (npEncrypted.Length != 24)
{
string fr = File.ReadAllText(filePath);
npEncrypted = fr.Substring(0, 24);
}
npDecrypted = Decryptor(npEncrypted);
passwordTrace = npDecrypted; // added for debugging only! remove when done.
secureString npSecure = new SecureString();
foreach (char c in npDecrypted)
{
npSecure.AppendChar(c)
}
Properties.Settings.Default.adminpw = npSecure;
Properties.Settings.Default.Save();
}
}
}
catch (FileNotFoundException fnfe)
{
// code for handling this type of exception
}
catch (NullReferenceException nre)
{
// code for handling this type of exception
}
catch (Exception e)
{
// code to catch ANY other type of exception
}
}
Now, there are no errors or warnings when the VS debugger compiles everything, and it works correctly when debugging. But, if I copy the compiled exe (from C:\project\bin\Debug directory) and run it the issue arises.
The point that says passwordTrace = ... is called at another point by a message box. This works correctly when running via debugger and there aren't any exceptions thrown anywhere (I do have try/catches all over the place), but for whatever reason the PasswordTrace and the Properties.Settings.Default.adminpw don't seem to be holding their value throughout the applications execution.
Also, the file that is being read is an encrypted text file which will always have only 1 line of characters and that line is always 24 characters long. An example would be:
09sdjf09ausd08uf9!%38==
As a final statement, I also copied the app.exe.config and app.pdb to the server directory where I copied the compiled .exe file to see if that had anything to do with it and it didn't fix anything. I also tried running the .exe directly from the Debug directory (the same file that I'm copying elsewhere) and it works correctly. As soon as I move it off of the Local Disk it doesn't work.
So, my suspicions are that it has something to do with the environments working directory, or something to do with how the app is executing. I read something somewhere that noted the default users is not set, but I think that was specifically regarding ASP.NET. If that was the case and the user double-clicking on the .exe didn't have a proper network authentication then what would I do? And if it has something to do with the working directory, how can I circumvent this?
I'm gonna keep fiddling and if I figure it out I'll update this, but I'm so lost at the moment! lol! And for the last time - everything it/was working correctly until copying it to the server location.
Thanks in advance! :)

Overwrite encrypted configuration section without decrypting?

When I try to access configuration section that is encrypted and cannot be decrypted properly (for example, someone just grabbed config file from another machine blindly) - Configuration class is throwing an exception. I want to catch that situation and rewrite the section completely in such case.
I've tried to remove and add back the section in question, but it seems that removal is ignored - second statement in 'catch' throws another exception about such section already exists:
try
{
// this getter might throw an exception - e.g. if encrypted on another machine
var connStrings = config.ConnectionStrings;
}
catch (ConfigurationException)
{
config.Sections.Remove("connectionStrings");
config.Sections.Add("connectionStrings", new ConnectionStringsSection());
}
It might be related to the fact I have connectionStrings section residing in separate file, i.e. my config file has something like <connectionStrings configSource="connections.config"/>, while actual encrypted content is in the connections.config file.
Is it possible to do what I need without falling back to direct XML manipulations, by using .NET Configuration classes only?
I'm pretty sure this will do what you want. In my case, I just included a bogus connectionString setting:
<connectionStrings>
<add connectionString="foo"/>
</connectionStrings>
I didn't include a "name" property, so trying to read ConnectionStrings will blow up, just like in your case:
try {
var x = ConfigurationManager.ConnectionStrings; //blows up
}
catch {
var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
config.Sections.Remove("connectionStrings");
config.Save(); //Now save the changes
ConfigurationManager.RefreshSection("connectionStrings"); //and reload the section
var x = ConfigurationManager.ConnectionStrings; //can now read!
}
You still won't be able to do this:
config.Sections.Add("connectionStrings", new ConnectionStringsSection());
Because (at least on my machine), it's picking up connectionStrings from the Machine.config.
I suspect though that in your case that's fine. You are now at a point where if you want to, you can add your own connection strings and you don't really need to completely blow away the connection string section.

Class Library Connection String Confusion

I have managed to confuse myself... not difficult, I know, and am looking for some guidance...
I have written a dll which I am now starting to use in my Winforms UI.
This is a follow on question to this:
Class Libray Connection String - How to change?
As told in that post, I have added the identical connection string settings from the app.config in the dll to my App.config in my UI.
In my UI, I have a text box where the user can enter the connection string and hit "Save", which runs the following code:
Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
config.ConnectionStrings.ConnectionStrings["TPAPI.Properties.Settings.TruePotentialConnectionString"].ConnectionString = txtConnectionString.Text;
config.Save(ConfigurationSaveMode.Modified, true);
ConfigurationManager.RefreshSection("connectionStrings");
Which seems to update the string in the file correctly.
But, and this is where the previous posting confuses me...
That setting only gets read when the software starts. Somehow I need to change the setting which Belogix has explained I should do like this:
var connectionString = "Data Source=MegaServer;Initial Catalog=MyDb; .. etc ..";
using (var db = new MyDataContext(connectionString))
{
// This will connect to MegaServer...
}
But, I am calling functions in my dll like this:
List<Page> pages = Database.getlistOfPagesToScan();
How do I tell that call to start using the newly saved connection string from the UI's App.config?
Can anyone shed any light?
Thanks
You should get the connection string from the settings in the dll. Your answer is actually in the question you post.
Use this:
var connectionString = config.ConnectionStrings.ConnectionStrings["TPAPI.Properties.Settings.TruePotentialConnectionString"].ConnectionString;
You can also use this:
string s = Properties.Settings.Default.ConnectionStr;
Console.WriteLine("Connection string from main app: " + s);
//
// When setting access modifier on Class library to `public`
//
s = ClassLibrary1.Properties.Settings.Default.ConnectionStr;
Console.WriteLine("Connection string from dll: " + s);
Assuming you are using ConfigurationManager to retrieve the application connection strings, try:
ConfigurationManager.RefreshSection("connectionStrings");

C# Modify Connection String that exists inside a library

I have a Class Library, which inside just has a DataSet (MySQL connector) and a Connector class.
I use this class in multiple projects to connect to the database, and I always had the password embedded in the connection string but now I need to be able to modify this string(for security purposes) so I can have the user connect using their own account.
How can I modify this connection string.
I have tried the following
Properties.Settings.Default.DataBaseConnectionString = "String";
But it seems that the connection string is readonly becase it doesn't appear to have a setter value.
I also tried the following with no luck
Properties.Settings.Default.DatabaseConnectionString.Insert(
Properties.Settings.Default.DatabaseConnectionConnectionString.Length - 1,
"Password=dbpassword;");
You can modify them like this:
Properties.Settings.Default["MyConnectionString"] = newCnnStr;
For a full solution that also saves the new value to the file, you need to do something like this:
private static void ModifyConnectionStrings()
{
// Change the value in the config file first
var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
const string newCnnStr = "server=(local);database=MyDb;user id=user;password=secret";
config.ConnectionStrings.ConnectionStrings["MyProject.Properties.Settings.MyConnectionString"].ConnectionString = newCnnStr;
config.Save(ConfigurationSaveMode.Modified, true);
// Now edit the in-memory values to match
Properties.Settings.Default["MyConnectionString"] = newCnnStr;
}
If your dataset is in another assembly, you can still do this providing you make the settings for that assembly public. To do this:
Right-click the project in the solution explorer and click Properties
Click the Settings tab.
Change the Access Modifier dropdown to "Public", save, and close.
Then you can do this (assuming the other project is called "MyProject.DataLayer"):
private static void ModifyConnectionStrings()
{
// Change the value in the config file first
var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
const string newCnnStr = "server=(local);database=MyDb;user id=user;password=secret";
config.ConnectionStrings.ConnectionStrings["MyProject.Properties.Settings.MyConnectionString"].ConnectionString = newCnnStr;
config.ConnectionStrings.ConnectionStrings["MyProject.DataLayer.Properties.Settings.MyConnectionString"].ConnectionString = newCnnStr;
config.Save(ConfigurationSaveMode.Modified, true);
// Now edit the in-memory values to match
Properties.Settings.Default["MyConnectionString"] = newCnnStr;
MyProject.DataLayer.Properties.Settings.Default["MyConnectionString"] = newCnnStr;
}
Don't you have the source code of that class?
Also, but a little more complicated method, would be to patch the assembly using Reflector with the Reflexil AddIn.
I think you're asking how you can change connectionstring properties at runtime depending on who is using the application. Hope this helps.
In the past I have done this by making my connection string contain parameters that I can provide using string.Format.
<connectionStrings>
<add name="SomeDB" connectionString="("Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0};Jet OLEDB:Database Password={1}"" />
</connectionStrings>
string connectionString = string.Format(ConfigurationManager.ConnectionStrings["SomeDB"].ConnectionString, location, password);
It looks like you are loading the connection string from the config file, you should be able to change it from there. Once built it will be a file named the same as your compiled form plus .config. (For example application.exe.config)

Categories

Resources