Click here to Skip to main content
Click here to Skip to main content

Provider Design Patterns in NET 2.0

By , 29 Oct 2008
Rate this:
Please Sign up or sign in to vote.

Table of Contents

  • Introduction
  • What are the benefits of the article?
  • Application Architecture
  • Application Configuration
  • Provider Implementation
  • Restoring Database
  • Debugging Code
  • Conclusion
  • References

Introduction

Provider Design Pattern is a new pattern that Microsoft formalized in ASP.NET Whidbey. The pattern was officially named in the summer of 2002 when Microsoft was designing Whidbey's new Personalization feature.

Benefits of Provider

  1. No need to explicitly instantiate classes. The .NET Framework will automatically manage class instantiation, including re-using classes that have already been instantiated. This will greatly improve the memory management of your application.
  2. Switching data sources is much easier. Changing the source of data for your application from the current database to any database, whether SQL Server, Oracle, XML, or other, is as simple as replacing your existing concrete (implementer) class with a new concrete (implementer) class and inheriting from your provider class. That is all. Your presentation and business logic layers remain unchanged, thereby reducing effort required to switch data sources and the amount of regression testing required thereafter.
  3. Learning the Provider Design concept will make it very easy to customize built-in .NET framework providers.

A provider implementation must derive from an abstract base class, which is used to define a contract for a particular feature.

For example, to create a Person provider for SQL, you create a new class SqlPersonProvider, which derives from PersonProvider. The base class of PersonProvider is ProviderBase. The ProviderBase class is used to mark implementers as a provider and forces the implementation of a required method and property common to all providers.

image32.png

What are the Benefits of the Article?

  • Understanding the implementation of Provider Design Pattern
  • Understanding the implementation of 3-tier architecture application
  • Understanding the application architecture
  • Naming Conventions

Note: I strongly suggest that you use the exact names that I use in this document to develop your solution for the purpose of learning this concept. Once you have tested and understood how it works, then you can integrate your own naming conventions.

Application Implementation

I developed an application, Phone Book, as a desktop application to describe the provider idea.

The phone book application was developed with the use of a 3 tier architecture as shown in the application architecture.

Database objects like tables, fields and stored procedures are represented by object oriented classes, properties and methods on the provider infos of CompanyName library.

image26.JPG

The presentation layer calls the result from business logic layer and then data retrieved from database by the use of providers implemented at data access layer library.

Solution Projects [ 4 Projects]

  1. BusinessLogicLayer: The business logic layer of the application
  2. CompanyName: It includes the global classes to all solution's projects
  3. DataAccessLayer: The data access layer of the application
  4. PhoneBookApplication: The presentation layer
image27.png

Application Architecture

The following figure depicts the common tiers of a distributed application. This document distinguishes between business data and the business processes that use the data; the business process tier is discussed only where needed for clarification. Likewise, the presentation tier is discussed only where there are direct implications for the way data is represented, such as the way Microsoft® ASP.NET Web pages expose business data. Read more.

image33.png

The following diagram describes the architecture of all solution's projects:

image28.png

The following diagram, ERD, is the database design of phonebook:
image29.png

The following diagram, Use-Case diagram, describes the main functionality of the application:

image30.png

Now, we can discuss the implementation of each project:

(1) Open Visual Studio 2005

  1. Create Windows Application and name it PhoneBookApplication
  2. Choose File Menu --> Add --> New Project and name it BusinessLogicLayer
  3. Choose File Menu --> Add --> New Project and name it DataAccessLayer
  4. Choose File Menu --> Add --> New Project and name it CompanyName

CompanyName

This project will include common libraries that are globally used in the development and shared objects among projects so you will add reference for CompanyName library to all other applications.

Example: I created the provider info classes to be shared among the "BussinessLogicLayer" and "DataAccessLayer" projects.

image5.png

(2) Add Reference to the below DLLs for the BusinessLogicLayer and DataAccessLayer

  • System.Web
  • System.Configuration
  • CompanyName

