Writing your own Provider

Writing the Provider base class

Providers need to have a base class they can inherit from to provide the necessary functionality for example:

public abstract class MenuProviderBase : ProviderBase
{
    public abstract IEnumerable RetrieveMenuItems();
}

This base class should inherit from the ProviderBase class in the System.Configuration assembly, by convention it should be marked as abstract.

Custom provider collection class

You also need to create a provider collection class this will hold a collection of your custom providers:

public class MenuProviderCollection : ProviderCollection
{
    public new MenuProviderBase this[string name]
    {
        get {return base[name] as MenuProviderBase;}
        set {base[name] = value;}
    }

    public override Add(ProviderBase provider)
    {
        if (provider == null)
            throw new ArgumentNullException("provider");

        if (provider as MenuProviderBase)
             throw new ProviderException("Invalid provider type");

             base.Add(provider);
    }
}

Writing the configuration section class

 You need to have a way to translate the settings from the web.config file into an object that knows how to handle them, this is acheived by inheriting from the ConfigurationSection class:

public class MenuProviderConfigurationSection : ConfigurationSection
{
    [ConfigurationProperty("defaultProvider")]
    public string DefaultProvider
    {
        get {return base["defaultProvider"] as string;}
        set {base["defaultProvider"] = value;}
    }

    [ConfigurationProperty("providers")]
    public ProviderSettingsCollection Providers
    {
        get {return base["providers"] as ProviderSettingsCollection;}
    }
}

Using the ProviderConfigurationCollection allows us to rely on the built-in provider model to handle passing configuration values to our providers as you will see later on.

Writing a provider implementation

With the provider base class in place we can create our own custom provider implementation:

public class XmlMenuProvider : MenuProviderBase
{
    private string xmlFile = string.Empty;
    private string name = "XmlMenuProvider";
    public override Initialize(string name, NameValueCollection config)
    {
        if (!string.IsNullOrEmpty(name))
            this.name = name;

        this.xmlFile = config["xmlFile"];

        if (string.IsNullOrEmpty(xmlFile))
            throw new ProviderException("xmlFile attribute must be supplied");
    }

    public override IEnumerable RetrieveMenuItems()
    {
        //... read xml file populate, perform some yield returns on matched nodes
    }
}

As I mentioned before by using the ProviderConfigurationCollection we get any attributes declared for the provider pushed into the Initialize method via the config NameValueCollection parameter pretty neat, you still need to make sure that the necessary settings have been placed in the config file like I’m doing for the xmlFile setting above.

Creating a provider factory

in order to get hold of a provider implementation we need somewhere where we can look at the config, grab the appropiate configuration section we declared above, instantiate the providers and pull out the one to use, this is something that we don’t want to perform everytime we ask for a provider so instead we use a factory that should only do this operation once and for subsequent calls it will give us a provider already instantiated, here’s the code:

public static class MenuProviderFactory
{
    private static MenuProviderBase activeProvider = null;
    private static MenuProviderCollection providers = null;
    private static readonly object locker = new object();

    public static MenuProviderBase RetrieveMenuProvider()
    {
        if (activeProvider == null)
        {
            lock (locker)
            {
                if (activeProvider == null)
                {
                    MenuProviderConfigurationSection config =
                                                                                 WebConfigurationManager.GetSection( "system.web/menuProviders" ) as MenuProviderConfigurationSection;

                    if (config == null)
                        throw new ConfigurationErrorsException("menuProviders section is not a MenuProviderConfigurationSection" );

                    providers = new MenuProviderCollection();
                    ProvidersHelper.InstantiateProviders(config.Providers, providers, typeof(MenuProviderBase));
                    activeProvider = providers[config.DefaultProvider);
                    if (activeProvider == null)
                        throw new ProviderException("unable to load default provider");
                }
            }
        }

        return activeProvider;
    }
}

We can utilize some of the classes found in the System.Web.Configuration namepsace, namely the WebConfigurationManager which can provider us our particular section by supplying it with the path location. Also we can use the ProvidersHelper class to instantiate the providers by passing in the provider settings from our configuration section.

Setting up web.config

After we have all the classes in place we then need to configure the web.config, firstly we need to add a section to the system.web sectionGroup, so inside the configuration element add the following:


    <section />

After adding that we then need to add the actual provider section, so inside the system.web element add the following:


    
        
    

Usage

For this example we will assume we have our own menu control, the usage inside the Render method becomes:

public void Render(HtmlTextWriter writer)
{
    MenuProviderBase provider = MenuProviderFactory.RetrieveMenuProvider();
    foreach (MenuItem menuItem in provider.RetrieveMenuItems())
    {
        //... create html elements for screen
    }
}
Advertisements