C# Optional fields in application settings - c#

Is there a way to create some optional fields in application settings. For example for one client we need some client based settings in the settings file, something like this:
<?xml version="1.0"?>
<configuration>
<configSections>
<userSettings>
<setting name="Client_1_out_folder" serializeAs="String">
<value>c:\</value>
</setting>
<setting name="Some_other_setting" serializeAs="String">
<value>True</value>
</setting>
...
And for the other client we dont need the Client_1_out_folder at all so to keep the config file clean would be nice to remove it from the config file all together. So for client 2 that part of config file would look like:
<?xml version="1.0"?>
<configuration>
<configSections>
<userSettings>
<setting name="Some_other_setting" serializeAs="String">
<value>True</value>
</setting>
...

Create a custon configuration section for your settings. Then on the configurationsection class, mark the property as "IsRequired=false" to make that property optional.
[ConfigurationProperty("frontPagePostCount"
, DefaultValue = 20
, IsRequired = false)]

You can create a class which inherits from ConfigurationSection.
Then, you can do practically whatever you want. It's much more powerful than the user settings.
MSDN: How to: Create Custom Configuration Sections Using ConfigurationSection
You can extend ASP.NET configuration
settings with XML configuration
elements of your own. To do this, you
create a custom configuration section
handler. The handler must be a .NET
Framework class that inherits from the
System.Configuration.ConfigurationSection
class. The section handler interprets
and processes the settings that are
defined in XML configuration elements
in a specific section of a Web.config
file. You can read and write these
settings through the handler's
properties.
The article says "ASP.NET", but it's not just for ASP.NET. It works equally well for WinForms.

I recommend creating your own configuration sections with Configuration Section Designer.
Unfortunately this tool isn't compatible with VS2010 but it is so very helpful that I keep using VS2008 to use it. Either way you create an extra assembly for the configuration section handler so you can use VS2008 only for this assembly and build the rest of the solution with VS2010. So this isn't a huge drawback at all.

There is also a good sample about create a custom configuration sections.
I hope it can help you...
app-config-and-custom-configuration-sections

Put those common settings in a .config file, and refer it in a special config file.
<!-- in general.config -->
<appSettings>
<add key="common1" value="something"/>
<add key="common2" value="something else"/>
</appSettings>
<!-- in client1.config -->
<appSettings file="general.config" >
<add key="specialKey1" value="for client 1 only"/>
</appSettings>
<!-- in client2.config -->
<appSettings file="general.config" >
<add key="specialKey2" value="for client 2 only"/>
</appSettings>

The use of custom configuration sections is a good idea, and you can then code for the entry to be required. That is a nice and clean way to handle this problem.
However, you could also handle this by a class that picks up these details, and tests for the existence ( or otherwise ) of this, having them all still in the usersettings section. So your main code would access the setting from the class:
if(Settings.HasClient)
//use Settings.Client;
Process(Settings.OtherSetting);
Depending on how you need to use them. Within Settings Constructor, you would access the settings directly.

I suspect there is something missing from your question.
If client 2 does not require the "Client_1_out_folder" setting and does not try and retrieve it at run time, you should be able to simply remove it, without having to make any other changes.
Have you tried doing so?

Related

Updating connection string to LDAP with NHibernate and Oracle