(3) Add References of "CompanyName" and "BusinessLogicLayer" to the desktop application

image6.png

(4) Configuring the App.config of the "PhoneBookApplication":

image8.png

If you look to the XML elements in app.config, we define sectiongroup named "PhoneBook" and added 2 sections to it which are "Person" and "Group".

<sectionGroup name="PhoneBook">
      <section name="Person" 
	type="CompanyName.PhoneBook.DataAccessLayer.SectionConfig, DataAccessLayer" />
      <section name="Group" 
	type="CompanyName.PhoneBook.DataAccessLayer.SectionConfig, DataAccessLayer" />
    </sectionGroup>

Then define that section group as:

<PhoneBook>
    <Person>
      <providers>
        <add name="SqlPerson" 
	type="CompanyName.PhoneBook.DataAccessLayer.SqlPersonProvider, DataAccessLayer" 
             connectionStringName="strcon" />
        <add name="OraclePerson" 
	type="CompanyName.PhoneBook.DataAccessLayer.OraclePersonProvider, 
	DataAccessLayer" 
         connectionStringName="OracleConnection" />
        <add name="AccessPerson" 
	type="CompanyName.PhoneBook.DataAccessLayer.AccessPersonProvider, 
	DataAccessLayer" 
             connectionStringName="AccessConnection" />
      </providers>
    </Person>
    <Group>
      <providers>
        <add name="SqlGroup" 
	type="CompanyName.PhoneBook.DataAccessLayer.SqlGroupProvider, DataAccessLayer" 
             connectionStringName="strcon" />
      </providers>
    </Group>
  </PhoneBook>

Then define the connection strings for data stores of "SQL, Oracle and Access":

<connectionStrings>
    <add name="strcon" connectionString="Data Source=.;
	Initial Catalog=AhmedEid_PhoneBook;Integrated Security=True" />
    <add name="OracleConnection" connectionString="oracle_connection_string" />
    <add name="AccessConnection" connectionString="Access_connection_string" />
  </connectionStrings>

Person provider could retrieve data from SQL, Oracle or Access database.
Group provider could retrieve data from SQL only

(5) Implementing DataAccessLayer

Suppose PhoneBook is a midsize business and the PhoneBookApplicaion contains 2 major sections:

  1. Person: Profile management
  2. Groups: Categorization of persons.

The following code reads all providers defined in your web.config. That is all you have to do to make providers information available to other classes.

using System;
using System.Configuration;
namespace CompanyName.PhoneBook.DataAccessLayer
{
    public class SectionConfig : ConfigurationSection
    {
        [ConfigurationProperty("providers")]
        public ProviderSettingsCollection Providers
        {
            get
            {
                return (ProviderSettingsCollection)base["providers"];
            }
        }
    }
}

We need to create another class to have access to the Framework provider collection and to add our new provider(s) to the provider collection.

using System;
using System.Configuration.Provider;
namespace CompanyName.PhoneBook.DataAccessLayer
{
    public class ProviderList : ProviderCollection
    {
        public override void Add(ProviderBase provider)
        {
            if (provider == null) throw new ArgumentNullException
		("The provider parameter cannot be null.");
            base.Add(provider);
        }
    }
}

Create another class that will initialize the provider by the provider name.

Example: Initialize the Group provider for SQLGroupProvider class using provider name of "SQLGroup".

using System;
using System.Collections.Generic;
using System.Text;
using System.Web.Configuration;
using System.Configuration;
using CompanyName.PhoneBook.DataAccessLayer;

namespace CompanyName.PhoneBook.DataAccessLayer
{
    public abstract class InitMember<T>
    {
        public static ProviderList Providers(string _providerSectionName)
        {
            SectionConfig qc = 
		(SectionConfig)ConfigurationManager.GetSection(_providerSectionName);
            providerCollection = new ProviderList();
            // this will instantiate PersonProvider with the class 
            // "personimpl" which inherit it
            ProvidersHelper.InstantiateProviders(qc.Providers, 
		providerCollection, typeof(T));
            providerCollection.SetReadOnly();
            return providerCollection;
        }

