More Provider Model Goodieness - Selective Provision
So this time i've taken the old example one step further. Let's say that you've got a plugin architecture that works with the Provider model, so that plugins can be snapped right in via the configuration file. Now think about this for a moment - what if you wanted to be able to snap in as many plugins as you want, but want to be able to reference the one you need at run-time with a key?
Without going into too long-winded a discussion like the last post on this topic (which ended up being something shy of an article), here's an example of how to do something like this.
First off, the configuration file itself.
<
configuration>
<configSections>
<sectionGroup name="SelectiveProvider">
<section name="Settings"
type="SelectingProviderViaXPath.SelectiveProviderReader,
SelectingProviderViaXPath"
/>
</sectionGroup>
</configSections>
<SelectiveProvider>
<Settings>
<provider key="helloWorld">
<typeName>SelectingProviderViaXPath.HelloWorld</typeName>
</provider>
<provider key="goodbyeWorld">
<typeName>SelectingProviderViaXPath.GoodbyeWorld</typeName>
</provider>
</Settings>
</SelectiveProvider>
</configuration>
Now, a quick look at the configuration reader and settings classes.
using
System;
using System.Configuration;
using System.Xml;
using cs = System.Configuration.ConfigurationSettings;
namespace SelectingProviderViaXPath
{
public class SelectiveProviderReader : IConfigurationSectionHandler
{
/// <summary>
/// Creates an instance of the GenericDal settings object
/// from the custom configuration settings.
/// </summary>
/// <returns></returns>
public object Create(object parent,
object context,
XmlNode section)
{
XmlNodeList lst = section.SelectNodes("provider");
return new SelectiveProviderSettings(lst);
}
}
/// <summary>
/// The settings class, which contains the type name to be loaded.
/// </summary>
public class SelectiveProviderSettings
{
const string section = "SelectiveProvider/Settings";
string typeName = String.Empty;
XmlNodeList providerList;
internal SelectiveProviderSettings(XmlNodeList installedProviders)
{
providerList = installedProviders;
}
public string TypeName
{
get { return typeName; }
}
/// <summary>
/// Set the provider according to what was asked for in the
/// GetSettings() parameter.
/// </summary>
/// <param name="key"></param>
void SetProvider(string key)
{
for(int i=0; i<providerList.Count; i++)
{
string j = providerList[i].Name;
string keyValue = providerList[i].Attributes["key"].Value;
XmlNode nodTypeName = providerList[i].SelectSingleNode("typeName");
if(keyValue == key)
{
typeName = nodTypeName.InnerText;
return;
}
}
}
/// <summary>
/// Return an instance of the Settings object.
/// </summary>
/// <param name="providerKey">The provider's key we want to instantiate.</param>
/// <returns></returns>
public static SelectiveProviderSettings GetSettings(string providerKey)
{
SelectiveProviderSettings b =
(SelectiveProviderSettings)cs.GetConfig(section);
b.SetProvider(providerKey);
return b;
}
}
}
Next, the IProvider interface and a few basic implementations of it.
using
System;
namespace SelectingProviderViaXPath
{
public interface IProvider
{
void WriteMessage();
}
public class HelloWorld : IProvider
{
public void WriteMessage()
{
Console.WriteLine("Hello World!");
}
}
public class GoodbyeWorld : IProvider
{
public void WriteMessage()
{
Console.WriteLine("Goodbye World!");
}
}
}
Finally, a little console client to exemplify the whole deal.
using System;
namespace
SelectingProviderViaXPath
class Client
{
static void Main(string[] args)
{
Client c = new Client("goodbyeWorld");
c.WriteMessage();
Console.ReadLine();
}
protected IProvider provider;
/// <summary>
/// Constructor which sets up the required provider.
/// </summary>
/// <param name="providerKey">Key of the provider in the config setting.</param>
public Client(string providerKey)
{
SelectiveProviderSettings settings =
SelectiveProviderSettings.GetSettings(providerKey);
Type t = Type.GetType(settings.TypeName);
object o = Activator.CreateInstance(t);
provider = o as IProvider;
}
void WriteMessage()
{
if(provider != null)
provider.WriteMessage();
else
Console.WriteLine("Provider does not implement IProvider");
}
}
}
There you have it - the Provider model, extended somewhat so that multiple plugins can coexist in harmony, only to be used when needed by an individual application at run-time. Deviation, variation, and extension, all through one little model.