Read 3rd party config section - c#

In a web.config file, I have a 3rd party settings section :
<configuration>
<configSections>
<sectionGroup name="TheProduct">
<section name="TheSection" type="TheCompany.TheProduct.TheSectionConfigurationHandler, TheCompany.TheProduct, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b1e9bee111e9429c" />
</sectionGroup>
</configSections>
<TheProduct>
<TheSection somevalue="true" />
</TheProduct>
</configuration>
I want to read the value of this section from another application, but when I try to find the section, I always get null.
Here is my code :
var config = ConfigurationManager.OpenExeConfiguration(
#"C:\inetpub\wwwroot\TheProduct\web.config"
);
var settings = config.GetSection("TheProduct/TheSection"); // always null
What is the correct way to retrieve the config section ?
[Edit] If the calling app, which defines the same section, I can do :
var settings = ConfigurationManager.GetSection("TheProduct/TheSection");
and it's working. Mybe I'm not opening the web.config file using the correct way ?

Using the configuration manager requires that your application be able to instantiate the actual strongly-typed configuration objects. If you can't add a reference to the defining assembly, then your only choice is to use one of the XML api's (either System.Xml or System.Linq.Xml) to read it manually, in which case you won't be using the configuration manager at all.

Related

Inconsistent ConfigurationManager behaviour

I have learnt about custom configurations using the ConfigurationManager. For some reason you have to use the assembly reference in the section element of the app.config, otherwise the ConfigurationManager won't load the app.config. But in an ASP.NET app this works fine. Why?
Consider this custom configuration class:
namespace CustomConfiguration
{
class MySection : ConfigurationSection
{
[ConfigurationProperty("link", IsKey = true)]
public string Link
{
get => (string) this["link"];
set => this["link"] = value;
}
}
}
Using this app.config, I can easily get the link-attribute of myCustomSection in my program:
<configuration>
<configSections>
<section name="myCustomSection" type="CustomConfiguration.MySection, myAssembly" />
</configSections>
...
<myCustomSection link="link i can access in my code" >
</myCustomSection>
</configuration>
Removing the assembly reference in section-element of app.config will result in a ConfigurationErrorsException, because the ConfigurationManager can't load my CustomConfiguration.MySection class in it's own System.Configuration assembly.
E.g.:
<section name="myCustomSection" type="CustomConfiguration.MySection" />
But Microsofts documentation says I should be able to do this.
And in fact I can do this in an ASP.NET app. Not supplying an assembly-name in a type attribute for a section still works and system.configuration magically looks in the right app assembly. Why?
The ASP.NET hosting environment has a different assembly loading behaviour;
it loads all referenced assemblies (from then bin and GAC) at the moment of startup.
For that reason, the assembly of the CustomConfiguration.MySection section can be resolved automatically without specifying it in the type definition.
If you include the settings below in you web.config file, your assembly myAssembly will not be loaded at initial startup anymore.
Then it will also be required to specify the assembly part in the type definition via CustomConfiguration.MySection, myAssembly. Here you get the same behaviour as in a non web-based application.
<configuration>
<!-- ... -->
<system.web>
<compilation>
<assemblies>
<remove assembly="myAssembly" />
</assemblies>
</compilation>
</system.web>
<!-- ... -->
</configuration>
The referenced documentation in your question shows that a section (shown belown) can be declared in an app.config file (of non web-based application), but this will only work for the out-of-the-box supplied configuration classes/handlers, like eg. System.Configuration.SingleTagSectionHandler, which resides in the core System assemby (System.dll).
For all other (custom) sections, a full assembly-qualified name is required.
<configuration>
<configSections>
<section name="sampleSection"
type="System.Configuration.SingleTagSectionHandler" />
</configSections>
<sampleSection setting1="Value1"
setting2="value two"
setting3="third value" />
</configuration>

Web.Config custom section returns null

Well I have following entries in my web config for SagePay Integration in an ASP.net MVC project.
<configuration>
<configSections>
<section name="SagePayConfiguration" type="System.Configuration.IgnoreSectionHandler" requirePermission="true" restartOnExternalChanges="true" allowLocation="true"/>
</configSections>
<SagePayConfiguration>
<!--Mandatory
Set to TEST for the Test Server and LIVE for the live environment-->
<add key="sagepay.api.env" value="TEST" />
.
.
</SagePayConfiguration>
However I am getting null when trying to access it from C# code.
SagePayConfiguration sagePayConfiguration = (SagePayConfiguration)System.Configuration.ConfigurationManager.GetSection("SagePayConfiguration");
Any help what I am missing here?
You must cast the ConfigrationItem to "System.Configuration.IgnoreSectionHandler" or set the type Attribute in web.conf to an existing Type in your assembly.
<section name="<SectionName>" type="<your type (FQN)>, <Assembly>" />
After that you can access it
YourType o = ((YourType)ConfigurationManager.GetSection("sectionName"));
EDIT:
The Type must derive from ConfigurationSection.

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.

