Introduction
One thing that is constant is change. This is especially true for the software industry where change is very often and swift. The solutions we create today are challenged by each new technology, but building a solution from scratch to incorporate these new developments is not always feasible. Software architects have learned this early in the game and started abstracting the business behavior from its implementation to protect their solutions from change.
The ASP.NET team was also faced with many challenges while working toward the ambitious goal of reducing the development time for web developers. They wanted to create a solution that is not only simple and elegant but also extendable. More specifically, they wanted a scalable, easy-to-use, well-documented system with full control over its internal implementations. The result is a design termed the provider model that allows you to extend or change the behavior of an application or control based on business requirements.
At the first glance, the provider model appears as an enhanced version of the abstract factory pattern . In reality it is more than that. It is a system in itself that incorporates a number of patterns like the singleton , the strategy , and the component configurator pattern . The provider model itself is not new; a number of applications, most notably DotNetNuke and Log4Net, have been successfully using it for quite some time. ASP.NET 2.0 standardizes the approach and brings it to mainstream development.
In the text to come, I explain where you should use the provider model. Then I explore the particular way that ASP.NET 2.0 Beta 2 implements this model. To do this I build a Payment Gateway solution based on the provider model. Next, I look into the issues you will find when using the provider model, and finally I introduce a provider model framework for ASP.NET 1.x, mimicking it against ASP.NET 2.0 so you can start using it today and be able to port the code to ASP.NET 2.0 when it goes live.
This article assumes you are familiar with design patterns, especially the Abstract Factory Pattern and Singleton pattern. If not, I would encourage you to skim through the links in the "Related Links" section at the end of this article.
System Requirements
To run the sample code for the sample, you need
- .NET Framework 2.0 Beta 2 (CTP April 2005)
- Visual Studio 2005 or Visual Studio 2005 Express Editions (Web Developer)
- Visual Studio 2003
- Internet Explorer
You can download Visual Studio 2005 Express Editions from http://lab.msdn.microsoft.com/express/vwd/default.aspx.
The Sample Code
All the sample code for this article has been written in C#. The sample download for this article contains two solutions.
- A VS .NET 2005 solution containing an online ordering web application. The App_Code folder has all the classes that implement the Payment Gateway providers. The web project also has the credit card control that uses these providers.
- A VS.NET2003 solution contains two projects named ProviderModel and TestProviderModel . The first is a library that allows you to implement the provider model in .NET1.x. The TestProviderModel uses this library to implement the payment processor solution. This project further uses the same classes as defined in the ProviderGateway project for .NET 2.0. As you will notice, there are no changes between the two code sets, they are just compiled for different runtimes. The test project is built as a console application that shows that the provider model could be used both for Windows and Web applications.
Disadvantages of the Abstract Factory Pattern
Software's success is directly proportional to its extensibility features. If the software is independent of how its constituent pieces are created, composed, and presented, the solution will be more maintainable going forward. This loose coupling will allow us to expand the solution as needed in the future. Consider the situations where
- The system specifications are not clear and continuously changing.
- The system needs to handle objects of same type but the actual type is known only at runtime.
- The system implementation will be extended over a period of time.
These are very common requirements, and people have solved this problem in a number of different ways. The Gang of Four (see the "Related Links" Section) spotted a pattern in solutions for these problems. They named it the Abstract Factory Pattern . The intent of this pattern is
"To provide an interface for creating families of related or dependent objects without specifying their concrete classes"
The Abstract Factory Pattern provides an abstraction layer, but there are number of limitations with this solution, most importantly
- It is a code-based solution. The factory class needs to know what concrete classes are available that could be created. If the solution is extended by adding new implementations of the interface, then at minimum you have to update the factory class from the core.
- The use of an interface-based contract approach limits the solution's adaptability. If the interface is required to be changed, all the old code will break; thus we cannot easily add new functionalities in the core implementation.
The Solution - The Provider Model
The ideal solution is the one where we do not have to change the core implementation of the factory class each time we add a new implementation of the interface. We can also use the chain of inheritance to publish the common API to protect ourselves from having to make contract updates.
The provider model does exactly this. It uses standard application configuration files to add, clear, or remove a concrete implementation to the solution without recompiling the core. This creates a plug-and-play architecture that can be extended as needed. For instance, ASP.NET 2.0 provides a SQL and Access provider for memberships that are interchangeable. But it does not stop here; if you were porting an existing application from classic ASP or ASP.Net 1.x and already have a custom Membership database, then you do not need to redesign it; you can create a custom Membership provider and use it instead of the out-of-box providers and still reap the benefits of all the features of ASP.NET 2.0.
The provider model uses the provider to publish the contract or the well-defined API just like the interface in the Abstract Factory Pattern. The model also allows full control to the implementer in implementing this API. Before I show how ASP.NET 2.0 uses this model, you need to understand the inner workings of the model.
To get a hold on new technology is to apply it. To demonstrate how to implement a provider model–based solution, I show you how to create a simple credit card user control using the provider model. The control prompts for credit card information and processes the transaction using one of the Payment Gateway providers. The provider for this control supports charge and refunds only. I create two providers, one of them ( PayPalPaymentGatewayProvider ) supporting processing of Visa, MasterCard and American Express cards only, and the other ( ANetPaymentGatewayProvider ) supporting these and additionally the Discover card. The providers are very simple and do not take into account complexities such as merchant verification. Extra logic in the control enables choosing the correct provider based on the card type. Before I jump into the implementation of the solution, let's see how the finished product looks. Figure 1 shows the rendered control in default mode, if you try to charge a MasterCard or Visa card, it processes the request using the PayPalPaymentGatewayProvider , as indicated by the returned transaction ID, displayed at the bottom.