I've inherited an app that uses NHibernate and FluentNibernate to connect to an Oracle database. Unfortunately, I have no experience with NHibernate. The current connection string is like so:
Data Source=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=hostname)(PORT=0000)))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=servicename)));User Id={username};Password={password};
but now needs to switch over to LDAP (see the last code snippet).
My question, is it possible to make a change only to the web.config with an LDAP connection or will I need to add the Oracle.ManagedDataAccess and make some code changes?
It is my understanding that OracleClientConfiguration has been deprecated based on this page hence the part of the question about a new nuget package. Another note, there isn't a hibernate.cfg.xml file within the project (which I think is ok?) but wondering if this project isn't using the best practices for NHibernate. Any other hints or tips would be appreciated.
C# code:
private ISessionFactory GetSessionFactory()
{
var connectionString = LoginHelper.GetConnectionString(ConnectionStringSetting, LoginInfoSetting);
var iSessionFactory
= Fluently
.Configure()
.Database(OracleClientConfiguration.Oracle10.ConnectionString(connectionString))
.Mappings(e => e.FluentMappings.AddFromAssembly(Assembly.GetExecutingAssembly()))
.BuildSessionFactory();
return iSessionFactory;
}
Web.config (there no other references to NHibernate in the Web.config. There are some references to dependentAssembly in other dll.config files):
<configuration>
<configSections>
<section name="hibernate-configuration" type="NHibernate.Cfg.ConfigurationSectionHandler, NHibernate" />
<configSections>
<configuration>
What I (think I) need in the Web.config is something like so:
<oracle.dataaccess.client> <!-- or <oracle.manageddataaccess.client>? -->
<version number="*">
<LDAPsettings>
<LDAPsetting name="DIRECTORY_SERVERS" value="" />
<LDAPsetting name="DIRECTORY_SERVER_TYPE" value="" />
<LDAPsetting name="DEFAULT_ADMIN_CONTEXT" value="" />
</LDAPsettings>
<settings>
<setting name="NAMES.DIRECTORY_PATH" value="" />
<setting name="LDAP_ADMIN" value="" />
</settings>
</version>
</oracle.dataaccess.client>
EDIT 1:
Contents of ldap.ora file:
DIRECTORY_SERVERS=(ldap:XXX)
DEFAULT_ADMIN_CONTEXT="dc=Oracle,dc=com"
DIRECTORY_SERVER_TYPE=ad
Here is what I did in order to get this to work...
In the Oracle folder structure, navigate to the Network folder.
Add the following files (should be able to copy and paste another folder down called Sample):
sqlnet.ora
ldap.ora
tnsnames.ora
sqlnet.ora (the key here was adding LDAP to the beginning of the list):
SQLNET.AUTHENTICATION_SERVICES= (NTS)
NAMES.DIRECTORY_PATH= (LDAP, TNSNAMES, EZCONNECT)
ldap.ora:
DIRECTORY_SERVERS=(ldap:XXX:XXX)
DEFAULT_ADMIN_CONTEXT="dc=oracle,dc=com"
DIRECTORY_SERVER_TYPE=XX
tnsnames.ora:
No change was required; left as default
Connection string:
<add name="connection" connectionString="Data Source=YourDataSource"/>
<!-- might need to add a username/password -->
According documentation it should be this:
<LDAPsettings>
<LDAPsetting name="DIRECTORY_TYPE" value="AD" />
<LDAPsetting name="DEFAULT_ADMIN_CONTEXT" value="dc=Oracle,dc=com"/>
</LDAPsettings>
Or use
<settings>
<setting name="TNS_ADMIN" value="C:\oracle\work"/>
</settings>
and specify LDAP settings in C:\oracle\work\ldap.ora file (in conjunction with C:\oracle\work\sqlnet.ora file).
I don't think it is still valid but have a look at ODP.NET Managed library does resolve alias, but 32-bit library does
You can modify the config file with a simple text editor or use the config tool OraProvCfg.exe. Would be like this:
OraProvCfg.exe /action:config /product:odpm /frameworkversion:v4.0.30319 /providerpath:C:\oracle\product\12.1\Client_x64\odp.net\managed\common\Oracle.ManagedDataAccess.dll /set:settings\TNS_ADMIN:C:\oracle\network\admin
OraProvCfg.exe /action:config /product:odpm /frameworkversion:v4.0.30319 /providerpath:C:\oracle\product\12.1\Client_x64\odp.net\managed\common\Oracle.ManagedDataAccess.dll /set:LDAPsettings\DIRECTORY_TYPE:ad
Note, you have a OraProvCfg.exe for 32-bit (x86) and for 64-bit. Run the one which matches to your application, (or simply run both).

Variables in XML configuration