When are settings from app.config actually read?

When are settings from app.config actually read by application?
Suppose I have a windows service and some app settings for it. In code I have a method where some setting is used. Method is being called in every iteration, not just once during all the time. If I change the setting value through the configuration file should I restart the service for it to be "refreshed" inside or will it be accepted the next time without any interaction from my side?
You need to call ConfigurationManager.RefreshSection method to get the latest values read directly from disk. Here's a simple way to test and provide answer to your question:
static void Main(string[] args)
{
while (true)
{
// There is no need to restart you application to get latest values.
// Calling this method forces the reading of the setting directly from the config.
ConfigurationManager.RefreshSection("appSettings");
Console.WriteLine(ConfigurationManager.AppSettings["myKey"]);
// Or if you're using the Settings class.
Properties.Settings.Default.Reload();
Console.WriteLine(Properties.Settings.Default.MyTestSetting);
// Sleep to have time to change the setting and verify.
Thread.Sleep(10000);
}
}
My app.config containing:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
<section name="ConsoleApplication2.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
</sectionGroup>
</configSections>
<appSettings>
<add key="myKey" value="Original Value"/>
</appSettings>
<userSettings>
<ConsoleApplication2.Properties.Settings>
<setting name="MyTestSetting" serializeAs="String">
<value>Original Value</value>
</setting>
</ConsoleApplication2.Properties.Settings>
</userSettings>
</configuration>
After you start the application, open the app.config within the build folder, and change the value of the appSetting "myKey". You'll see the new value printed out to the console.
To answer the question, yes they are cached on the first time they are each read I think, and to force the read straight from the disk, you need to refresh the section.
Either when you load it up via the configuration manager (ConfigurationManager.GetSection("x/y");) or when you try to access the properties.
There is a slight grey area here because when you get the configuration out via the config manager:
var config = (MyConfigSection)ConfigurationManager.GetSection("MyConfigSection");
You get a configuration object back if you have provided the configuration section type in the configurationSections element at the top of the config file. If you do not actually provide the actual config you will still get an object back.
However if you have a required field that is not set it will not throw an exception till you call the property. I have worked this out whilst trying to unit test my custom configuration sections.

Add custom configuration Element at runtime

Is it possible to add an custom configuration element at runtime.
Here is my app.config file
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="NodeList"
type="Configuration.NodeListSection, NodeListConfiguration"
requirePermission="false" />
</configSections>
<NodeList>
<nodes>
<add name="Dev1" isdefault="false" description ="Dev server" />
<add name="QA1" isdefault="true" description="QA server"/>
<add name="Prod1" isdefault="false" description="Production" />
</nodes>
</NodeList>
</configuration>
Can we add more nodes at runtime using C# code.
This doesn't appear to be from a built-in configuration section. You will find that "NodesList" is an section/element that is custom written. To determine where in your codebase it is coming from look for "NodesList" at the top of your config file in the configSections element. That will point you at the class to look into.
After that, you need the class to support write operations properly.
To learn a lot more about customising configuration files there is a great series at CodeProject on the topic. In particular, the section on Saving Configuration Changes should be helpful to you.
Edit (after more info added to question):
Try something like (of course it all depends on what's in NodeListSection codebase):
using Configuration;
var nodeListSection = ConfigurationManager.GetSection("NodeList") as Configuration.NodeListSection;
var newNode = new NodeElement() { Name = "xyz", IsDefault = false, Description = "New Guy" };
nodeListSection.Nodes.Add(newNode);
Configuration.Save(ConfigurationSaveMode.Modified);
The file you have posted does not look like a normal .NET config file, but a custom XML file.
In either case - .config files are just XML files - you can open, manipulate and save them using any of the XML libraries within the BCL, such as XDocument.
However, if you want to make changes to configuration during runtime, you will need to decide whether the application should apply these changes at runtime as well and code for this, as normally a configuration file will only be read at startup.
private void AddNewKey_Config(string key, string value, string fileName)
{
var configFile = ConfigurationManager.OpenExeConfiguration(fileName);
configFile.AppSettings.Settings.Add(key, value);
configFile.Save();
}

Categories