Figure 1. The rendered credit card control - charging a Visa card
When you try to charge a Discover card, the control automatically chooses the ANetPaymentGatewayProvider as shown in Figure 2.

Figure 2. The rendered credit card control - charging a Discover card
Since this article is concentrated on the provider model, I discuss only the relevant provider model sections of the code. However, the full source code for the control is available in the download.
The Implementation
ASP.NET 2.0's implementation of the provider model is very straightforward. However, it does set certain rules that must be followed to implement it. As I develop the sample, I discuss each of the following rules:
Rule 1: Well-Defined Contract
The provider must define the contract using an abstract class that must be inherited from System.Configuration.ProviderBase . The framework uses the ProviderBase class for all the internal plumbing to support the provider infrastructure (provider caching and configuration). By inheriting from this class, it automatically becomes part of the provider model built inside the framework.
The provider (contract class) should define all the methods and properties that it requires to implement the core functionalities as the public API. The base class ( ProviderBase ) should have only the minimum set of required features, to encourage the implementers to provide real value in their concrete providers.
Following this rule, let's create the PaymentGatewayProvider class as seen here:
public abstract class PaymentGatewayProvider : ProviderBase
{
public abstract string ChargeCard(CreditCard card, double amount);
public abstract bool Refund(CreditCard card, double amount);
public abstract string[] SupportedCardTypes{get;}
}
The provider class is abstract and inherits from ProviderBase . The class publishes the API that the control will use. The implementation of this API is independent of the control's core functionality. I can go ahead and create the control without worrying about the back-end.
Rule 2: Unique Name
Each provider has to have a unique name. ASP.NET 2.0 supports more than one provider for a component. For example, for the PaymentGatewayProvider you can have credit card processors like Authorize.NET, PayPal, or any other leading bank and create a concrete provider for each kind of processor. The framework will then let you make a call to any one or all of them using the name of the provider. The provider's name is used as an identification key, therefore it should be unique. By virtue of inheriting from ProviderBase , the provider has a read-only Name property. All of the concrete implementations have to override it and return the appropriate name that uniquely identifies the provider.
Rule 3: Configuration Information
The provider configuration information, like available providers and their initialization values, should be maintained in an application configuration file ( Web.config for ASP.NET applications and App.config for Windows applications). The concrete implementation of the provider should know how to initialize itself.
Once the provider for a component or application feature is created, you have to describe it in the configuration file before it can be used. You can define an unlimited number of providers in the configuration section for the same feature. A provider can itself have initialization values or use configuration files to configure itself. The provider model configuration syntax allows you to define the values as name-value pairs right within the declaration. The general syntax to describe the provider is shown here:
<configSections>
<section
name="sectionName"
type="SectionHandlerType, Assembly" >
</section>
</configSections>
<sectionName default = "ProviderName" Attribute = "Any Attribute">
<providers >
<clear/>
<add
name="ProviderName"
type="ProviderType"
Attribute="Any Additional Information"
/>
<remove name="ProviderName"/>
</providers>
</sectionName>
To create a provider section, you first have to inform the framework how you will be handling it. This is done by defining a section inside the configSections tag. You can name the section whatever you like. The provider section has to follow the structure as defined in the preceding code. The top node for the section will be sectionName , and it can have any number of attributes. Ideally, it is used to define the name of the default provider as set by default attribute. The child element providers are to group all the available providers. Providers can be added to the group using the add element. This element has two compulsory attributes named name and type . The name attribute holds the name of the provider, and type holds the full type information of the provider in the form of "class, assembly". You can define any other configuration data as a key-value (name-value) attribute of the add element.
Note: If your provider needs a connection string that is shared among other parts of the application, do not define it as an element's attribute, rather use the connectionString element in the configuration file.
ASP.NET caches all of the configuration data for quick access. Since the provider model relies on existing functionality, you need a way to remove or clear the provider collection. That is where the remove and clear tags come into the picture. To remove a provider from the list, use the remove tag. It has name as the only required attribute. The value of the name attribute should be one of the provider's name as defined within an add tag. The clear tag clears the entire list. If you use the clear tag, then it should be the first child tag of the providers tag.
Note: The .NET Framework caches the providers in memory for fast access. If you need to remove a provider, do not just comment it out in the configuration file, rather use the remove tag, which will force the framework to flush that provider from cache.
For this application, I create two providers named PayPalPaymentGatewayprovider and ANetPaymentGatewayProvider . I discuss this code in next section. First, I define the configuration section for these two providers in the web.config file. Following the previously mentioned rules, I can configure the providers as shown here:
<configSections>
<section name="paymentGateway"
type="cpSphere.Articles.PaymentGatewaySection, __code"/>
</configSections>
<paymentGateway default="PayPalPaymentGatewayProvider">
<providers>
<add name="PayPalPaymentGatewayProvider"
type="cpSphere.Articles.PayPalPaymentGatewayProvider, __code"
supportedCardTypes ="Visa,MasterCard"
endPoint="https://paypal.com"
accountEmail="test@testAccount.com"/>
<add name = "ANetPaymentGatewayProvider"
type ="cpSphere.Articles.ANetPaymentGatewayProvider, __code"
userName="MyName"
password ="myPassword"
supportedCardTypes ="Visa,MasterCard,Discover"/>
</providers>
</paymentGateway>
The configSections defines each paymentGateway in the providers section. It further sets the PaymentGateway section defined in classes stored in the App_Code folder as the section handler. Note that there are three custom attributes, endpoint , accountEmail , and supportedTypes , for PayPalPaymentGatewayProvider , which are used to initialize the provider. endPoint is used to define the payment processor's access point, accountEmail is the account identifier, and supportedType is a comma-delimited list of credit card types that the processor supports. Similarly, I have username , password , and supportedType attributes for the ANetPaymentGatewayProivider . As you have guessed, it is not necessary that every provider has the same kind of attributes. Each provider should have attributes that are relevant to its implementation. Notice the default attribute of the paymentGateway element. It lets you set the default or selected processor, i.e., you can change the behavior of the Payment Gateway solution by setting this attribute. If you set it to PayPalPaymentGatewayProvider , it will be used as the default; setting it to any other value will force the factory class to use a new provider for the transactions.
Note: ASP.NET 2.0 introduces the concept of dynamic code compilation. You can drop the code file in a special folder named App_Data and the framework compiles it and makes it available to the application. This dynamic assembly is named __code . This name is used in the web.config file to reference the configuration sections.
The configSection attribute defines cpSphere.Articles,PaymentGatewaySection as the class that handles the configuration section request for the paymentGateway section. The next step is to create this class. In ASP.NET 1.x, you had to create a configuration section handler class that would implement the System.Configuration.IConfigurationSectionHandler interface. This is still supported, but we have a better way to accomplish the same thing. We can create a class that inherits from the System.Configuration.ConfigurationSection class. The advantage is that you do not have to parse the XmlNode , and you can access the attributes as properties. The following listing shows the PaymentGatewaySection class that handles the paymentGateway section:
public class PaymentGatewaySection : ConfigurationSection
{
[ConfigurationProperty("default",
DefaultValue = "PayPalPaymentGatewayProvider")]
public string DefaultProvider
{
get { return (string)base["default"]; }
set { base["default"] = value; }
}
[ConfigurationProperty("providers")]
public ProviderSettingsCollection ProviderSettings
{
get {
return base["providers"] as ProviderSettingsCollection; }
}
}
In order to access the attributes as properties, you need to define ConfigurationPropertyAttribute on the property that maps the attribute of the given name to this property. The ConfigurationPropertyAttribute allows you to set the DefaultValue that will be returned if the desired attribute is not defined in the configuration file. The ProviderSettings property returns all the providers defined in the section.
Note: Decorating the DefaultProvider property with the ConfigurationProperty attribute and setting its default value to PayPalPaymentGatewayProvider is not the same as defining the default provider in the configuration file. By setting this default value, you are making sure that you have a concrete provider to work with. A better approach is to define a DefaultPaymentGatewayProvider class that does nothing but throws a NotImplementedException .
The concrete providers should override the Initialize() method defined in ProviderBase. . This method takes a NameValueCollection as input that contains the properties as defined in the configuration section. This method is called from within the factory class, as discussed in detail in the next section.
Rule 4: Provider Manager
There should be a static sealed class that works as the provider factory and exposes all the functionalities of the base provider. This class should be thread safe. It is usually named as [FeatureName]ProviderManager . The provider model supports an unlimited number of providers for a feature. The provider manager class initializes them and defines the logic of which provider should be used in which situation. It is recommended that all the client code use the provider manager class to access the provider and its base functionalities. Since this class could be called from anywhere, it should be implemented in a thread-safe way.
The logic for selecting a provider is completely in the hands of the application developer using the provider-based component. ASP.NET 2.0's implementation of the provider model only supports a single provider at a time. However, there are three choices in implementing the selection logic when a provider method call is made, namely:
- Select the default provider and call its method.
- Select all the providers and call methods on each of them.
- Select provider(s) based on some criteria and forward the call to them.
Each of the preceding approaches has its merit. For example, if you are implementing a provider to choose among back-end data stores as ASP.NET 2.0 does, then the first approach is appropriate. In scenarios like exception logging, where you want to log the error to a database and would also like to send an e-mail, the second approach is appropriate with a database and an e-mail provider. However, there could be situations where you want to call a specific provider in certain conditions only. The PaymentGatewayProvider is one such example. Suppose the PayPalGatewayProvider is the default provider, but the back-end credit card processor does not support the Discover card processing, and you want to support it in your application. ANet supports it, but they have a different transaction rate for Visa and MasterCard. Therefore, the business logic is that all the transactions except those using Discover card should be done by PayPal, and the Discover card transactions should be done by ANet.
The PaymentGatewayManager class is the provider manager. This is a sealed static class that provides direct access to all the available providers using the Providers property and to the default provider using the DefaultProvider . The PaymentGatewayManager class further exposes the base provider functionalities. The important point to note here is that whenever a method or property is accessed, you need to make sure that all the providers have been initialized. This is done by calling the helper Initialize() method, the implementation of which is shown here:
private static void Initialize()
{
if (_initialized) return;
lock (_syncRoot)
{
_config = ConfigurationManager.GetSection("paymentGateway")
as PaymentGatewaySection;
if (_config == null)
throw new ProviderException("paymentGateway section is missing");
ProvidersHelper.InstantiateProviders(_config.ProviderSettings, _providers,
typeof(PaymentGatewayProvider));
if (_providers == null || _providers.Count == 0)
throw new ArgumentNullException("Providers");
// we don't want anyone to change it
_providers.SetReadOnly();
_initialized = true;
}
}
As you can see, Initialize() is a thread-safe method. The GetSection() method on ConfigurationManager class is called and it returns a PaymentGatewaySection object. Next, I use the System.Web.Configuration.ProviderHelper class to initialize the provider collection. The InitiateProviders() method internally initiates the providers and calls their Initialize() method, passing them all the attributes as a NameValueCollection . Since I do not want anyone to change the internal initial state of the providers, I mark the provider collection object as read-only. Finally, I set the _initialize variable to true , so I don't have to initialize the providers with each call.
The PaymentGatewayManager class exposes the core methods Charge() and Refund() as overloads. The overload Charge(CreditCard card, double amount, string providerName) allows the client to call the Charge() method on the required provider. The Charge(CreditCard card, double amount) method calls the method on default provider. The actual logic to select the provider is implemented in the method as shown here:
public static string Charge(CreditCard card, double amount)
{
return Charge(card, amount, "");
}
public static string Charge(CreditCard card, double amount,
string providerName)
{
Initialize();
PaymentGatewayProvider p = Provider(providerName);
if (p.SupportCardType(card.CardType))
return Provider(providerName).Charge(card, amount);
else
{
foreach (ProviderBase pb in Providers)
{
if (((PaymentGatewayProvider)pb).SupportCardType(card.CardType))
{
return ((PaymentGatewayProvider)pb).Charge(card, amount);
}
}
throw new NotSupportedException(card.CardType);
}
}
private static PaymentGatewayProvider Provider(string providerName)
{
if (string.IsNullOrEmpty(providerName))
providerName = _config.DefaultProvider;
PaymentGatewayProvider p = Providers[providerName]
as PaymentGatewayProvider;
if (p == null)
throw new ArgumentException("providerName");
return p;
}
First, I make sure that the desired provider supports the processing of the given CreditCard . If it doesn't, I loop through the available providers and select the first provider that supports the processing of the given CreditCard type and call methods on it. The same logic occurs when the Refund() method is called.
Rule 5: The Concrete Provider Should Be Thread Safe
The provider can be called by the manager from any thread. Ideally, it should be stateless; if not, then it should be thread safe.
The providers are now ready to be created, namely PayPalPaymentGatewayProvider and ANetPaymentGatewayProvider . These are simple classes and do not implement actual processing logic. (Hey, it's an article on the provider model, not on Payment Gateways) The following listing for PayPalPaymentGatewayProvider is quite straightforward and doesn't require a point-by-point explanation.
public class PayPalPaymentGatewayProvider : PaymentGatewayProvider
{
private static int _txnId =0;
public override string Charge(CreditCard card, double amount)
{
if (!SupportCardType(card.CardType))
throw new Exception("Card is not supported.");
return "PayPalTransaction" + _txnId++;
}
public override bool Refund(string transactionId, CreditCard card,
double amount)
{
return true;
}
public override void Initialize(string name,
System.Collections.Specialized.NameValueCollection config)
{
_name = name;
if (String.IsNullOrEmpty(_name))
_name = "PayPalPaymentGatewayProvider";
_endPoint = config["endPoint"];
_accountEmail = config["accountEmail"];
_supportedTypes = config["supportedCardTypes"];
base.Initialize(_name, config);
}
#region Name
private string _name;
public override string Name
{
get { return _name; }
}
#endregion
#region Description
private string _description ="";
public override string Description
{
get { return _description; }
}
#endregion
#region EndPoint
private string _endPoint;
public string EndPoint
{
get { return _endPoint; }
}
#endregion
#region AccountEmail
private string _accountEmail;
public string AccountEmail
{
get { return _accountEmail; }
}
#endregion
#region SupportedCardTypes
private string _supportedTypes;
public override string[] SupportedCardTypes
{
get { return _supportedTypes.Split(','); }
}
#endregion
Now the fun part: run the web application provided in the download. If you select any credit card type except Discover, PayPalPaymentGatewayProvider is used for processing. If you select Discover, then ANetPaymentGatewayProvider is used as you can see by the transaction ID returned. Now leave the browser open and go to the web.config file and change the default attribute's value to ANetPaymentGatewayProvider . Rerun the application, and you should see that all the card processing is done by ANetPaymentGatewayProvider .
What ASP.NET 2.0 Features Are Based on the Provider Model
The provider model is now built into the core of ASP.NET 2.0. The ASP.NET team has big plans on using this model for providing new and exciting features in future releases. At this time, the following features are built with the provider model.
- Membership
- User Roles
- Profile
- Personalization
- Site Map
- Session State
Not all of these features use the same rules to implement the provider model (the Session State provider does not have the manager class, for example). The discussion of the implementations of these providers deserves their own articles, and I have included the references to some good ones in the Related Links for this article.
Provider Model Applications
The Web Control is the revolutionizing feature of ASP.NET that allows the reuse of UI code. However, if you look around, you will notice that it is not only the UI code that could be reused. There are a number of common features among websites (there is always a FAQ section on a website or Contact section, most community sites have ranking solutions for items and comments, etc.). These features are the same among web applications, but their implementations are different. If you want to use one of these implementations from one application to another, you have to take all the pieces (back-end database, business logic layer). This is commonly referred to as the "take it or leave it" mentality. In the real world, we need solutions that we can pull out of the box, customize, and reuse.
This is where the provider model comes into the picture. You can develop all the core functionalities using providers and enhance them or change them as needed. You can even let third parties build onto the core functionalities. You can develop WebControls based on the provider model, but they lack the management piece of the solution. On the other hand, Web Parts (another new feature of ASP.NET 2.0) allow you to use custom verbs and role-based rendering, which could be used for management features. The provider model and the web part together can be a very powerful combination. Using these two, you can provide completely reusable solution. I can see a big market for Web Parts based on the provider model.
Provider Model Limitations
If you have read the article from the beginning, you are probably convinced at this point that the provider model is a viable solution. Be warned that the provider model is not a silver bullet. The provider model is a work in progress, and it has a number of limitations:
- There are no recommendations for using multiple providers.
- There is no straightforward answer to increase the core functionality of a provider (should you add a virtual or abstract method in the base provider).
- Ideally, providers should be stateless, but what about stateful providers?
Here are a few examples of where the provider model should not be used:
- If you are interested only in changing the back-end database, the provider model is not for you. ADO.NET 2.0 has much better support for this. See the "Related Links" section for more information.
- If your goals are simple, do not add complexity to the solution by blindly implementing the provider model.
Provider Model Changes From Beta 1 to Beta 2
As I discussed previously, the provider model could be used for Web and Windows applications alike. In Beta 1, this support was limited and complex. In Beta 2, there is now a configuration assembly named System.Configuration.dll that provides a more standardized way to manage configuration information.
Previously, you had a GetSection() method defined in the ConfigurationSettings class. This method has moved out to a more generic class named ConfigurationManager . The ConfigurationManager class is the preferred class to work with all the configuration files; all other methods have been depreciated. Here is a quick summary of the supported methods:
| Task | Old Beta 1 Technique | New Beta 2 Technique |
|---|---|---|
| Getting the AppSettings section from the configuration file. | ConfigurationSettings.AppSettings["Key"] | ConfigurationManager.AppSettings["MyKey"] |
| Getting the ConnectionStrings section from the configuration file. | ConfigurationSettings.ConnectionSteings["myConKey"].ConnectionString | ConfigurationManager.ConnectionStrings["myConKey"].ConnectionString |
| Getting a specific configuration section from the current default configuration. | ConfigurationSettings.GetConfig("mySection") | ConfigurationManager.GetSection("mySection") |
The Provider Model in ASP.NET 1.x
You can implement the provider model using today's .NET 1.x; actually, one such implementation is available for download from MSDN (see the "Related Links" section for details). This implementation provides the basic classes to implement the provider model. However, there are couples of things to watch out for:
- The download base classes are incomplete, and you have to do lot of work to implement the model completely.
- The classes do not follow the ASP.NET 2.0 standards. You will have to do a lot of work when you are ready to move to ASP.NET 2.0.
Keeping these limitations in view, I have provided set of base classes that follow ASP.NET 2.0 class naming and structures. They are implemented in the cpSphere.Configuration and cpSphere.Configuration.Provider namespaces. When you are ready to move your code to .NET 2.0, you will only have to change the using statement! Using these classes, you can create your providers as I have done in this article. You can reference the cpSphere.Configuration.Provider assembly in your project, import the cpSphere.Configuration and cpSphere.Configuration,Provider namespaces, and you are on your way to building providers the ASP.NET 2.0 way.
These classes implement only the required functionalities of the ASP.NET 2.0 classes, which they are mimicking. I have rewritten the PaymentGateway application in .NET 1.x using this library, which is also included in the download. If you compare the .NET 1.x provider implementations with the ASP.NET 2.0 implementation, you will not find any differences. The .NET 1.x implementation is a console application that demonstrates the provider model can be used both for Windows desktop applications and web applications. The output from the console application is shown in FigureFigure 3. As you can see from the transaction ID, PayPalPaymentGatewayProvider is used to process MasterCard and Visa cards, and ANetPaymentGatewayProvider is used when a Discover card needs to be charged.

Figure 3. NET 1.x implementation of the Provider Model
Conclusion
The provider model is the next evolutionary step toward the reuse of features/components. The beauty of the provider model lies in its simplicity and delegation of its implementation. Server Controls created a new market segment for reusable UI widgets. The provider model builds on top of them and allows developers to package common functionalities in reusable components that can be modified by third parties as needed. The provider model is easy to implement and allows us to easily divide the core and enhanced functionalities. ASP.NET 2.0 uses this model to make many of the core functionalities available to us, therefore we can safely bet that it will live for a long time. I recommend you use this development model today and reap the benefits in years to come.

May 15, 09:09 am
Where is the sample download link for this article?
It does not appear above anywhere. If so it is well hidden.
I’d like to check it out.
Thanks in advance
Ron