GetSection object cannot be cast to IDictionary<> - c#

I hope I'm just missing something simple. I need to read/write to a section of my exe.config file. I have this in my code:
var appConfiguration = ConfigurationManager.OpenExeConfiguration("Mytest.Console.exe");
var fileEnvironment = appConfiguration.GetSection("fileEnvironment");
and this is my app.config:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="fileEnvironment" type="System.Configuration.DictionarySectionHandler"/>
</configSections>
<fileEnvironment>
<add key="TestEntry1" value="A nice value"/>
<add key="TestEntry2" value="Another value"/>
</fileEnvironment>
</configuration>
My appConfiguration variable is returned as {System.Configuration.Configuration} and the "HasFile" property is set to true.
Without casting my variable "fileEnvironment" is returned as System.Configuration.DefaultSection. When I add as IDictionary<string, string> to the GetSection method fileEnvironment is null.
Any ideas?

I kept researching the dictionary issue and came up with this stackoverflow Q&A! It produces a collection instead of a dictionary, but points the way to a solution. Thanks for everyone's time.

According to this old article, when a section is implemented using DictionarySectionHandler, ConfigurationManager.GetSection() will return a non-generic IDictionary, and not an IDictionary<T,V>. That's why your cast failed.
Although in modern times, it looks like it actually returns a HashTable.

Related

Problem calling a second .config for keys in C#

I need a second .config to manage alot of keys. I tried using
<configSections>
<section name="apiConnection" type="CustomConfig.apiConnectionSection, CustomConfig" />
</configSections>
<apiConnection configSource ="ApiConnection.config"/>
Where "ApiConnection.config" is my .config file to manage keys but this didn't work.
Then i tried the "file" property in appSettings.
<appSettings file="ApiConnection.config">
This didn't work either. I Tried with:
../ApiConnection.config
~/ApiConnection.config
But nothing...
Some ideas?
The program doesnt break, just not show me the keys when i try with the ConfigurationManager.
https://i.stack.imgur.com/6xHK2.png
<img src= https://i.stack.imgur.com/6xHK2.png/>
EDIT
My file is in root path (with web.config)
The file looks like
<?xml version="1.0" encoding="utf-8"?>
<appSettings>
<add key="Secret" value="z8xyHkN3Eju2TS9u-4MXeI2AbZiuTsF7xYJcjIJ" />
<add key="Audience" value="keyforms"/>
</appSettings>
Ok I think I know what your problem is based on your last comment.
This code is creating a new configuration section called apiConnection.
<configSections>
<section name="apiConnection" type="CustomConfig.apiConnectionSection, CustomConfig" />
</configSections>
<apiConnection configSource ="ApiConnection.config"/>
This section's values will not be contained in app settings. So you won't be able to access it via
ConfigurationManager.AppSettings
You will need to access it in a different manner. Now the exact manner will depend on your implementation of CustomConfig.apiConnectionSection and CustomConfig. I would search your code to find the class that defines how this works.
This example shows how to pull values from a custom config section, SecureAppSettings that uses the NameValueCollection in the same manner as AppSettings. You will have to do some digging to figure out what Types you will need to utilize.
NameValueCollection section = (NameValueCollection)ConfigurationManager.GetSection("secureAppSettings");
string userName = section["userName"];

Castle Windsor: How to convert an appSettings dependency to a list or an array?

