.Net Provider Pattern - Designing decoupled and extensible Component for .Net Application
Introduction
Provider Design pattern of .Net Framework 2.0 is one of the most exciting way to design component in decoupled and extensible way. In my today’s post, I will discuss about this design pattern and how we can use it to make the components inside our applications decoupled while providing extension point for our components and finally I will discuss some of the great advantages of this design pattern. Hence , sit back tight , I will give you a quick tour on Provider pattern which I divided in following sections –
1. What is Provider Design Pattern ?
2. Provider Pattern – Nuts & Bolts in Nutshell
3. Advantages of Provider Design Pattern
4. Last few words…
1. What is Provider Pattern?
Provider pattern is a way in .Net Framework 2.0 to design extensible and decoupled Component. Ron Howard mentioned -
“A provider is simply a contract between an API and the Business Logic/Data Abstraction Layer. The provider is the implementation of the API separate from the API itself.”
Provider Pattern is a way to get rid of the coupling among the components while making the components extensible. Main reason for our today's discussion on Provider Pattern is its wonderful ability to publish the API and at the same time make the API pluggable; that is - it gives us the flexibility to choose the API that is best suited for the application rather than the one developed by API developer. And from an API developer perspective, it allows them to create an extension point for the API where clients of the framework can extend the functionality in their own way.
In the next section, we will see how we can build our component using Provider Design Pattern -
2. Provider Pattern - Nuts & Bolts in a Nutshell
The basic idea behind the provider pattern is to have multiple concrete providers and selecting one of them depending on configuration (just a change in *.config file can lead to completely different provider to perform the operation) at the runtime by avoiding writing huge amount of code and coupling among the components. Provider pattern completely abstract the decision of which provider to use out of programming interface. That way we can say that, it has some kind of dependency injection/inversion flavor, but it does not use any kind of container like Windsor or Spring.net.
First, let’s consider our problem domain that we will be using throughout today's discussion. For the sake of simplicity, we have the following simple domain object- User and we need a persistence media to store(/Save) and retrieve(/Get) it –
Figure1 : User Domain Object
So, the class responsible for communicating with the physical persistence media -
PersistenceManager has only 2 job now –
public static void Save<T>(T obj)
public static T Get<T>()
Currently we are only supporting two Persistence media - SQL Server2005 and Xml (In File system). Depending on the clients’ need, we will be using any one of them at the runtime, keeping in mind that in future client might use some other persistence media like Oracle , mySql or whatever.
If you are not familiar with provider pattern , you would have solved this problem using any dependency injection container (e.g. Windsor/Spring.Net/Unity) , or introducing factory method to instantiate the desired component at the runtime; that is, either you had to introduce new code (write & manage) or new vocabulary to grasp( with dependency injection container) while providing your custom solution. Truly that would be an overhead if we would like to solve ONLY this problem. Then, why not use something from Framework when Framework is providing it (e.g. Asp.net Membership)?
Here, I will show you how you can solve it using Provider Design Pattern and .Net Framework 2.0 very easily.
Provider design pattern consist of following basic parts –
API class (PersistenceManager) to publish API( save and get). It is also responsible to instantiate a concrete provider depending on the configuration.
Domain specific Abstract Provider (PersistenceProviderBase) aka Application ProviderBase inherited from ProviderBase class of System.Configuration.Provider namespace.
Concrete Providers (XmlPersistenceProvider and SqlPersistanceProvider) inherited from domain specific Abstract Provider.
Custom Configuration Section to configure the Providers and a class inherited from ConfigurationSection to represent it in .Net.
The following diagram shows different parts of provider design pattern that I will clarify one by one –
Figure2 : Basic Parts of Provider Design Pattern
Let's start with the last part, i.e. , Custom Configuration Section -
Custom Configuration Section
In order to make the provider pattern pluggable and flexible, it is quite obvious that we need to implement a means by which we can configure the provider at the runtime. Application configuration is the most easy and convenient way to link the communication between our Providers and API.
First we need to add a class inherited from ConfigurationSection to handle the configuration of the providers or we can implement :
Figure3 : Class to store Custom Configuration Section
Following is the custom section for the Persistence Provider:
Listing1.1 : Custom Configuration Section: 1:
2: <PersistenceProvider defaultProvider="XmlPersistenceProvider">
3: <providers>
4: <add name="XmlPersistenceProvider" type="ProviderPattern.XmlPersistenceProvider, ProviderPattern" PersistenceMediaConnectionString="C:\PersistenceMedia\DocumentInfos.xml"/>
5: <add name="SqlPersistenceProvider" type="ProviderPattern.SqlPersistenceProvider, ProviderPattern" PersistenceMediaConnectionString="[Your database connection string]"/>
6: </providers>
7: </PersistenceProvider>
Important attributes to note here:
1. defaultProvider : Refers to the default Concrete Provider.
2. PersistenceMediaConnectionString : Defined to provide extra information needed to the Concrete Provider. For example , XmlPersistenceProvider will save the information about the domain object in the path specified and SqlPersistenceProvider use the value specified as the connection string to the database.
We can also define as many other attributes as needed that can be used while initializing the concrete provider( e.g. logFilePath). Benefit is, we can come back to the configuration and change it at the runtime and Concrete Provider will be updated accordingly.
Now let's move on and explore the API class whose responsibility is to initialize the concrete provider depending on the configuration section and publish the API.
API Class
API class is the major gateway to the concrete provider as I mentioned earlier that it communicates directly with the underlying persistence media using one of the concrete provider to perform the operation (e.g. Save/Get) that is exposes –
Listing2.1 : Save Method of API Class:
public static void Save<T>(T obj)
{
if (DefaultProvider == null)
{
Instantiate(
PersistenceProviderConfigSection.GetPersistenceConfigSection());
}
DefaultProvider.Save<T>(obj);
}
Listing2.2 : Get Method of API Class:
public static T Get<T>()
{
if (DefaultProvider == null)
{
Instantiate(PersistenceProviderConfigSection.GetPersistenceConfigSection());
}
return DefaultProvider.Get<T>();
}
If we look at the PersistenceManager closely, the static Instantiate method, that instantiate the provider based on the Configuration from PersistenceProviderConfigSection. In order to do that, we have a built in support in the .Net Framework2.0 , that is, we will use ProvidersHelper class of System.Web.Configuration namespace. The ProvidersHelper.InstantiateProviders method initializes the concrete providers by calling the Intialize() method of the Concrete Providers (I will come back to this point in the next section again).
Listing2.3 : Instantiation of the Providers :
private static void Instantiate(PersistenceProviderConfigSection configSection)
{
if (Providers == null)
SetupProvider(configSection);
}
private static void SetupProvider(PersistenceProviderConfigSection config)
{
if (config == null)
throw new Exception("PersistenceProvider are not configured to be used with this application");
Providers = new PersistenceProviderCollection();
ProvidersHelper.InstantiateProviders(config.Providers, Providers, typeof(PersistenceProviderBase));
DefaultProvider = Providers[config.DefaultProvider] as PersistenceProviderBase;
}
Hence, the DefaultProvider property in the will always refers to the default Concrete provider mentioned in the configuration. Now, we will dive into the details of the Concrete provider and how it gets initialized when we call ProvidersHelper.InstantiateProviders(config.Providers, Providers, typeof(PersistenceProviderBase)).
Note: If you are thinking: why would we add a reference to System.Web.Configuration namespace if it is not a web application, then you are right ;) ? However, we can easily implement class we need to avoid adding reference - ProvidersHelper.( We will come back to this point later.)
Now, let's move to the most important 2 parts of the Provider Design Pattern that is ( you already know isn't it :) ?) - Application speciProviderBase and Concrete Providers .
Application ProviderBase and Concrete Providers
In shortcut, Application ProviderBase class/ Domain specific Provider Base provides the abstract version of the functionality exposed by the API class and the responsibilities of the concrete provider is to implement those API in their own way. As we can see from Figure 4 our Application ProviderBase - PersistenceProviderBase implements IPersistenceProvider which noting but describes the API exposed by our API class.
Figure4 : Application ProviderBase class - PersistenceProviderBase
About the ProviderBase - a class of the Net framework's System.Configuration.Provider namespace which have all the members to be implemented by the concrete providers.
It contains properties to describe the concrete providers – Name and Description. Initialize() is used to initializes the provider.PersistenceProviderBase is the mirror of the API or Services that will be performed by Concrete Provider. It is the place to implement the members generic to the all concrete provider.
Concrete Providers overrides with the concrete implementation(Save/Get of the User) of the abstract API provided in the PersistenceProviderBase class.
Figure5 : Concrete Providers and their members
In addition to that, in the Initalize() method, concrete providers also initialize the properties that are related to the concrete providers and configured through web.config. For example,
- _PersistenceMediaPath of the concrete providers will be initialized with the value ofPersistenceMediaConnectionString from web.config.
- Name and Description will be initialized.
Following is the code related to the Initialize of SqlPersistenceProvider :
Listing3.1 : Initialize() of Concrete Provider:
public override void Initialize(string name, NameValueCollection config)
{
name = string.IsNullOrEmpty(name) ? "SqlPersistanceProvider" : name.Trim();
base.Setup("SqlPersistanceProvider", "SqlPersistanceProvider saves the T to SQL DB", config);
_PersistenceMediaPath = config[PersistenceMediaPathKeyName];
//Call the base class to initialize
base.Initialize(name, config);
}
Following diagram shows the Call Stack how the Concrete Providers' Initialize() method get called from the API class when first time it initializes the Providers:
Figure6 : Call Stack - How all the concrete providers get initialized from API class
After initialization, we are only left with the Provider-specific implementation of the abstract functionality of the PersistenceProviderBase - Save/Get. Every provider will implement in their own way. For example , if we consider XmlPersistenceProvider , it will implement the Save/Get as Listing 3.2 & 3.3, where as , SqlPersistenceProvider will store and retrieve it from Sql Server -
Listing3.2 : XmlPersistenceProvider's Save method
public override void Save<T>(T obj)
{
string serailzedObj = SerializeHelper.Serialize(obj);
File.WriteAllText(_PersistenceMediaPath, serailzedObj);
}
Listing3.3 : XmlPersistenceProvider's Get method
public override T Get<T>()
{
string stringContent = File.ReadAllText(_PersistenceMediaPath);
if (!String.IsNullOrEmpty(stringContent))
return ((T)SerializeHelper.Deserialize(typeof(T), stringContent));
return default(T);
}
Hence , we are done with implementation of the Provider Design Pattern. In the next section, we will look into some benefits that we will achieve by leveraging this Pattern.
3. Advantages of Provider Design Pattern
Here comes, moment of truth. Why would you implement Provider Design Pattern - to create loosely coupled components that are extensible from an application perspective.
If we go back to Listing1.1 where we defined our custom configuration section, we set XmlPersistenceProvider as the default PersistenceProvider. Now, due to any reason, client of our API wants to use SqlPersistenceProvider that we provided with our API. What needs to be done , just change in the value of the defaultProvider in the web.config file and our client can start using the SqlPersistenceProvider.
Listing4.1 : Configuration Section with defaultProvider = SqlPersistenceProvider
1: <PersistenceProvider defaultProvider="SqlPersistenceProvider">
2: <providers>
3: <add name="XmlPersistenceProvider" type="ProviderPattern.XmlPersistenceProvider, ProviderPattern" PersistenceMediaConnectionString="C:\PersistenceMedia\DocumentInfos.xml"/>
4: <add name="SqlPersistenceProvider" type="ProviderPattern.SqlPersistenceProvider, ProviderPattern" PersistenceMediaConnectionString="[Your database connection string]"/>
5: </providers>
6: </PersistenceProvider>
Now, due to some reason, our Client wants to develop provider based on , let's say, MySql. That is also pretty simple. They just need to create a Concrete Provider, MySqlPersistenceProvider inheriting from Application ProviderBase(Figure7) and implementing the APIs by overrriding. In addition, They need to add a <add name = "MySqlPersistenceProvider" ... in the Custom Configuration Section, and they are done. :) Isn't it simple ? :)
Figure7 : Extending PersistenceProviders
Listing 4.2 : Configuration Section with MySqlPersistenceProvider
1: <PersistenceProvider defaultProvider="MySqlPersistenceProvider">
2: <providers>
3: <add name="XmlPersistenceProvider" type="ProviderPattern.XmlPersistenceProvider, ProviderPattern" PersistenceMediaConnectionString="C:\PersistenceMedia\DocumentInfos.xml"/>
4: <add name="SqlPersistenceProvider" type="ProviderPattern.SqlPersistenceProvider, ProviderPattern" PersistenceMediaConnectionString="[Your database connection string]"/>
5: <add name="MySqlPersistenceProvider" type="ProviderPatternExplored.MySqlPersistenceProvider, ProviderPatternExplored" PersistenceMediaConnectionString="[Your my sql database connection string]"/>
6: </providers>
7: </PersistenceProvider>
Using the same process, they can implement any PersistenceProvider they need. Moreover, in future, if we want to add any new Providers, it's just a piece of cake ;). That's make our component very extensible conforming to the Open-Close Principle of OOP.
Last but not the least; these loosely coupled and extensible components results in a less code to manage and less vocabulary to maintain, since framework is providing the basis for the Provider Design Pattern. Moreover, it gives your team members a familiarity with the existing built-in Providers in .Net Framework.
4. Last few words…
In this example, we saw how we can leverage .Net Provider Design Pattern to make a component loosely couple and extensible.
As I mentioned earlier also, one of the catch implementing provider pattern is that, we have to use ProvidersHelper class of System.Web,Configuration while building the component. But we can easily get rid of adding reference to System.Web namespace, by implementing ProvidersHelper class very easily. (I will try to cover it in one of my next post.)
I tried to clarify the implementation details of Provider pattern in this article , however, still if there are any questions regarding this article, please feel free to ask me. I would really appreciate your feedback.