I'm trying to use xml configuration file in my project. Now it looks like:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="replication" type="Project.Replication.ReplicationConfigSection, Project.Replication" />
<section name="processing" type="Project.Processing.ProcessingConfigSection, Project.Processing" />
</configSections>
<replication>
<streams>
<stream name="STREAM_DATA_14360" />
</streams>
</replication>
<processing dataStream="STREAM_DATA_14360" />
</configuration>
It works OK, but I'm confused with duplicates in it ("STREAM_DATA_14360").
Can you remind me, how to create variables in XML or something for data reusing to be acceptable in application configuration?
UPDATE:
In real life my configuration has much more sections. There is a value, which apeears in many of this sections: STREAM_DATA_14360. So I want to be able to change this value only in one place of config file, and in other places to use reference to it.
Speed of changing configuration - is the first reason for it.
Size of a file is a second, because values can be huge: STREAM_INFO_FUTURE_SESSION_CONTENTS_12421 (that is third-party names)
You can simply add this value in <appSettings> and access it as you are saying.
You can do this as below:
<appSettings>
<add key="StreamName" value="STREAM_DATA_14360"/>
</appSettings>
In the code, you can access it as below:
string streamName = ConfigurationManager.AppSettings["StreamName"];
Make sure to add reference to System.Configuration assembly before using this.
XML doesn't have any native expansion macros or templating - any scenario would require that you do a preprocess step or have the code that reads the config involved in substituting the value.
If those aren't redacted names though, it seems a simple search/replace would solve the problem without much of a concern on false positives.
You could put something together with T4 templates as a preprocessor, whether that's worth it really depends on how often you expect to modify this file.
It should also be possible to shoehorn the web.config transformation engine into doing the replacements, but you may have to write some hosting code for the XDT engine depending on how your config file is setup.
Apart from using external code that might (or might not) facilitate your life, you can define your own classes that inherit from ConfigurationSection, wherein you define and encapsulate your key/value pairs and use the ConfigurationProperty attribute.
Have look at http://msdn.microsoft.com/en-us/library/2tw134k3.aspx for more info on How to: Create Custom Configuration Sections Using ConfigurationSection.
EDIT: you can make references in xsd (check here)
Thanks for your answers. I agree with Mark, there's no support of variables or references in XML. But, in my case there's much simpler solution. I feel stupid now, but hope that it will help another slowpoke too.
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="global" type="Project.GlobalConfigSection, Project" />
<section name="replication" type="Project.Replication.ReplicationConfigSection, Project.Replication" />
<section name="processing" type="Project.Processing.ProcessingConfigSection, Project.Processing" />
</configSections>
<global>
<streamNames>
<streamName name="STREAM_DATA_14360" id="1"/>
</streamNames>
</global>
<replication>
<streams>
<stream nameId="1" />
</streams>
</replication>
<processing dataStreamId="1" />
</configuration>
Consequence: need to edit code to use global section as a source of all long names
Advantage: fast renaming, reusability of values

Edit App.Config name-value pairs from a separate tool

I am trying to create a simple tool for a service person to update a few entries in the App.Config of a different program. The App.Config file contains custom parameters used upon initialization of our program.
Since the App.Config contains many sensitive items a tool is needed to ensure only certain parameters are changed. Thus, the reason not to allow them to edit the App.Config directly.
My questions:
How can I access the name-value pairs from the config sections of an App.config from a separate program?
Which is better suited for the UI: Winforms or WPF? Are their controls that make it easy to add more entries in the future?
The tool should allow the user to set either a String, int, double or Boolean.
Here is the structure of the App.Config:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<sectionGroup name="Settings">
<section name="Section1" type="System.Configuration.NameValueSectionHandler"/>
<section name="Section2" type="System.Configuration.NameValueSectionHandler"/>
<section name="Section3" type="System.Configuration.NameValueSectionHandler"/>
<section name="Section4" type="System.Configuration.NameValueSectionHandler"/>
</sectionGroup>
</configSections>
<Settings>
<Section1>
<add key="NAME_STRING" value="Some String"/>
</Section1>
<Section2>
<add key="NAME_INTEGER" value="10"/>
</Section2>
<Section3>
<add key="NAME_DOUBLE" value="10.5"/>
</Section3>
<Section4>
<add key="NAME_BOOLEAN" value="true"/>
</Section4>
</Settings>
... Omitted ...
</configuration>
In the program which uses the App.Config itself, I can easily change the values like so:
NameValueCollection nvc = (NameValueCollection)ConfigurationManager.GetSection("Settings/Section1");
Is there a similar way to do this from a separate program after loading the App.Config?
An answer to Question 1: An app.config file is an XML file. It might be easiest to load it as an XML document and modify that programmatically, followed by a save, than to use System.Configuration classes.
ETA: I believe it can be done with ConfigurationManager. Look at the OpenMappedExeConfiguration method. There's a good example there.
You could treat the app.config file as a normal XML file. Use either XDocument or XmlDocument to load the file.
Then use XPath or Linq to XML to find the name-value pairs.
As for Windows.Forms vs. WPF, its a design decision. Both have good and bad points.
[Update]
If you still want to use System.Configuration, you can use the ConfigurationManager.OpenExeConfiguration to get access to the other program's app.config file. This returns a Configuration object, which has a GetSection method.

Read AppSettings from MEF plugin

