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

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>

Related

How to Create a Configuration Section That Contains a Collection

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"];
}
}
}

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.

BLToolkit using a MySQL connection

I am making an attempt to establish a connection to a MySQL server using BLToolkit, and have installed MySql.Data (6.5.4), BLToolkit (4.1.12) and BLToolkit.MySql (4.1.12) via NuGet. I can make a connection to a MSSQL server in a single line, but have had trouble with MySQL and ended up with the following configuration file ...
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<BLToolkit>
<dataProviders>
<add type="BLToolkit.Data.DataProvider.MySqlDataProvider" />
</dataProviders>
</BLToolkit>
<configSections>
<section name="BLToolkit" type="BLToolkit.Configuration.BLToolkitSection, BLToolkit.4" />
</configSections>
<connectionStrings>
<add name="Test"
connectionString="Data Source=localhost;Port=3306;Database=bltest;User ID=root;Password=root;"
providerName="MySql.Data.MySqlClient" />
</connectionStrings>
</configuration>
I have extended the DbManager class to implement a reference to the tables, and passed the name of the connection string into the base class. This is how I implemented this behaviour, which should be telling BLToolkit to load the connectionString from the configuration file ...
class BlDb : DbManager {
public BlDb()
: base("Test") {
return;
}
public Table<Car> Car { get { return GetTable<Car>(); } }
public Table<Make> Make { get { return GetTable<Make>(); } }
}
An exception, however, is thrown. The exception is "The type initializer for 'BLToolkit.Data.DbManager' threw an exception." with the inner exception being "Configuration system failed to initialize". How should I proceed? Please note that a similar question does exist on SO, Getting BLToolkit to work with MySQL, which might be a helpful reference for you but doesn't make any sense whatsoever to me. Is installing both NuGet packages not enough?
Firts you need to add the reference to the BLToolkit.Data.DataProvider.MySql.4.dll to your project. Then modify your extended DbManager class to look as the following
class BlDb : DbManager
{
public BlDb()
: base( new BLToolkit.Data.DataProvider.MySqlDataProvider(), "Server=myServerAddress;Database=myDataBase;Uid=myUsername;Pwd=myPassword" )
{
}
public Table<Car> Car { get { return GetTable<Car>(); } }
public Table<Make> Make { get { return GetTable<Make>(); } }
}
you can replace the hard-coded connection string and return it from your app.config file like ConfigurationManager.ConnectionStrings["Test"].ConnectionString

get certain property from app.config without looping

i need your help
i have app.config like this
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
...
</configSections>
<connectionStrings>
...
</connectionStrings>
<appSettings />
<userSettings>
<MySettings>
<setting name="Precision" serializeAs="String">
<value>0</value>
</setting>
</MySettings>
</userSettings>
<applicationSettings>
...
</applicationSettings>
</configuration>
what I need is to get 'Precision' value. how to get that without looping SectionGroups, SectionCollection?
note:
I've DAL and in my DAL need this precision to format the decimal value and the precision is managed by user(client) through the Presentation Layer. I save the precision value in app.config. The problem, the app.config is in Presentiation Layer, and I cannot use Properties.MySetting.Default.Precision to get that. (Thanks to Branko & Tim to remind me about this reason)
I would consider "settings injection" here - like dependency injection, but for settings :)
Presumably your entry point configures the whole system... so get it to read all the settings from app.config, and use those when creating and configuring the DAL (and anywhere else which needs settings). The only piece of code which needs to know how to use app.config can be the entry point. Everything else can be specified via POCOs, individual constructor parameters etc.
This is good in several ways:
Your code will be easier to test: to test different settings, just pass in different constructor arguments. No need for files etc.
Your immediate problem is solved: the DAL no longer needs to look through the settings file
You're isolating your configuration storage in a single place which can change if you decide to (say) use a different XML format, or an "ini"-style configuration format
If I've understood Jon's answer correctly it looks like the following:
public interface IConfigurationWrapper
{
IDictionary<string, string> Properties { get; }
T GetSection<T>(string name) where T : ConfigurationSection;
}
public class ConfigurationWrapper : IConfigurationWrapper
{
// implementation with with ConfigurationManager.GetSection or just placeholders
}
public interface IProduct
{
string Name { get; }
}
public class Product : IProduct
{
readonly IConfigurationWrapper m_configuration;
public Product(string key, IConfigurationWrapper configuration)
{
m_configuration = configuration;
}
public string Name
{
get { // use m_configuration to get name from .config }
}
}
public class ProductFactory
{
readonly IConfigurationWrapper m_configuration;
public ProductFactory(IConfigurationWrapper configuration)
{
m_configuration = configuration;
}
public IProduct CreateProduct(string key)
{
return new Product(key, m_configuration);
}
}
and usage looks like:
var config = new ConfigurationWrapper();
var factory = new ProductFactory(config);
var product = factory.CreateProduct("myproductkey");
clients work with IProduct interface only, "products" layer works with IConfigurationWrapper, while wrapper works with whatever the configuration you have (.config or mocks, you can have mock products for testing too). Code above is heavily stripped part of larger system just to provide example don't get it too literally.

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