Click here to Skip to main content
15,881,139 members
Articles / Web Development / ASP.NET

Provider Pattern

Rate me:
Please Sign up or sign in to vote.
3.84/5 (14 votes)
31 Mar 2007CPOL3 min read 107.1K   1.2K   67   6
Create your own custom providers
Sample Image - maximum width is 600 pixels

Introduction

Provider pattern is one of the most interesting features that Microsoft introduced in .NET 2.
A lot of features including membership providers, roles providers, profile providers, health monitor event providers, site map providers, and more had the same design concept. These features are not changeable but extendable, and that is the beauty of the provider pattern.

In this article, I will demonstrate the concept of provider pattern itself, and how easy it is to create your own services designed by provider pattern, and make your services extendable and reusable.

Using the Code

Image 2

Using the code is very simple. Just open the solution file in Visual Studio 2005 and run the application. There is no database or web service to connect, it is simply a console application to give you a full idea of the provider pattern.

Image 3

Provider Pattern in More Detail

It is not a brand new idea; it came basically from the popular strategy pattern. It is a good idea to have a quick look at the strategy pattern in here.

Image 4

I see the strategy pattern as a changeable algorithm depending on different situations or different context. This algorithm is encapsulated in object, this object implements an interface or inherits from an abstracted class.

For example, the Paintbrush saves the same picture in different formats, GIF, BMP,...

Or Microsoft Word saves the document in different formats, doc, rtf, txt,... In my work, we used strategy pattern very often. Maybe, we will come to the top of our design pattern list, one of the implementations that we used, we used in collection builder to build the same collection in different ways.

For example, Customers collection has the same methods and behavior in a certain context, once the context changes, all the behavior changes. We have AllCustomers context and InvoiceCustomer context, ProductCustomer context, and each of them has a different database, sometimes it is a simple table in database or 1-many or many-many relation tables in database. So we create one collection but with different behaviors. I developed an authentication module. This module is very complex, but after I applied a strategy pattern with composite pattern, it became very simple to maintain, or to extend. The user object has a property called Role, and depending on the context the Role changes. The strategy pattern is beyond the scope of this article, but it is worth studying and implementing this pattern.

However, I still can see a few differences between strategy pattern and provider pattern.

Strategy pattern is a Generic concept, not sticking to a specific technology or global scenarios, but provider pattern in most cases is a configurable service, provided by external source, that you can plug-in to your application, or you may extend this service to create your own custom service, then plug-in to your application.

The service provider should have the same features or contract, that is defined in the base class as contract, but with different behaviors, different formats or different connectivity like using web services, database(s) or XML files or streams.

Creating Your Own Provider

There are 3 major steps to create your own provider.

Step 1: Create Your ServiceBase

Image 5

To create your own provider pattern, you must inherit your service from System.Configuration.Provider.ProviderBase class:

C#
using System;
using System.Collections.Specialized;

namespace System.Configuration.Provider
{
    // Summary:
    //     Provides a base implementation for the extensible provider model.
    public abstract class ProviderBase
    {
        // Summary:
        //     Initializes a new instance of the 
        //     System.Configuration.Provider.ProviderBase
        //     class.
        protected ProviderBase();

        // Summary:
        //     Gets a brief, friendly description suitable for display in administrative
        //     tools or other user interfaces (UIs).
        //
        // Returns:
        //     A brief, friendly description suitable for display in administrative tools
        //     or other UIs.
        public virtual string Description { get; }
        //
        // Summary:
        //     Gets the friendly name used to refer to the provider during configuration.
        //
        // Returns:
        //     The friendly name used to refer to the provider during configuration.
        public virtual string Name { get; }

        // Summary:
        //     Initializes the provider.
        //
        // Parameters:
        //   config:
        //     A collection of the name/value pairs representing the 
        //     provider-specific attributes
        //     specified in the configuration for this provider.
        //
        //   name:
        //     The friendly name of the provider.
        //
        // Exceptions:
        //   System.ArgumentNullException:
        //     The name of the provider is null.
        //
        //   System.InvalidOperationException:
        //     An attempt is made to call 
        //     System.Configuration.Provider.ProviderBase.Initialize
        //     (System.String,System.Collections.Specialized.NameValueCollection)
        //     on a provider after the provider has already been initialized.
        //
        //   System.ArgumentException:
        //     The name of the provider has a length of zero.
        public virtual void Initialize(string name, NameValueCollection config);
    }
}

In our sample, I created CurrencyProviderBase:

C#
using System;
using System.Configuration.Provider;

namespace Cli.Lsp.Provider.CurrencyProvider {
    public abstract class CurrencyProviderBase : ProviderBase {
        protected string _name;

        public override string Name {
            get { return _name; }
            }
        protected string _description;

        public override string Description {
            get { return _description; }
            }
        public abstract decimal Convert(Currency cur, decimal value);
        public abstract void Add(Currency cur, DateTime date, decimal factor);
        public abstract void Remove(Currency cur, DateTime date);
        public abstract CurrencyConversionItem[] List
		(Currency cur, DateTime startDate, DateTime endDate);
        }
    }

Step2: Create Your ServiceCollection

Image 6

To define your own collection class, you should inherit from System.Configuration.Provider.ProviderCollection.

C#
using System;
using System.Collections;
using System.Reflection;

namespace System.Configuration.Provider
{
    // Summary:
    //     Represents a collection of provider objects that 
    //     inherits from System.Configuration.Provider.ProviderBase.
    public class ProviderCollection : ICollection, IEnumerable
    {
        // Summary:
        //     Initializes a new instance of the 
        //     System.Configuration.Provider.ProviderCollection
        //     class.
        public ProviderCollection();

        // Summary:
        //     Gets the number of providers in the collection.
        //
        // Returns:
        //     The number of providers in the collection.
        public int Count { get; }
        //
        // Summary:
        //     Gets a value indicating whether access to the collection is synchronized
        //     (thread safe).
        //
        // Returns:
        //     false in all cases.
        public bool IsSynchronized { get; }
        //
        // Summary:
        //     Gets the current object.
        //
        // Returns:
        //     The current object.
        public object SyncRoot { get; }

        // Summary:
        //     Gets the provider with the specified name.
        //
        // Parameters:
        //   name:
        //     The key by which the provider is identified.
        //
        // Returns:
        //     The provider with the specified name.
        public ProviderBase this[string name] { get; }

        // Summary:
        //     Adds a provider to the collection.
        //
        // Parameters:
        //   provider:
        //     The provider to be added.
        //
        // Exceptions:
        //   System.ArgumentException:
        //     The System.Configuration.Provider.ProviderBase.Name of provider is null.-
        //     or -The length of the System.Configuration.Provider.ProviderBase.Name of
        //     provider is less than 1.
        //
        //   System.ArgumentNullException:
        //     provider is null.
        //
        //   System.NotSupportedException:
        //     The collection is read-only.
        public virtual void Add(ProviderBase provider);
        //
        // Summary:
        //     Removes all items from the collection.
        //
        // Exceptions:
        //   System.NotSupportedException:
        //     The collection is set to read-only.
        public void Clear();
        //
        // Summary:
        //     Copies the contents of the collection to the given array starting at the
        //     specified index.
        //
        // Parameters:
        //   array:
        //     The array to copy the elements of the collection to.
        //
        //   index:
        //     The index of the collection item at which to start the copying process.
        public void CopyTo(ProviderBase[] array, int index);
        //
        // Summary:
        //     Returns an object that implements the 
        //     System.Collections.IEnumerator interface
        //     to iterate through the collection.
        //
        // Returns:
        //     An object that implements System.Collections.IEnumerator to iterate through
        //     the collection.
        public IEnumerator GetEnumerator();
        //
        // Summary:
        //     Removes a provider from the collection.
        //
        // Parameters:
        //   name:
        //     The name of the provider to be removed.
        //
        // Exceptions:
        //   System.NotSupportedException:
        //     The collection has been set to read-only.
        public void Remove(string name);
        //
        // Summary:
        //     Sets the collection to be read-only.
        public void SetReadOnly();
    }
}

In our sample, CurrencyCollection is the service collection:

C#
using System;
using System.Configuration.Provider;

namespace Cli.Lsp.Provider.CurrencyProvider
{
    public class CurrencyCollection : ProviderCollection
    {
        public override void Add(ProviderBase provider)
        {
            if (provider == null)
                throw new ArgumentNullException("The provider parameter cannot be null.");

            if (!(provider is CurrencyProviderBase))
                throw new ArgumentException
		("The provider parameter must be of type MyProviderProvider.");

            base.Add(provider);
        }

        new public CurrencyProviderBase this[string name]
        {
            get { return (CurrencyProviderBase)base[name]; }
        }

        public void CopyTo(CurrencyProviderBase[] array, int index)
        {
            base.CopyTo(array, index);
        }
    }
}

Step 3: Implement First Concrete Provider

You should to implement at least one of the providers and test it carefully before releasing it in your own company library.

Image 7

C#
using System;
using System.Configuration;
using System.Configuration.Provider;

namespace Cli.Lsp.Provider.CurrencyProvider {
    public class SQLCurrencyConversion : CurrencyProviderBase {
        private String connectionString;

        public String ConnectionString {
            get { return connectionString; }
            set { connectionString = value; }
            }


        public override void Initialize(string name, 
		System.Collections.Specialized.NameValueCollection config) {
            #region Attributes check
            if ((config == null) || (config.Count == 0))
                throw new ArgumentNullException
		("You must supply a valid configuration parameters.");
            this._name = name;
            #endregion

            #region Description
            if (string.IsNullOrEmpty(config["description"])) {
                throw new ProviderException("You must specify a description attribute.");
                }
            this._description = config["description"];
            config.Remove("description");
            #endregion

            #region ConnectionString
            if (String.IsNullOrEmpty(config["connectionStringName"]))
                throw new ProviderException("The connection string is invalid.");
            connectionString = config["connectionStringName"];
            config.Remove("connectionStringName");

            ConnectionStringsSection cs =
                (ConnectionStringsSection)ConfigurationManager.GetSection
			("connectionStrings");
            if (cs == null)
                throw new ProviderException
		("An error occurred retrieving the connection strings section.");
            if (cs.ConnectionStrings[connectionString] == null)
                throw new ProviderException("The connection string could not be 
			found in the connection strings section.");
            else
                ConnectionString = 
		cs.ConnectionStrings[connectionString].ConnectionString;
            #endregion

            #region Extra Attributes validations
            if (config.Count > 0) {
                string extraAttribute = config.GetKey(0);
                if (!String.IsNullOrEmpty(extraAttribute))
                    throw new ProviderException
			("The following unrecognized attribute was found in 
				" + Name + "'s configuration: '" +
                                                extraAttribute + "'");
                else
                    throw new ProviderException("An unrecognized attribute was 
				found in the provider's configuration.");
                }
            #endregion
            }

        public override decimal Convert(Currency cur, decimal value) {
            Console.WriteLine
		("---- Convert method was Invoked SQL Currency conversion ----");
            Console.WriteLine("Provider Description ={0}", this.Description);
            Console.WriteLine("Connection string ={0}", this.ConnectionString);
            Console.WriteLine("Convert({0},{1}) ", 
			Enum.GetName(typeof(Currency), cur), value);
            Console.ReadLine();
            return 3.4m;
            }

        public override void Add(Currency cur, DateTime date, decimal factor) {
            Console.WriteLine("---- Add method was Invoked SQL Currency conversion ----");
            Console.WriteLine("Provider Description ={0}", this.Description);
            Console.WriteLine("Connection string ={0}", this.ConnectionString);
            Console.WriteLine("Add({0},{1},{2}) ", 
		Enum.GetName(typeof(Currency), cur), date, factor);
            Console.ReadLine();
            }

        public override void Remove(Currency cur, DateTime date) {
            Console.WriteLine
		("---- Remove method was Invoked SQL Currency conversion ----");
            Console.WriteLine("Provider Description ={0}", this.Description);
            Console.WriteLine("Connection string ={0}", this.ConnectionString);
            Console.WriteLine("Remove({0},{1}) ", 
		Enum.GetName(typeof(Currency), cur), date);
            Console.ReadLine();
            }


        public override CurrencyConversionItem[] List
		(Currency cur, DateTime startDate, DateTime endDate) {
            Console.WriteLine
		("---- List method was Invoked SQL Currency conversion ----");
            Console.WriteLine("Provider Description ={0}", this.Description);
            Console.WriteLine("Connection string ={0}", this.ConnectionString);
            Console.WriteLine("List({0},{1},{2}) ", 
		Enum.GetName(typeof(Currency), cur), startDate, endDate);
            Console.ReadLine();
            return new CurrencyConversionItem[0];
            }
        }
    }    

In the previous code, one of the most important codes is Initialize(…) method. You should retrieve the minimum information that should be in the configuration file.

Step 3: Creating ProviderManger

Image 8

Moreover, to simplify the pattern, create a static class ProviderManager that has two static properties; Provider (for default provider), and Providers (for other providers that are registered in application configuration), to simplify the access in runtime.

History

  • 31st March, 2007: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) NSW Curriculum & Learning Innovation Centre
Australia Australia
I am a senior developer self taught,
the main study is electronics and communication engineering

I am working as senior programmer in center for learning and innovation
www.cli.nsw.edu.au

I develop Software since 1995 using Delphi with Microsoft SQL Server

before 2000, I didn’t like Microsoft tools and did many projects using all database tools and other programming tools specially Borland C++ Builder, Delphi, Power Builder
And I loved Oracle database for its stability until I was certified as Master in Database Administration from Oracle University.

I tried to work in web programming but I felt that Java is hard and slow in programming, specially I love productivity.

I began worked with .Net since 2001 , and at the same time Microsoft SQL Server 7 was very stable so I switched all my way to Microsoft Tech.
I really respect .Net Platform especially in web applications

I love database Applications too much
And built library with PowerBuilder it was very useful for me and other developers

I have a wide experience due to my work in different companies
But the best experience I like in wireless applications, and web applications.
The best Application I did in my life is Novartis Marketing System 1999 it takes 8 months developing with PowerBuilder and Borland C++, SQL Server
Performance was the key challenge in this Application.
The other 2 applications that I loved Multilingual Project in Scada company in Italy 2000 and SDP Mobile media content platform server for ChinaUnicom 2004
I hope that you enjoy any Article I post.
God bless you.

Comments and Discussions

 
GeneralMy vote of 4 Pin
Eng. Ali Hammoud22-Feb-11 4:28
Eng. Ali Hammoud22-Feb-11 4:28 
GeneralMy vote of 5 Pin
Sreek2Code6-Dec-10 6:01
Sreek2Code6-Dec-10 6:01 
GeneralThanks Pin
Klinkby6-Oct-07 12:00
Klinkby6-Oct-07 12:00 
The strategy pattern is a nice way to decrease coherence. I wrote a post you might find interesting. It takes decoupling to the next level: http://klinkby.wordpress.com/2007/10/05/transparent-and-type-safe-system-decoupling/
GeneralRe: Thanks Pin
Refky Wahib7-Oct-07 11:47
Refky Wahib7-Oct-07 11:47 
GeneralNice post Pin
malovicn31-Mar-07 14:32
malovicn31-Mar-07 14:32 
GeneralRe: Nice post Pin
Refky Wahib31-Mar-07 23:21
Refky Wahib31-Mar-07 23:21 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.