I would like to access the values in the ConfigurationManager.AppSettings object from a MEF plugin which has its own app.config file.
However, the keys from the app.config file are not present in AppSettings after the plugin is loaded.
The keys from the application loading the plugin are still present.
I noticed that using a Settings.settings file allows this behaviour, via the app.config file, so the file must be being loaded somehow.
My plugin looks like:
[Export(IPlugin)]
public class Plugin
{
public Plugin()
{
// reads successfully from app.config via Settings object
var value1 = Settings.Default["Key1"];
// returns null from app.config via ConfigurationManager
var value1 = ConfigurationManager.AppSettings["Key2"]
}
}
The app.config looks like this:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<sectionGroup name="applicationSettings" type="..." >
<section name="Plugin.Settings" type="..." />
</sectionGroup>
</configSections>
<appSettings>
<add key="Key2" value="Fails" />
</appSettings>
<applicationSettings>
<Plugin.Settings>
<setting name="Key1" serializeAs="String">
<value>Works</value>
</setting>
</Plugin.Settings>
</applicationSettings>
</configuration>
I can manually load the app.config file with:
var config = ConfigurationManager.OpenExeConfiguration("Plugin.dll");
var value = AppSettings.Settings["Key2"].Value
but this seems more like a workaround than a solution.
Is there a way to access a MEF plugin's <appSettings> directly, from inside the plugin?
If not, what is recommended?
By default the ConfigurationManager loads the .config for the entry assembly, i.e. the assembly that started the currently executing process.
The correct way to do this would be something like this:
[Export(IPlugin)]
public class Plugin
{
public Plugin()
{
var config = ConfigurationManager.OpenExeConfiguration(Assembly.GetExecutingAssembly().Location);
var value = config.AppSettings.Settings["Key2"].Value;
}
}
This would make the plugin automatically open the .config for the DLL it was compiled in, and fetch values from there.
I'd recommend you to use a dependency injection tool like Unity, in order to provide to your plug-ins the configuration they required. By proceeding in this way, your plug-ins will no longer need to reference System.Configuration.dll.

Override config settings

I have a config file that is used in several projects, general.config, looks like:
<?xml version="1.0" encoding="utf-8" ?>
<appSettings>
<add key="mykey1" value="myvalue1"/>
<add key="mykey2" value="myvalue2"/>
</appSettings>
In one of the projects, I need to override one of the two settings. So the app.config of this project looks like:
<?xml version="1.0"?>
<configuration>
<appSettings file="general.config">
<remove key="mykey1"/>
<add key="mykey1" value="anothervalue"/>
<add key="mykey3" value="myvalue3"/>
</appSettings>
</configuration>
But remove is not working here. How can I override mykey1 without breaking mykey2? add works in this case. I can get myvalue3 from ConfigurationManager.
EDIT: general.config is copied to output folder automatically when compiling. Don't worry about the path issue. Currently I got:
ConfigurationManager.AppSettings["mykey1"]
//I got "myvalue1", but I want "anothervalue" here
//that is, this item is "overrided", just like virtual methods in C#
ConfigurationManager.AppSettings["mykey2"]
//this setting will not be modified, currently it works fine
ConfigurationManager.AppSettings["mykey3"] //good
A friend of mine answered this question. From MSDN:
You can use the file attribute to
specify a configuration file that
provides additional settings or
overrides the settings that are
specified in the appSettings element.
You can use the file attribute in
source control team development
scenarios, such as when a user wants
to override the project settings that
are specified in an application
configuration file. Configuration
files that are specified in a file
attribute must have the appSettings
element rather than configuration
element as the root node.
So in this question, the settings in general.config overrides items in app.config, different from that I think(want) app.config items overrides items in general.config. Now I think I have to resolve this issue in C# code(it will inevitable looks ugly).
Your use of the file attribute to load common settings with an expectation that keys added directly to the <appSettings> element would override those common settings is understandable, but unfortunately that is not how it works.
Microsoft's intention was for the file attribute to load common settings that override the individual application's settings.
This is discussed in some detail in the Microsoft Documentation
To overcome this problem, we very occasionally declare base settings in the common file, and then appropriately named overrides in the application configuration. However this does require additional code which is a bit ugly. e.g.
var config = ConfigurationManager.AppSettings["MSG_QUEUE_PROVIDER_OVERRIDE"]
?? ConfigurationManager.AppSettings["MSG_QUEUE_PROVIDER"]
?? "ActiveMQ";
<appSettings file="common.config">
<!-- Override the common values -->
<add key="MSG_QUEUE_PROVIDER_OVERRIDE" value="RabbitMQ"/>
</appSettings>
The elements are changed from the child and what i mean by that is currently your app.config is the parent file and the values are replaced by the ones existing in General.config
Since you are using remove in the parent file what its effectively doing is removing the element you specify in app.config but after that the elements from general.config are pushed in. Now say here in General.config you say remove mykey3 which is on your app.config you will see that the final collection has no key as mykey3.
In short this is not going to work. Hope this helped you.
You can add another config file say Test.config.
<appSettings>
<add key="mykey1" value="New value"/>
</appSettings>
and in the app.config appsettings section will look like this
<appSettings file="Test.config">
<add key="mykey1" value="myvalue1"/>
</appSettings>

Categories