Loading configuration for external providers
Many times i have to develop applications using external providers so users can change the provider in the configuration file and change the way the application behaves. It's very helpful on some external components like Custom data access (i.e. using the PetShop approach), logging, and so on.
Usually each provider uses an interface so the application can run with any class implementing the interface. For example:
public interface ISomeProvider
{
// Custom provider methods.
}
In the application config file i place external providers in a custom section like this one (to handle this you have to code an IConfigurationSectionHandler):
<configuration>
<configSections>
<section name="MyAppConfig" type="MyApp.MyConfigSectionHandler,MyApp" />
</configSections>
<MyAppConfig>
<logprovider name="default" type="MyApp.DefaultLogProvider,MyApp" />
</MyAppConfig>
</configuration>
After implementing solutions like that in many applications i was using something like a pattern for provider configurations.
I add a method named "Init" in the interface. This will allow to initialize all the providers during application bootstrap and avoiding static constructors, or obscure flags so in the first use the provider it gets initialized.
The "Init" method receives an XmlNode. During configration load, the section handler reads the "logProvider" node and creates an instance of the type "MyApp.DefaultLogProvider,MyApp", after the instance is created, the "Init" method is called using the "logProvider" node as the first parameter. Why does the parameter is defined the "Init" Method instead using the constructor?. Because the calling application uses the "Init" method to initialize the provider, when an instance of a Type is created using Activator.CreateInstance( Type ), the parameters to the constructor must be specified as an object[], and of course, constructors can't be specified in the interface, so using a method in an interface avoids type mismatch problems.
If the provider needs some configuration it will define its config within the node that defines the provider, and aviods creating extra section handlers or loading provider data from a different file. So the xml will looks like:
<logprovider name="default" type="MyApp.DefaultLogProvider,MyApp">
<provider_specific_config />
</logprovider>
And the interface having the Init Method:
public interface ISomeProvider
{
void Init( XmlNode configNode );
// Custom provider methods.
}
Some more issues, if the Init method throws an exception (and the application needs the provider) the application does not load. This is great because you don't have to start using the application to know if all the providers have initialized correctly, if something fails you wil have an error during application load.
A variant for this we used in the
Configuration Management Application Block and
User Interface Application Block. In those blocks we don't define an XmlNode in the Init method but an IDictionary. The contents of the IDictionary are the attributes on the provider node and it's values. Of course this will only specify name=value pairs, but will not allow complex configuration schemas.
Hope it helps somebody.