I'm writing an application that has some application level configurations, but also has some configurations that should be done on a per-user basis.
I'm writing a config section for BrowserStack to use the App.config file (per msdn) which has let me define the important things that need to be stored, however, there are things that belong in a config file that don't belong in version control. In this particular instance browserstack.user and browserstack.key (which are retrieved from their documentation) belong in a separate config file and they shouldn't be checked in.
Does c# configuration allow for this kind of behavior, or would I have to modify my config section to do this? (Included below for reference)
public class BrowserStackSettings : ConfigurationSection
{
[ConfigurationProperty("hubUrl", DefaultValue = "http://hub-cloud.browserstack.com/wd/hub/", IsRequired = false)]
public string HubUrl
{
get
{
return (string)this["hubUrl"];
}
set
{
this["hubUrl"] = value;
}
}
[ConfigurationProperty("user", IsRequired = true)]
public string User
{
get
{
return (string)this["user"];
}
set
{
this["user"] = value;
}
}
[ConfigurationProperty("key", IsRequired = true)]
public string Key
{
get
{
return (string)this["key"];
}
set
{
this["key"] = value;
}
}
}
C# has something called settings. The scope of one setting can be "user" or "application". Applications settings cannot be changed easily. They are stored in app's .config file in Program Files. But user settings can be changed, and of course can be different for each user. Those settings are stored in another .config file somewhere in user's profile.
So, app should somehow allow users to change those settings, and to save them just call Properties.Settings.Default.Save();
Related
I have a sample c# application.
In the App.config file there is parameters such as add key="access" value="abcd" inside appSettings tag and in other class file there is a get property such as
public string Access { get; } from where I am getting the value abcd.Here there is no set property.But I am getting the value of access as abcd and its working fine.
My question is, where the value of access in App.config is set in the application.
A readonly property still can be set from within the class that defines the property. And that's what is happening here.
Pseudo code:
public class Settings
{
private string _access;
public Settings()
{
_access = read from config;
}
public string Access { get { return _access; } }
}
Suppose I want to move my app from c:\myapp.exe to d:\myapp.exe. It will loose all settings. How to prevent that?
Personally I use the registry to save and load my settings, so the location of my application isn't affected, but if you're using User.Config etc and want to fix the location, this might help:
Can I control the location of .NET user settings to avoid losing settings on application upgrade?
It is an implementation detail of the LocalFileSettingsProvider class. Which has the unenviable job of storing user-scoped settings in a file that's guaranteed to be unique so that different apps cannot accidentally overwrite each others settings.
It does so by storing the file in an AppData directory that has a hashed name. The hash is computed from several properties of the application. It grabs as much as it can, starting with the attributes in the AssemblyInfo.cs file. Particularly the [AssemblyVersion] matters which is how it can detect that a new version of your app might not be compatible with the old version of the user.config file.
But the attributes are not enough to make it unique, it also uses the full path name of the .exe in the hash. Which is a very strong selector for the appropriate .config file.
So, inevitably, if you move the .exe somewhere else, that's going to change the hash and that's going to get you an empty user.config file with all the settings back to their default setting.
It's a bit questionable to tinker with this, an app should only ever have one install directory. c:\program files\companyname\appname is the standard. But you can by implementing your own SettingsProvider class. That's not exactly easy to do, System.Configuration is a pretty nasty namespace. But a decent starting point is the RegistrySettingsProvider sample, which is probably usable as-is.
-
This depends 100% on the application.
An application by itself just needs to find it's dependencies, or the list of DLLs it requires to run. It'll look in the current directory for these most of the time, so this usually isn't an issue.
The largest issue is in the registry. If the application has written where it was installed to the registry, it may look for certain files in the old directory at runtime.
If you installed the application, this is also stored in the registry, and uninstalling from Add/Remove programs will no longer work.
If the application does not use the registry though, it can be moved without consequence. Many portable apps which run off flash drives take this approach, and as a result can be moved or deleted as needed...
Hope It Helps Your Cause..:)
You can create your own Settings class. Which works just like the original. Below I'm posting my implementation of Settings class. Any improvements will be greatly appreciated.
using System;
using System.ComponentModel;
using System.Drawing;
using System.Reflection;
using System.Windows.Forms;
using Microsoft.Win32;
namespace MyNamespace
{
static class Settings
{
private static string _connectionString = #"data source=C:\\database.s3db";
public static string ConnectionString
{
get { return GetSetting("_connectionString"); }
set { _connectionString = value; }
}
private static string _archives = "";
public static string Archives
{
get { return GetSetting("_archives"); }
set { _archives = value; }
}
public static bool CheckDuplicates
{
get { return bool.Parse(GetSetting("_checkDuplicates")); }
set { _checkDuplicates = value; }
}
private static bool _saveDocks = true;
public static bool SaveDocks
{
get { return bool.Parse(GetSetting("_saveDocks")); }
set { _saveDocks = value; }
}
private static Font _font = new Font("Tahoma", 12, GraphicsUnit.Pixel);
public static Font Font
{
get
{
var convert = new FontConverter();
var value = convert.ConvertFromString(GetSetting("_font"));
return (Font) value;
}
set { _font = value; }
}
public static void Save()
{
Type type = typeof(Settings);
var registryKey = Registry.CurrentUser.CreateSubKey(string.Format(#"Software\{0}\{1}\Settings", Application.CompanyName, Application.ProductName));
if (registryKey != null)
{
foreach (var field in type.GetFields(BindingFlags.NonPublic | BindingFlags.Static))
{
var converter = TypeDescriptor.GetConverter(field.FieldType);
var value = converter.ConvertToString(field.GetValue(null));
registryKey.SetValue(field.Name, value ?? field.GetValue(null));
}
registryKey.Close();
}
}
public static void SetDefaults()
{
var registryKey = Registry.CurrentUser.OpenSubKey(string.Format(#"Software\{0}\{1}\Settings", Application.CompanyName, Application.ProductName));
if (registryKey == null)
{
Save();
}
else
{
registryKey = Registry.CurrentUser.CreateSubKey(string.Format(#"Software\{0}\{1}\Settings", Application.CompanyName, Application.ProductName));
if(registryKey == null) return;
Type type = typeof(Settings);
foreach (var field in type.GetFields(BindingFlags.NonPublic | BindingFlags.Static))
{
if (registryKey.GetValue(field.Name) != null)
{
var converter = TypeDescriptor.GetConverter(field.FieldType);
var value = converter.ConvertFrom(registryKey.GetValue(field.Name, field.GetValue(null)));
field.SetValue(null, value);
}
}
registryKey.Close();
}
}
private static string GetSetting(string name)
{
var registryKey = Registry.CurrentUser.OpenSubKey(string.Format(#"Software\{0}\{1}\Settings", Application.CompanyName, Application.ProductName));
if (registryKey != null)
{
if (registryKey.GetValue(name) != null)
{
return registryKey.GetValue(name).ToString();
}
registryKey.Close();
}
return "";
}
}
}
To use this with your application just add properties and backing field for your settings just like above. Make sure you use backing field's name as string argument in GetSetting method in property's get accessor. Make sure you assign default values to settings fields.
For saving settings see below code.
Settings.Archives = ".7z,.rar,.zip";
Settings.CheckDuplicates = true;
Settings.SaveDocks = false;
Settings.Font = fontDialog.Font;
Settings.Save();
You must call SetDefaults method in your constructor of a main form. See below code.
namespace MyNamespace
{
public partial class FormMain : Form
{
public FormMain()
{
InitializeComponent();
Settings.SetDefaults();
}
}
}
If you have suggestion for improving this class. Then comment.
Make a separate dll to read the settings via whatever method you prefer - registry or xml reading, ... and put it in system path. Then you can call dll anywhere your exe is.
But under what circumstance do you need this requirement? I just wonder.
I am trying to change the connection String values
connectionString="Data Source=localhost;Initial Catalog=Tracker;Persist Security Info=false;User ID=sa;Password=p#ssw0rd"
from my user interface. Could any one help to change the settings from the user interface in the windows application?
From the comment thread on the original post, it sounds like the OP needs to enumerate data sources and allow a user to pick one (and perhaps store that preference). Assuming that is the case...
The actual connection to the database should be secured using integrated Windows security if at all possible. This a best practice. Integrated security eliminates the need to store credentials anywhere.
If the goal is to pick a datasource, it's not hard to list all databases within an environment using C#.
The user's preference/selection should be stored somewhere other than the app.config. The registry, isolated storage, or an XML file are all valid options if no credentials are involved.
ASP.NET provides the Web Site Administration Tool to view and manage the configuration settings for your ASP.NET website. These configuration settings are stored in an xml file called web.config.
web.config is an application configuration file used to define configuration settings such as connecting strings, authentication, authorization etc., for an ASP.NET application
The tool provides an easy to use interface to edit the configuration settings. However this tool works well when you have to manually make changes. At times, there could be a possibility where we need to make changes to the web.config at runtime. For example: a user requesting to control the connecting string, change session timeouts and so on.
For this purpose, ASP.NET provides the Configuration API to programmatically manipulate the configuration settings in web.config. The API’s are contained in the System.Configuration and System.Web.Configuration namespaces. In fact, internally the Web Site Administration Tool uses the same API to edit configuration settings.
The WebConfigurationManager class in the System.Web.Configuration namespace is used to create a Configuration object. This object is used to read and write changes to the web.config file. See.
See also Securing Connection Strings
Let me clarify that this is not what you're asking but by reading this, you will be able to find some clues and come to know what is recommended and what is not.
If this is a Windows application then you don't need to change the app.config file to change the active database connection. I've written about this on my blog.
User below method to change connection string using C# :
public void SaveConnectionString(DbInfo dbinfo, string path,string appConfigFile)
{
try
{
string configFile = Path.Combine(path, appConfigFile);
var doc = new XmlDocument();
doc.Load(configFile);
XmlNodeList endpoints = doc.GetElementsByTagName("connectionStrings");
foreach (XmlNode item in endpoints)
{
if (item.HasChildNodes)
{
foreach (var c in item.ChildNodes)
{
if (((XmlNode)c).NodeType == XmlNodeType.Element)
{
var adressAttribute = ((XmlNode)c).Attributes["name"];
if (adressAttribute.Value.Contains("YourConStrName"))
{
if (dbinfo.dbType == dataBaseType.Embedded)
{
((XmlNode)c).Attributes["connectionString"].Value = SetupConstants.DbEmbededConnectionString;
((XmlNode)c).Attributes["providerName"].Value = SetupConstants.DbEmbededConnectionProvider;
}
else if (dbinfo.dbType == dataBaseType.Microsoft_SQL_Server)
{
if (dbinfo.sqlServerAuthType == SqlServerAuthenticationType.SQL_Server_Authentication)
{
// ((XmlNode)c).Attributes["connectionString"].Value = string.Format(SetupConstants.dbConnStringwithDb, dbinfo.databaseAdress, SetupConstants.SqlDbName, dbinfo.userId, dbinfo.password) + "MultipleActiveResultSets=true;";
((XmlNode)c).Attributes["connectionString"].Value = string.Format(SetupConstants.dbConnStringwithDb, dbinfo.databaseAdress, dbinfo.DatabaseName, dbinfo.userId, dbinfo.password) + "MultipleActiveResultSets=true;";
}
else if (dbinfo.sqlServerAuthType == SqlServerAuthenticationType.Windows_Authentication)
{
//((XmlNode)c).Attributes["connectionString"].Value = string.Format("Data Source={0};Initial Catalog={1};Integrated Security=True;MultipleActiveResultSets=true;", dbinfo.databaseAdress, SetupConstants.SqlDbName);
((XmlNode)c).Attributes["connectionString"].Value = string.Format("Data Source={0};Initial Catalog={1};Integrated Security=True;MultipleActiveResultSets=true;", dbinfo.databaseAdress, dbinfo.DatabaseName);
}
((XmlNode)c).Attributes["providerName"].Value = SetupConstants.DbSqlConnectionProvider;
}
}
}
}
}
}
doc.Save(configFile);
string exePath = Path.Combine(path, "EDC.Service.exe");
InstallerHelper.EncryptConnectionString(true, exePath);
}
catch (Exception ex)
{
//TODO://log here exception
Helper.WriteLog(ex.Message + "\n" + ex.StackTrace);
throw;
}
}
Add bellow class DBinfo
public class DbInfo
{
public DataBaseType dbType { get; set; }
public SqlServerAuthenticationType sqlServerAuthType { get; set; }
public string ConnectionString { get; set; }
public string databaseAdress { get; set; }
public string userId { get; set; }
public string password { get; set; }
public string Port { get; set; }
public string DatabaseName { get; set; }
}
public enum DataBaseType
{
Unknown = 0,
Embedded = 1,
Microsoft_SQL_Server =2,
}
public enum SqlServerAuthenticationType
{
Windows_Authentication = 0 ,
SQL_Server_Authentication =1
}
Consider the following configuration group in a .NET .config file.
<MySettingsGroup enabled="true">
<MySettingsSection enabled="true">
</MySettingsSection>
</MySettingsGroup>
The supporting classes are:
public class MySettingsConfigurationSection : ConfigurationSection
{
[ConfigurationProperty("enabled", DefaultValue = true, IsRequired = false)]
public bool Enabled
{
get
{
// works fine
return Convert.ToBoolean(this["enabled"]);
}
}
public class MySettingsConfigurationGroup : ConfigurationSectionGroup
{
[ConfigurationProperty("enabled", DefaultValue = true, IsRequired = false)]
public bool Enabled
{
get
{
// the way to retrieve attributes in sections is not supported by groups
// return Convert.ToBoolean(this["enabled"]);
return true;
}
}
How can the Enabled property on the MySettingsConfigurationGroup be implemented?
I don't think section groups were designed to be customized in the way you're attempting. A better solution would be to simply define your own configuration section that itself contains other configurations and omit the use of a section group altogether. Then, you'd get the full flexibility that configuration sections offer.
I have an application which i have some configuration files for cache, queue, and database.
public class ServerConfiguration: ConfigurationSection
{
[ ConfigurationProperty( FOO, DefaultValue = "", IsRequired = false ) ]
public string FOO
{
get { return (string)this[FOO]; }
set { this[FOO] = value; }
}
}
this is what i do for config files and I also have some inheritance hierarchy.
What do you use to handle configurations and what are some best practices for this purpose?
I love and use the Microsoft configuration library extensively but I try to make sure that my applications are not dependent on it. This usually involves having my configuration section implement an interface, so your example would look like:
public class ServerConfiguration : ConfigurationSection, IServerConfiguration
{
[ ConfigurationProperty( FOO, DefaultValue = "", IsRequired = false ) ]
public string FOO
{
get { return (string)this[FOO]; }
set { this[FOO] = value; }
}
}
public interface IServerConfiguration
{
public string FOO { get; } //Unless I am updating the config in code I don't use set on the interface
}
Now where ever you use your configuration in your code you only need to worry about IServerConfiguration and you can change your implementation without having to change the usages. Sometimes I just start of with a hard coded class during development and only change it to a configuration section when I actually need to have different values in different environments.
If you are using a configuration section you are also dependent on the ConfigurationManager. I have hidden this from my code by using an IConfigurationProvider[T] where T would be IServerConfiguration, you can see an example of this on my blog under configuration ignorance.
http://bronumski.blogspot.com/search/label/Configuration