        private static ProviderList providerCollection;
    }
}

The class takes the provider you want to initialize, i.e. PersonProvider, its class and the provider name.

This enum founded on CompanyName.Globals class:

image13.png
using that class from the Instance method of PersonProvider class:

/// <summary>
/// This will initialize the provider and add instance to the providers list
/// </summary>
/// <param name="_Provider"></param>
/// <returns></returns>
public static PersonProvider Instance(Globals.Providers _Provider)
{
    return (DataAccessLayer.PersonProvider)DataAccessLayer.InitMember
       <DataAccessLayer.PersonProvider>.Providers
	("PhoneBook/Person")[_Provider.ToString()];
}

This method will initialize the provider and add instance to the providers list.

Now our "DataAccessLayer" project has the necessary classes for all providers to be later developed.

So we are going to develop two providers:

  • PersonProvider
  • GroupProvider

Now, we create our provider model:

BaseProvider --> xxxProvider --> SQLxxxProvider xxx is the name of entity like Person, Group, ....etc.

Person Provider Classes:

image9.png

Group Provider Classes:

image10.png

Let me explain the implementation of the PersonProvider and you can create GroupProvider by yourself: first we create a class named "PersonProvider.cs":

using System;
using CompanyName.PhoneBook.Providers;
using System.Configuration.Provider;
using System.Configuration;
using System.Web.Configuration;

namespace CompanyName.PhoneBook.DataAccessLayer
{
    public abstract class PersonProvider : ProviderBase
    {
        /// <summary>
        /// This will initialize the provider and add instance to the providers list
        /// </summary>
        /// <param name="_Provider"></param>
        /// <returns></returns>
        public static PersonProvider Instance(Globals.Providers _Provider)
        {
            return (DataAccessLayer.PersonProvider)DataAccessLayer.InitMember
                <DataAccessLayer.PersonProvider>.Providers
		("PhoneBook/Person")[_Provider.ToString()];
        }
        /// <summary>
        /// Add new person
        /// </summary>
        /// <param name="_info"></param>
        /// <returns></returns>
        public abstract bool Add(PersonInfo _info);
        /// <summary>
        /// Modify selected person
        /// </summary>
        /// <param name="_info"></param>
        /// <returns></returns>
        public abstract bool Modify(PersonInfo _info);
        /// <summary>
        /// Delete selected person
        /// </summary>
        /// <param name="_PersonId"></param>
        /// <returns></returns>
        public abstract bool Delete(int _PersonId);
        /// <summary>
        /// Get all persons
        /// </summary>
        /// <returns></returns>
        public abstract PersonInfo[] Find();
        /// <summary>
        /// Get info of person
        /// </summary>
        /// <param name="_PersonId"></param>
        /// <returns></returns>
        public abstract PersonInfo GetInfo(int _PersonId);
        /// <summary>
        /// Get persons that match a given criteria
        /// </summary>
        /// <param name="_Searchinfo"></param>
        /// <returns></returns>
        public abstract PersonInfo[] Find(SearchCriteriaInfo _Searchinfo);
    }
}

Instance() method is responsible for instantiating our concrete (implementer) class (SqlGeneralProvider.cs), which has been defined within Web.config and "PersonProvider" abstract class, which it inherits from ProviderBase and includes person abstracted functions.

(6) BusinessLogicLayer

image14.png

Note: It is a good idea to add a Helper class into your BusinessLogicLayer project. This way, you can expose some common functionality to all of your BusinessLogicLayer classes just by inheriting from this helper class.

using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
namespace CompanyName.PhoneBook.BusinessLogicLayer
{
    /// <summary>
    ///  You can use helper to provide common info./data 
    /// needed OR to massage or add more info. 
    ///  to your data before sending it to presentation.
    /// </summary>
    public abstract class Helper
    {
        protected static string MachineName
        {
            get
            {
                return Environment.MachineName;
            }
        }
    }
   
