How to Create a Configuration Section That Contains a Collection - c#

There is a great question and answer here that illustrates how to create a custom configuration section that is able to parse configuration of the following form into .Net objects:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="CustomConfigSection" type="ConfigTest.CustomConfigSection,ConfigTest" />
</configSections>
<CustomConfigSection>
<ConfigElements>
<ConfigElement key="Test1" />
<ConfigElement key="Test2" />
</ConfigElements>
</CustomConfigSection>
</configuration>
My question is, does anyone know how to create the same custom configuration section without the ConfigElements element? For example, one that would parse the following CustomConfigSection element in place of the one shown above:
<CustomConfigSection>
<ConfigElement key="Test1" />
<ConfigElement key="Test2" />
</CustomConfigSection>
The issue that I have is that it appears that the type CustomConfigSection needs to inherit from both ConfigurationSection and ConfigurationElementCollection, which of course is not possible in C#. The other approach I have found requires me to implement IConfigurationSectionHandler, which is deprecated as of .Net v2. Does anyone know how to accomplish the desired result? Thanks.

You don't need to inherit from both ConfigurationSection and ConfigurationElementCollection. Instead, define your config section like this:
public class CustomConfigSection : ConfigurationSection
{
[ConfigurationProperty("", IsDefaultCollection = true)]
public MyConfigElementCollection ConfigElementCollection
{
get
{
return (MyConfigElementCollection)base[""];
}
}
}
And your config element collection:
[ConfigurationCollection(typeof(MyConfigElement), AddItemName = "ConfigElement"]
public class MyConfigElementCollection : ConfigurationElementCollection
{
protected override ConfigurationElement CreateNewElement()
{
return new MyConfigElement();
}
protected override object GetElementKey(ConfigurationElement element)
{
if (element == null)
throw new ArgumentNullException("element");
return ((MyConfigElement)element).key;
}
}
And config element itself:
public class MyConfigElement: ConfigurationElement
{
[ConfigurationProperty("key", IsRequired = true, IsKey = true)]
public string Key
{
get
{
return (string)base["key"];
}
}
}

Related

How do I add custom ConfigurationSection to Assembly?

I've spent a few weeks trying to figure this out, this is a duplicate of a question I asked previously but did not get a response to, so I am refining the question here.
I've created a custom class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections;
using System.Configuration;
namespace mssql_gui
{
public class TestConfigSection : ConfigurationSection
{
[ConfigurationProperty("", IsRequired = true, IsDefaultCollection = true)]
public TestConfigInstanceCollection Instances
{
get { return (TestConfigInstanceCollection)this[""]; }
set { this[""] = value; }
}
}
public class TestConfigInstanceCollection : ConfigurationElementCollection
{
protected override ConfigurationElement CreateNewElement()
{
return new TestConfigInstanceElement();
}
protected override object GetElementKey(ConfigurationElement element)
{
return ((TestConfigInstanceElement)element).Key;
}
}
public class TestConfigInstanceElement : ConfigurationElement
{
[ConfigurationProperty("key", IsKey = true, IsRequired = true)]
public string Key
{
get { return (string)base["key"]; }
set { base["key"] = value; }
}
[ConfigurationProperty("value", IsRequired = true)]
public string Value
{
get { return (string)base["value"]; }
set { base["value"] = value; }
}
}
}
I've implemented it:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="testSection" type="mssql_gui.TestConfigSection"/>
</configSections>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1"/>
</startup>
<appSettings>
<add key="Data Source" value="localhost\SQLEXPRESS"/>
<add key="Initial Catalog" value="(empty)"/>
<add key="Integrated Security" value="SSPI"/>
</appSettings>
<testSection>
<add key ="testKey" value="tesValue"/>
</testSection>
</configuration>
and I have tried to access it, I am getting:
An error occurred creating the configuration section handler for testSection: Could not load type 'mssql_gui.TestConfigSection' from assembly 'System.Configuration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.
I understand that in the type, I should be declare an assembly dll, but I'm confused about that...because in the official instructions by MS, it says to create a new class for the handler:
Create a public class that inherits from the
System.Configuration.ConfigurationSection class.
Add code to define the section's attributes and elements.
Adding the class (at least through the visual studio interface) creates a .cs file, not a .dll assembly file, so how to I add that custom class to an assembly file in order to reference it in the <configSections> part of app.config?
If I understand correctly, you have problem with resolving what actually your Assembly is, since you are only creating .cs files that determine types that this file hold.
Assembly (in maybe not so accurate shorcut) is just the project you have in your solution. It will get compiled into its seperate assembly - the .dll you mentioned - later on.
When you add class to any .cs file in given project, on compile it will be included in project's assembly.
By default, if you won't provide assembly for configSection where its corresponding type should be found, App.config defaults to System.Configuration assembly - that's where you get your error from, since you've declared your section in your own assembly (== project).
Right click in Visual Studio on your project that holds App.config file and choose Properties to check its Assembly name:
Then add this name to your App.config section declaration. In my example its ConsoleApp1, so I will add it to configuration accordingly:
<configSections>
<section name="testSection" type="mssql_gui.TestConfigSection, ConsoleApp1"/>
</configSections>
Ensure that the type attribute of the section element matches the
manifest of the assembly (ensure that you specify both the correct
namespace and type name).
You need to add the name of the assembly (where the type relies) to the type attribute:
You'll get the name of the assembly from the AssemblyInfo.cs within the project where TestConfigSection class is defined.
<section name="testSection" type="mssql_gui.TestConfigSection, ASSEMBLYNAME"/>
Example asuming your assembly names mssql_gui
<section name="testSection" type="mssql_gui.TestConfigSection, mssql_gui"/>
You read it like this:
Configuration config =
ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
TestConfigSection mySec = (TestConfigSection)config.Sections["testSection"];
See more details at MSDN
How to: Create Custom Configuration Sections Using ConfigurationSection

Understanding App.config and using Configuration Manager: GetSection vs. ConnectionString

For some context, I want to open a connection to a database and execute some queries.
Here's my App.config:
<dbservers>
<connectionStrings>
<add name="Cube_ConnectionString" connectionString="OLEDB; Datasource=http://cube.com; Initial Catalog=BP" />
</connectionStrings>
<queries>
<add connectionStringName="CubeConnectionString" usedBy="DataAccessor" connectionString="" />
</queries>
</dbservers>
This is how I intend to retrieve the connectionString:
System.Configuration.ConfigurationManager.ConnectionStrings["Cube_ConnectionString"].ConnectionString;
I am wondering whether GetSection or ConnectionString would be best to use. And what would be returned for both of them? How do these two methods function within nested XML such as this?
Additionally, what's the purpose of putting queries in the app.config?
Thanks in advance
I don't think that ConnectionStrings will work unless you put them in the standard section for those. If you want a custom <dbservers> section, you'll have to use GetSection instead.
This functionality is a bit awkward, but highly useful. How to: Create Custom Configuration Sections Using ConfigurationSection is a useful guide for this.
Essentially this boils down to creating a class that inherits from ConfigurationSection, and adding the proper attributes (from the above guide):
public class PageAppearanceSection : ConfigurationSection
{
// Create a "remoteOnly" attribute.
[ConfigurationProperty("remoteOnly", DefaultValue = "false", IsRequired = false)]
public Boolean RemoteOnly
{
get
{
return (Boolean)this["remoteOnly"];
}
set
{
this["remoteOnly"] = value;
}
}
// Create a "font" element.
[ConfigurationProperty("font")]
public FontElement Font
{
get
{
return (FontElement)this["font"]; }
set
{ this["font"] = value; }
}
// Create a "color element."
[ConfigurationProperty("color")]
public ColorElement Color
{
get
{
return (ColorElement)this["color"];
}
set
{ this["color"] = value; }
}
}
...and then adding a reference to your section in the app/web.config:
<configuration>
<configSections>
<section name="dbservers" type="Namespace.DbServersSection, YourAssembly"/>
</configSections>
</configuration>

How to store custom data in web.config?

I have the following method in my apiController:
public IEnumerable<something> GetData(DataProvider dataProvider)
{
return dataProvider.GetData();
}
What I need is to invoke this method from javascript and pass it a parameter of DataProvider derived type. I can handle this by passing string, e.g. "FirstProvider" and than write N number of if's in GetData() method to create an instance of proper type.
But is there some way that I can write in web.config file something like:
<DataProviders>
<type = FirstDataProvider, alias = "FirstProvider">
<type = SecondDataProvider, alias = "SecondProvider">
</DataProviders>
Change getData method to:
public IEnumerable<something> GetData(string dataProviderAlias)
{
// get provider type by it's alias from web congfig,
// then instantiated and call:
return dataProvider.GetData();
}
And then find and instantiate the type by it's alias?
Note: I accepted the answer below cause it's pointed me in a right direction, but msdn says that IConfigurationSectionHandler is deprecated.
So I used ConfigurationSection, ConfigurationElementCollection, ConfigurationElement classes instead to build custom config section.
You can store arbitrary data in web.config in the appSettings element:
<configuration>
<appSettings>
<add key="FirstAlias" value="FirstProvider" />
<add key="SecondAlias" value="SecondProvider" />
</appSettings>
</configuration>
And you can then read the values using:
String firstAlias = System.Configuration.ConfigurationManager.AppSettings["FirstAlias"];
String secondAlias = System.Configuration.ConfigurationManager.AppSettings["SecondAlias"];
It's built-in. It's supported. It's where you're supposed to store custom data.
First of all, you can only store valid xml in web.config. <type = FirstDataProvider, alias = "FirstProvider"> is not valid xml.
Second, there are a lot of moving pieces. Please follow the steps carefully -
web.config
Make sure you enter the proper namespace for DataProviders. type="YOUR_APPLICATION.DataProviders".
<configuration>
<configSections>
<section name="DataProviders" type="WebApplication2010.DataProviders"
requirePermission="false"/>
</configSections>
<DataProviders>
<Provider type="FirstDataProvider" alias="FirstProvider"/>
<Provider type="SecondDataProvider" alias="SecondProvider"/>
</DataProviders>
....
</configuration>
Code
public class DataProviders : IConfigurationSectionHandler
{
private static bool _initialized;
public static List<Provider> _providers;
public object Create(object parent, object configContext, XmlNode section)
{
XmlNodeList providers = section.SelectNodes("Provider");
_providers = new List<Provider>();
foreach (XmlNode provider in providers)
{
_providers.Add(new Provider
{
Type = provider.Attributes["type"].Value,
Alias = provider.Attributes["alias"].Value,
});
}
return null;
}
public static void Init()
{
if (!_initialized)
{
ConfigurationManager.GetSection("DataProviders");
_initialized = true;
}
}
public static IEnumerable<Provider> GetData(string dataProviderAlias)
{
return _providers.Where(p => p.Alias == dataProviderAlias);
}
}
public class Provider
{
public string Type { get; set; }
public string Alias { get; set; }
}
Global.asax
For good design practice, you want to read data from web.config only once, and store them in static variables. Therefore, you want to initialize inside Application_BeginRequest of Global.asax.
public class Global : System.Web.HttpApplication
{
void Application_BeginRequest(object sender, EventArgs e)
{
DataProviders.Init();
}
}
Usage
var providers = DataProviders.GetData("FirstProvider").ToList();
Well, I'm not sure if I understand what you want to achieve, but to implement your idea you need a custom section handler.
http://msdn.microsoft.com/en-us/library/2tw134k3(v=vs.100).aspx
In case that you want to create a database connection for specific dataprovider, see this similar question:
ASP.NET: How to create a connection from a web.config ConnectionString?
In my case, I needed to store two byte[] variables in my Web.Config file. Since it must be valid XML data, I simply stored the contents of the arrays like so:
<appSettings>
<add key="Array1" value="0,1,2,3,4,5,6,7,8,9" />
<add key="Array2" value="0,1,2,3,4,5,6,7,8,9" />
</appSettings>
I then call a function that reads this into a C# string, split it into a string[], and parse each string element as a byte into a resulting byte[] and return it.

Best way of injecting application configuration

Well, I'm making my foray into this fantastic site with a question about the correct way to inject configuration settings into application components. So, the overview is : I have an application written in C# .Net 3.5. It consists of 3 assemblies - a Core, a Data and a Service. The data & service assemblies require settings retrieved from the app.config, which is done via a settings file, eg.
Code :
public static String RequestQueueConnectionString
{
get { return ConnectionSettings.Default.RequestQueueConnectionString; }
}
Config :
<applicationSettings>
<MyNamespace.Data.ConnectionSettings>
<setting name="RequestQueueConnectionString" serializeAs="String">
...
Now, the assemblies are all setup using StructureMap for IoC - which to my mind should provide the answer to what I am looking for, but I just can't quite see it!
IoC :
public static void ConfigureStructureMap(IContainer container)
{
container.Configure(x => ...
...
What I want to be able to do is to inject a configuration class already populated into the IoC container such that those settings are used for that assembly, NOT those specified in the settings file / app.config. So perhaps :
public static void ConfigureStructureMap(IContainer container, MyConfigClass config)
{
container.Configure(x => x.For<DataConfig>()
.Singleton()
.Use ???
...
I hope I have provided enough details here - forgive a newbie if I have not and please let me know what else would be helpful in answering this!
So, after a lot of searching and trial and error, I was presented with #default.kramer's link, which I duely followed! With a little bit of trial and error, again (best way in my opinion), I managed to get the solution I was looking for. Now, whilst you can follow the link (and I would highly suggest doing so), I am going to post the solution to my question as I implemented it. Hopefully this might help someone with a similar problem.
So, I now have my configuration setup class like so :
public static class DispatchConfiguration
{
public static void ConfigureStructureMap(IContainer container, IDispatchConfiguration dispatchConfig)
{
DispatchProcessBatchSize = dispatchConfig.DispatchProcessBatchSize;
ServiceIsActive = dispatchConfig.ServiceIsActive;
...
}
Now, before I was using a settings file to retrieve the configuration out of the app.config file. This was obviously good for ensuring I had flexibility in changing my config settings, but it left me with the problem of not being able to easily test those settings. Say 9/10 tests required the service to be active, but 1 test wanted to test "ServiceIsActive = false;", now I'm in trouble.
Now, however, I am able to inject the configuration from the test :
[Given(#"Config\.IsServiceActive returns false")]
public void GivenConfig_IsServiceActiveReturnsFalse()
{
var settings = new DispatchSettings
{
ServiceIsActive = false,
DispatchProcessBatchSize = 100,
UpdatedBy = "Unit Test"
};
DispatchConfiguration.ConfigureStructureMap(ObjectFactory.Container, settings);
}
And then in the real world I am able to get the settings from app.config :
public void Start(String[] args)
{
var dispatchConfig = this.GetDispatchConfiguration();
DispatchConfiguration.ConfigureStructureMap(ObjectFactory.Container, dispatchConfig);
...
}
private IDispatchConfiguration GetDispatchConfiguration()
{
var config = (DispatchSettings)ConfigurationManager.GetSection("DispatchSettings");
return config;
}
And then the actual config class looks like :
[XmlRoot(ElementName = "DispatchSettings", Namespace = "")]
public sealed class DispatchSettings : IDispatchConfiguration
{
public Int32 DispatchProcessBatchSize { get; set; }
public Boolean ServiceIsActive { get; set; }
...
}
For the sake of completeness the interface looks like so :
public interface IDispatchConfiguration
{
Int32 DispatchProcessBatchSize { get; }
Boolean ServiceIsActive { get; }
...
}
And finally, the config file looks like this :
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="DispatchSettings" type="MyNamespace.XmlConfigurator, MyNamespace.Core" />
</configSections>
<DispatchSettings type="MyNamespace.DispatchSettings, MyNamespace.Core">
<ServiceIsActive>True</ServiceIsActive>
<DispatchProcessBatchSize>100</DispatchProcessBatchSize>
</DispatchSettings>
Now, anyone with a keen eye will spot "MyNamespace.XmlConfigurator". I found this on one of my Google journeys, and the code allows you to deserialize an Xml config into a class of your desire (as shown in this example). So, to ensure you have the complete code to make this technique work, below is the code for the XmlConfigurator. I cannot remember where I came across it, but a big thanks to the person who wrote it!!
public sealed class XmlConfigurator : IConfigurationSectionHandler
{
public XmlConfigurator()
{
}
public object Create(object parent, object configContext, XmlNode section)
{
XPathNavigator navigator = null;
String typeName = null;
Type sectionType = null;
XmlSerializer xs = null;
XmlNodeReader reader = null;
try
{
Object settings = null;
if (section == null)
{
return settings;
}
navigator = section.CreateNavigator();
typeName = (string)navigator.Evaluate("string(#type)");
sectionType = Type.GetType(typeName);
xs = new XmlSerializer(sectionType);
reader = new XmlNodeReader(section);
settings = xs.Deserialize(reader);
return settings;
}
finally
{
xs = null;
}
}
}
And there you have it! I hope this allows anyone with a similiar issue to resolve it and is clear enough to follow!

How do you load a dictionary into a console application from an app.config?

I want to load a dictionary at startup in my console app from my app.config.
I know that I could use an xml library or linq to XML to load it to parse and traverse it. My question is there a BUILT IN way of doing it.
Isn't there some way to add an application configuration section into the app.config and then have it loaded automagically using ConfigurationManager class in the System.Configuration namespace?
Any example? BTW, I am in NET20.
EDIT
Sorry, I should have clarified. I want to load the dictionary WITHOUT using AppSettings. I know how to do that already. Of course, the downside of using AppSettings is that I have to change my code to add new values to my dictionary. That is why I am looking for a way to do it automatically.
You will need to add an <appSettings> section to your app.config file. It will look something like:
<appSettings>
<add key="foo" value="fooValue" />
<add key="bar" value="barValue" />
<add key="baz" value="bazValue" />
</appSettings>
From within your app, you can grab these values with System.Configuration.ConfigurationManager.AppSettings, which is a NameValueCollection, which is essentially a dictionary from string to string.
string myFoo = System.Configuration.ConfigurationManager.AppSettings["foo"];
You can use the appSettings section the way you describe, but that section easily gets polluted with a variety of needs, and so I usually avoid it. You can make custom sections to deal with this.
Imagine you have a class called "PluginSpec," you can write code like this:
[ConfigurationCollection(typeof(PluginSpec), AddItemName = "Plugin",
CollectionType = ConfigurationElementCollectionType.BasicMap)]
public class PluginCollection : ConfigurationElementCollection
{
//This collection is potentially modified at run-time, so
//this override prevents a "configuration is read only" exception.
public override bool IsReadOnly()
{
return false;
}
protected override ConfigurationElement CreateNewElement()
{
return new PluginSpec();
}
protected override object GetElementKey(ConfigurationElement element)
{
PluginSpec retVal = element as PluginSpec;
return retVal.Name;
}
public PluginSpec this[string name]
{
get { return base.BaseGet(name) as PluginSpec; }
}
public void Add(PluginSpec plugin){
this.BaseAdd(plugin);
}
}
The above code can be used from a member of another config class, like this:
[ConfigurationProperty("", IsDefaultCollection = true)]
public PluginCollection Plugins
{
get
{
PluginCollection subList = base[""] as PluginCollection;
return subList;
}
}
The above would be a member in a class that derives from ConfigurationElement or ConfigurationSection.

Categories