I have a component that depends on a list of strings:
// ctor
public MyComponent(IList<string> someStrings) { ... }
Using Castle Windsor, I'm trying to provide this dependency from the AppSettings section of my app.config file like so:
container.Register(Component
.For<IMyComponent>()
.ImplementedBy<MyComponent>()
.DependsOn(Dependency.OnAppSettingsValue("someStrings", "SomeStrings"))
);
The Windsor documentation on Inline Dependencies says:
appSettings and conversion: Values in the config file are stored as text, yet the dependencies in your code may be of other types (TimeSpan in this example). Windsor has got you covered, and for most cases will perform the appropriate conversion for you.
Does this cover my case where I'd like to convert to IList<string>?
If so, what is the correct way to enter the list of strings in the app.config file?
If not, is there an extensibility point where I can specify my own conversion?
The bigger issue here is that it's not trivial to store a list of values in appSettings. This question addresses it and the best answer is to create your own section in the settings file, which you then have to access via ConfigurationManager.GetSection() instead of ConfigurationManager.AppSettings.Get(), which is what Dependency.OnAppSettingsValue() uses. Looking through the rest of the Dependency class, it seems there's no built-in way to do this.
However, if it really is just strings that you need, you have at least two options that aren't all that bad (in my opinion).
1. Store the strings in your App.config file using a StringCollection.
This is just a shorter, built-in version of creating your own section in App.config. Use the editor in Visual Studio's Project Properties page to add an application setting of type StringCollection. This will make your App.config look something like this:
<configuration>
<configSections>
<sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
<section name="YourApplication.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
</sectionGroup>
</configSections>
<applicationSettings>
<YourApplication.Properties.Settings>
<setting name="SomeStrings" serializeAs="Xml">
<value>
<ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<string>one</string>
<string>two</string>
<string>three</string>
</ArrayOfString>
</value>
</setting>
</YourApplication.Properties.Settings>
</applicationSettings>
</configuration>
Then when configuring your component:
// StringCollection only implements IList, so cast, then convert
var someStrings = Properties.Settings.Default.SomeStrings.Cast<string>().ToArray();
container.Register(Component.For<IMyComponent>()
.ImplementedBy<MyComponent>()
.LifestyleTransient()
.DependsOn(Dependency.OnValue<IList<string>>(someStrings)));
2. Store the strings as a delimited list in appSettings, then manually split them.
This is likely the simpler approach, though assumes you can figure out a delimiter for your strings (which may not always be the case). Add the values to your App.config file:
<configuration>
<appSettings>
<add key="SomeStrings" value="one;two;three;four" />
</appSettings>
</configuration>
Then when configuring your component:
var someStrings = ConfigurationManager.AppSettings["SomeStrings"].Split(';');
container.Register(Component.For<IMyComponent>()
.ImplementedBy<MyComponent>()
.LifestyleTransient()
.DependsOn(Dependency.OnValue<IList<string>>(someStrings)));
In either case, we're just adding a small amount of work on top of Dependency.OnValue, which is all Dependency.OnAppSettingsValue does anyways.
I think this answers your questions, but to be explicit:
Yes, but you're doing the conversion yourself so you can convert to anything you'd like.
See the linked question and accepted answer, or use a StringCollection.
Dependency.OnValue is the extension (in my opinion), and I don't know of or see any other place where you would do it. However, given the steps above I don't think this is necessary.

How to deal with changed ConfigurationSection-Definition?

I have a Configuration-File that I read with the ConfigurationManager. There are some Sections that I defined by myself.
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="applicationWideSettingsSection" type="MyTestApp.ApplicationSettings.ApplicationWideSettingsSection, MyTestAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</configSections>
<applicationWideSettingsSection>
<pathToSomeFile value="C:\Users\..." />
</applicationWideSettingsSection>
</configuration>
There are new requirements: now I have to store another value in the applicationWideSettingsSection like this:
<applicationWideSettingsSection>
<pathToSomeFile value="C:\Users\..." />
<pathToSomeOtherFile value="C:\Programs\..." />
</applicationWideSettingsSection>
So I change my definition of the applicationWideSettingsSection. If I now run the application with an old config-File it throws an exception on this line:
var configSection = _config.GetSection("applicationWideSettingsSection");
because there is only the pathToSomeFile-Setting and the other one is missing.
Is there a way to manually add another (default value) to this section?
How would you deal with this??
Thanks in advance, Joerg
Because I didn't want to change my design away from my custom Setting-Types I found a different way:
I set the IsRequired to false so no exception is thrown when the setting is missing. If it's missing I set it to some value from code.
In a different approach I wanted to override some of the Configuration-Section methods but did not get it to work...
now I have to store another value in the applicationWideSettingsSection
Instead of your custom type ApplicationWideSettingsSection you can use a key-value pairs, so
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="applicationWideSettingsSection"
type="System.Configuration.NameValueSectionHandler" />
</configSections>
<applicationWideSettingsSection>
<pathToSomeFile value="C:\Users\..." />
</applicationWideSettingsSection>
</configuration>
This will store 1 or many parameters and allow you to enumerate on the inner parameters. You can also try DictionarySectionHandler. I found sample code in this post.

Getting section filtering to xml attribute

I want to select SMS section block according to tip attribute of SMS xml.
Currently: ConfigurationManager.GetSection("Logger/Sms") works but is there any way to get section like ConfigurationManager.GetSection("Logger/Sms[#tip='VF']")?
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<sectionGroup name="Logger">
<section name="Sms" type="caSectionTest.LogHandler, caSectionTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</sectionGroup>
</configSections>
<Logger>
<Sms tip="Clickatell">
<icerik>Soğuk zincir uygulamasından gönderilen sms</icerik>
<telNo>9053123123123</telNo>
<api>3363050</api>
<user>pkUser</user>
<pass>passhm</pass>
</Sms>
<Sms tip="Vodafone">
<icerik>write something into sms</icerik>
<telNo>905123123123</telNo>
<originator>336123</originator>
<user>ctUser</user>
<pass>9Mdfpass</pass>
</Sms>
</Logger>
</configuration>
You've probably long since moved on, but I created an XPath lookup for xml XElement recently available here: https://github.com/ChuckSavage/XmlLib/ If you want to use jsobo's comment to get the information you want.
You would use it like:
XElement root = XElement.Load(file);
XElement sms = root.XPathElement("//Sms[#tip={0}]", "VF"); // or "//Sms[#tip='VF']"
By using it with string.Format() syntax, you pass the type to the XPath as well, if you wanted to do a DateTime check, etc. I also find it easier for variable injections as well, instead of "//Sms[#tip='" + variable + "']". XPathElement is just XPath().FirstOrDefault() to return a single element.

App.config - custom section not working

I recently started building a console version of a web application. I copied my custom sections from my web.config. to my app.config. When I go to get config information i get this error:
An error occurred creating the configuration section handler for x/y: Could not load type 'x' from assembly 'System.Configuration
The line that it is not liking is:
return ConfigurationManager.GetSection("X/Y") as Z;
Anyone run into something like this?
I was able to add
<add key="IsReadable" value="0"/>
in the appSettings and read it.
Addition:
I do actually have this defined about the custom section:
<configSections>
<sectionGroup name="x">
<section name="y" type="zzzzz"/>
</sectionGroup>
</configSections>
it sounds like your config-section handler is not defined
<configSection>
<section
name="YOUR_CLASS_NAME_HERE"
type="YOUR.NAMESPACE.CLASSNAME, YOUR.NAMESPACE, Version=1.1.0.0, Culture=neutral, PublicKeyToken=PUBLIC_TOKEN_ID_FROM_ASSEMBLY"
allowLocation="true"
allowDefinition="Everywhere"
/>
</configSection>
I had this identical issue recently. I created a custom sectiongroup for a web application(ran just fine), but when I ported this layer to a console app, the sectiongroup was failing.
You were correct in your question regarding how much of the "type" is required in your section definition. I've modified your configuration section with an example below:
<configSection>
<section
name="yourClassName"
type="your.namespace.className, your.assembly"
allowLocation="true"
allowDefinition="Everywhere" />
</configSection>
You'll notice, the type now has class name followed by assembly name. This is required for interaction outside of a web environment.
NOTE: Assembly name does not necessarily equal your namespace(for a given section).
If you want a custom config handler you have to define the class and reference it as shown by Steven Lowe. You can inherit from predefined handlers, or you can just use the value/key pair that is offered in appSetting section as you noted.
At the top of your file you require to have configSection tag with inside the section.
You can have sectionGroup too. Example:
<configuration>
<configSections>
<sectionGroup name="x">
<section name="y" type="a, b"/>
</sectionGroup>
<configSections>
</configuration>
This class works as a general custom configuration section handler for any type...
public class XmlConfigurator : IConfigurationSectionHandler
{
public object Create(object parent, object configContext, XmlNode section)
{
if (section == null) return null;
Type sectionType = Type.GetType((string)(section.CreateNavigator()).Evaluate("string(#configType)"));
XmlSerializer xs = new XmlSerializer(sectionType);
return xs.Deserialize(new XmlNodeReader(section));
}
}
In your app.config, add
<section name="NameofConfigSection" type="NameSpace.XmlConfigurator, NameSpace.Assembly"/>
And in the configuration section element, add an attribute to specify the type you want the root element deserialized into..
<?xml version="1.0" encoding="utf-8" ?>
<NameofConfigSection configType="NameSpace.NameofTypeToDeserializeInto, Namespace.Assembly" >
...
</NameofConfigSection>

Categories

Resources