    // Add more methods/properties below
}

Then define the business classes for the Person and Group providers.

Person.cs

using System;
using System.Collections.Generic;
using System.Text;
using CompanyName.PhoneBook.Providers;
using CompanyName.PhoneBook.DataAccessLayer;

namespace CompanyName.PhoneBook.BusinessLogicLayer
{
    public abstract class Person : Helper
    {
        static PersonProvider objPersonProvider;
        /// <summary>
        /// Person Constructor
        /// </summary>
        
        //Class Constructor: will be invoked 1 time only / Appdomain
        static Person()
        {
            objPersonProvider = PersonProvider.Instance(Globals.Providers.SqlPerson);
        }
        // static methods for person
        /// <summary>
        /// Add new person
        /// </summary>
        /// <param name="_info"></param>
        /// <returns></returns>
        public static bool Add(PersonInfo _info)
        {
            // You can use helper to provide common info./data needed OR to
            // massage or add more info. to your data before sending it to
            // presentation.
            // Here we use helper class to get MachineName and pass it along
            // with data to presentation.
            return objPersonProvider.Add(_info);
        }
        /// <summary>
        /// Modify selected person
        /// </summary>
        /// <param name="_info"></param>
        /// <returns></returns>
        public static bool Modify(PersonInfo _info) 
        {
            return objPersonProvider.Modify(_info);
        }
        /// <summary>
        /// Delete selected person
        /// </summary>
        /// <param name="_PersonId"></param>
        /// <returns></returns>
        public static bool Delete(int _PersonId) 
		{ return objPersonProvider.Delete(_PersonId); }
        /// <summary>
        /// Get all persons
        /// </summary>
        /// <returns></returns>
        public static PersonInfo[] Find() { return objPersonProvider.Find(); }
        /// <summary>
        /// Get info of person
        /// </summary>
        /// <param name="_PersonId"></param>
        /// <returns></returns>
        public static PersonInfo GetInfo(int _PersonId) 
		{ return objPersonProvider.GetInfo(_PersonId); }
        /// <summary>
        /// Get persons that match a given criteria
        /// </summary>
        /// <param name="_Searchinfo"></param>
        /// <returns></returns>
        public static PersonInfo[] Find(SearchCriteriaInfo _Searchinfo) 
		{ return objPersonProvider.Find(_Searchinfo); }
    }
}

Define objPersonProvider as PersonProvider to be shared among the instances of Person class. Then initialize it on the class constructor which will be invoked for one time/ Appdomain.

Person objPerson_1 = new Person();  // on class A
Person objPerson_2 = new Person();  // on class B 
Person objPerson_3 = new Person();  // on class C 
//Class Constructor: will be invoked 1 time only / Appdomain
static Person()
{
       objPersonProvider = PersonProvider.Instance(Globals.Providers.SqlPerson);
}

The objPersonProvider will be initialized with the objPerson_1 constructor only on the class A, then other instances like objPerson_2 or objPerson_3 will use the static object objPersonProvider. You can add your business logic to every method before calling the data access provider as:

public static bool Add(PersonInfo _info)
{
  // Here: you can add you business logic to every method 
  // before calling the data access provider
  return objPersonProvider.Add(_info);
}

PhoneBook Application

The presentation layer as desktop application for implementing the business logic to the client.

image31.png

How to use the BL layer from the presentation layer ? Sample code

// build the search criteria
SearchCriteriaInfo objSearchInfo = new SearchCriteriaInfo();
objSearchInfo.FilterNameBy = (Globals.FilterNameBy)this.cboName.SelectedValue;

objSearchInfo.GroupId = (int)cboGroup.SelectedValue;
objSearchInfo.Name = txtName.Text.Trim();
if(chkFrom.Checked)
    objSearchInfo.FromDate = datefrom.Value;
if(chkTo.Checked)
    objSearchInfo.ToDate = dateto.Value;

objSearchInfo.SortBy = (rdoasc.Checked) ? Globals.SortBy.Asc : Globals.SortBy.Desc;
objSearchInfo.SortByBirthDate = chkbirthdate.Checked; ;
objSearchInfo.SortByGroup = chkGroup.Checked;
objSearchInfo.SortByName = chkName.Checked;
objSearchInfo.SortByTele = chkTele.Checked;

objSearchInfo.TeleNumber = txtTeleNumber.Text.Trim();
objSearchInfo.TelephoneType = (Globals.TelephoneTypes)this.cboTeleTypes.SelectedValue;

// get result from bus logic layer
PersonInfo[] objresult = Person.Find(objSearchInfo);
image12.png

image17.png

How to save and retrieve image from database ?

Save to database:

Save the image from the picturebox to MemoryStream and then save it on database as Byte[]:

// prepare the image
if (null != imgperson.Image) // picturebox control 
{
    MemoryStream stream = new MemoryStream();
    imgperson.Image.Save(stream, System.Drawing.Imaging.ImageFormat.Jpeg);
    info.Image = stream.ToArray();
}
Retrieve from database:
 // display the image
 byte[] PImage = info.Image;
 if (null != PImage)
 {
     MemoryStream stream = new MemoryStream(PImage);
     imgperson.Image = Image.FromStream(stream);
 }

image18.png

Restoring Database on SQL Server 2005

Attach the database AhmedEid_PhoneBook.mdf from the directory DataBase on the root directory to the SQL Server Engine.

Debugging Scenario

Let us debug the advanced search button code to retrieve results with criteria from the client.
The following scenario will lead you to understand the Provider implementation.

Make breakpoints as the following:

1. AdvancedSearch form class ( presentation layer )

image19.png

2. Person class of BusinessLogicLayer ( BLL layer )

image20.png

3. PersonProvider Class of DataAccessLayer ( DAL layer )

image21.png

4. InitMember<T> class of DataAccessLayer ( DAL layer )

image22.png

The above code will provide and add the SqlPersonProvider class to the providers list to be available for any usage.

image24.png

5. SqlGroupProvider class of DataAccesslayer ( DAL layer )

image23.png

Note: After debugging the code for the 1st time, the providers (PersonProvider and groupProvider) will be added to the System.Configuration.Provider.ProviderCollection and for 2nd time the results will be retrieved without instantiating the classes again.

image25.png

References

License

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

About the Author

Ahmad Eid Salim
Software Developer (Senior)
Egypt Egypt
No Biography provided

Comments and Discussions

 
QuestionA bit complicated. PinmemberMember 1019633819-Feb-14 3:32 
Questiondreader = cmdsp.exectureader() PinmemberHyrmetMy30-Apr-12 7:00 
AnswerRe: dreader = cmdsp.exectureader() PinmemberHyrmetMy30-Apr-12 7:29 
GeneralError Pinmembernguyenvanthangtt15-Sep-10 8:37 
Generalgood article PinmemberDonsw5-Feb-09 7:47 
Thisis a good article but I think if yo uare looking to handle multiple datbase there might be better patterns.
GeneralTry another link PinmemberSohel_Rana11-Nov-08 16:03 
GeneralRe: Try another link PinmemberAhmad Eid Salim11-Nov-08 21:07 
GeneralThanks and well done Pinmemberwboghdady7-Nov-08 23:04 
GeneralUsing the Provider Pattern Pinmemberjimibt4-Nov-08 0:21 
GeneralUnity Application Block PinmemberUrs Enzler3-Nov-08 5:11 
GeneralWell documented... PinmemberBAIJUMAX3-Nov-08 4:20 
Generalabstract factory Pinmemberzhaojicheng3-Nov-08 3:10 

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

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

| Advertise | Privacy | Mobile
Web03 | 2.8.140415.2 | Last Updated 29 Oct 2008
Article Copyright 2008 by Ahmad Eid Salim
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid